Using cTYPES to invoke C + + dynamic libraries in Python

Source: Internet
Author: User
Tags valgrind

Using cTYPES to invoke C + + dynamic library Getting started in Python

Using the cTYPES library, you can directly invoke a dynamic library written in C, and if you are invoking a dynamic library written in C + +, you need to declare the functions of the extern dynamic library using keywords:

#include <iostream>using namespace std;extern "C" {    void greet() {        cout << "hello python" << endl;    }}

Compile the above C + + program into a dynamic-link library:

g++ hello.cpp -fPIC -shared -o hello.so

Using the ctypes import dynamic Library in Python code, call the function:

# -*- coding: utf-8 -*- #from ctypes import CDLLhello = CDLL('./hello.so')if __name__ == '__main__':    hello.greet()

Run the above Python program:

[email protected]:~/codespace/python$ python3 hello.pyhello python
Parameter passing writes an integer addition function
#include <iostream>using namespace std;extern "C" {    int add(int a, int b) {        return a + b;    }}

Compile the dynamic library and call it in Python code:

# -*- coding: utf-8 -*- #from ctypes import CDLLhello = CDLL('./hello.so')if __name__ == '__main__':    a = input('input num1: ')    b = input('input num2: ')    print('output: %d' % hello.add(int(a), int(b)))

Run the above code to get the output:

[email protected]:~/codespace/python$ python3 hello.pyinput num1: 12input num2: 34output: 46
Attempt to pass string arguments
#include <iostream>#include <cstdio>using namespace std;extern "C" {    void print_name(const char* name) {        printf("%s\n", name);    }}

Python code calls:

# -*- coding: utf-8 -*- #from ctypes import CDLLhello = CDLL('./hello.so')if __name__ == '__main__':    name = input('input name: ')    hello.print_name(name.encode('utf-8'))  # 此处需要将Python中的字符串按照utf8编码成bytes

To view the output:

[email protected]:~/codespace/python$ python3 hello.pyinput name: yanhewuyanhewu
Object oriented

To write a dynamic-link library in C + + We will definitely think of how to invoke the C + + class in Python, because ctypes only C-language functions (functions declared in C + +) can be called extern "C" , we need to do some processing on the interface:

C + + code example

#include <iostream>#include <cstdio>#include <string>using namespace std;class Student {private:    string name;    public:    Student(const char* n);    void PrintName();};Student::Student(const char* n) {    this->name.assign(n);}void Student::PrintName() {    cout << "My name is " << this->name << endl;}extern "C" {    Student* new_student(const char* name) {        return new Student(name);    }    void print_student_name(Student* stu) {        stu->PrintName();    }}

Called in Python code:

# -*- coding: utf-8 -*- #from ctypes import CDLLhello = CDLL('./hello.so')class Student(object):    def __init__(self, name):        self.stu = hello.new_student(name.encode('utf-8'))        def print_name(self):        hello.print_student_name(self.stu)if __name__ == '__main__':    name = input('input student name: ')    s = Student(name)    s.print_name()

Output:

[email protected]:~/codespace/python$ python3 hello.pyinput student name: yanhewuMy name is yanhewu
Memory leaks?

The last part of us we tried to use to ctypes invoke C + + dynamic libraries with classes, here we can not help but think of a problem, we in the dynamic library using new dynamic request memory will be the Python GC cleanup? Here we can fully assume that dynamic memory in C + + dynamic library is not applied using the memory request mechanism in Python, Python should not do this part of the memory GC, if it is true, C + + dynamic Library will have a memory leak problem. That's the truth, isn't it? We can use the Memory Check tool Valgrind to check the python code above:

Command:

valgrind python3 hello.py

Final result output:

==17940== HEAP SUMMARY:==17940==     in use at exit: 647,194 bytes in 631 blocks==17940==   total heap usage: 8,914 allocs, 8,283 frees, 5,319,963 bytes allocated==17940== ==17940== LEAK SUMMARY:==17940==    definitely lost: 32 bytes in 1 blocks==17940==    indirectly lost: 0 bytes in 0 blocks==17940==      possibly lost: 4,008 bytes in 7 blocks==17940==    still reachable: 643,154 bytes in 623 blocks==17940==         suppressed: 0 bytes in 0 blocks==17940== Rerun with --leak-check=full to see details of leaked memory==17940== ==17940== For counts of detected and suppressed errors, rerun with: -v==17940== Use --track-origins=yes to see where uninitialised values come from==17940== ERROR SUMMARY: 795 errors from 86 contexts (suppressed: 0 from 0)

As you can see, definitely lost 32 bytes, there is a memory leak, but it is not the problem of dynamic library we have to further verify:

C + + code adds a destructor definition:

#include <iostream>#include <cstdio>#include <string>using namespace std;class Student {private:    string name;    public:    Student(const char* n);    ~Student();    void PrintName();};Student::Student(const char* n) {    this->name.assign(n);}Student::~Student() {    cout << "Student's destructor called" << endl;}void Student::PrintName() {    cout << "My name is " << this->name << endl;}extern "C" {    Student* new_student(const char* name) {        return new Student(name);    }    void print_student_name(Student* stu) {        stu->PrintName();    }}

Run the same Python code:

[email protected]:~/codespace/python$ python3 hello.pyinput student name: yanhewuMy name is yanhewu

As you can see from Student the output, the destructor is not called. It is determined here that the Python GC does not process the requested memory in the dynamic library and does not handle it (after all, it is not the memory requested in the Python environment, it is not possible to determine whether it is the memory on the heap or the memory on the stack). However, the problem of memory leaks still needs to be solved, you can refer to the following practices:

Add a memory release interface to your C + + code:

#include <iostream>#include <cstdio>#include <string>using namespace std;class Student {private:    string name;    public:    Student(const char* n);    ~Student();    void PrintName();};Student::Student(const char* n) {    this->name.assign(n);}Student::~Student() {    cout << "Student's destructor called" << endl;}void Student::PrintName() {    cout << "My name is " << this->name << endl;}extern "C" {    Student* new_student(const char* name) {        return new Student(name);    }    // 释放对象内存函数    void del_student(Student* stu) {        delete stu;    }    void print_student_name(Student* stu) {        stu->PrintName();    }}

In Python code, the Student memory deallocation function is called in the class:

# -*- coding: utf-8 -*- #from ctypes import CDLLhello = CDLL('./hello.so')class Student(object):    def __init__(self, name):        self.stu = hello.new_student(name.encode('utf-8'))    def __del__(self):        # Python的对象在被GC时调用__del__函数        hello.del_student(self.stu)        def print_name(self):        hello.print_student_name(self.stu)if __name__ == '__main__':    name = input('input student name: ')    s = Student(name)    s.print_name()

Run Python code:

[email protected]:~/codespace/python$ python3 hello.pyinput student name: yanhewuMy name is yanhewuStudent's destructor called

As you can see, destructors for classes in C + + dynamic libraries Student are called. Use Valgrind again to check memory usage:

==23780== HEAP SUMMARY:==23780==     in use at exit: 647,162 bytes in 630 blocks==23780==   total heap usage: 8,910 allocs, 8,280 frees, 5,317,023 bytes allocated==23780== ==23780== LEAK SUMMARY:==23780==    definitely lost: 0 bytes in 0 blocks==23780==    indirectly lost: 0 bytes in 0 blocks==23780==      possibly lost: 4,008 bytes in 7 blocks==23780==    still reachable: 643,154 bytes in 623 blocks==23780==         suppressed: 0 bytes in 0 blocks==23780== Rerun with --leak-check=full to see details of leaked memory==23780== ==23780== For counts of detected and suppressed errors, rerun with: -v==23780== Use --track-origins=yes to see where uninitialised values come from==23780== ERROR SUMMARY: 793 errors from 87 contexts (suppressed: 0 from 0)

As you can see, it definitely lost has changed to 0 to determine that the memory requested by the dynamic library was successfully freed.

Using cTYPES to invoke C + + dynamic libraries in Python

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.