Differences

This shows you the differences between two versions of the page.

Link to this comparison view

tutorials:python:python_call_c_by_ctypes [2019/05/20 14:16] (current)
alanzheng created
Line 1: Line 1:
 +===== Python call C/C++ by ctypes =====
  
 +The way I like to call C/C++ from Python is by ctypes. ​
 +The reason is that ctypes is part of the standard library, therefore is more stable and widely available than swig. 
 +With ctypes, you need to satisfy any compile time dependency on python, and your binding will work on any python that has ctypes, ​
 +not just the one it was compiled against.
 +
 +There is an example on windows, and suppose compiled it to test.dll
 +
 +==== C/C++ code ====
 +
 +<​code>​
 +#define DLL_EXPORT __declspec(dllexport)
 +
 +#include <​iostream>​
 +#include <​string>​
 +using namespace std;
 +
 +typedef struct StructTest
 +{
 +    char* name;
 +    int age;
 +    int score[3];
 +}StructTest,​ *StructPtr;
 +
 +class TestLib
 +{
 +    public:
 +        int passInt(int a);
 +        double passDouble(double d);
 +        char passChar(char c);
 +        char* passString(char* s);
 +        StructTest passStruct(StructTest st);
 +        StructPtr passStructPtr(StructPtr p);
 +        StructPtr passStructArray(StructTest vst[], int size);
 +};
 +
 +int TestLib::​passInt(int a) {
 +    cout << a << " in c++" << endl;
 +    return a;
 +}
 +
 +double TestLib::​passDouble(double d) {
 +    cout << d << " in c++" << endl;
 +    return d;
 +}
 +
 +char TestLib::​passChar(char c) {
 +    cout << c << " in c++" << endl;
 +    return c;
 +}
 +
 +char* TestLib::​passString(char* s) {
 +    cout << s << " in c++" << endl;
 +    return s;
 +}
 +
 +StructTest TestLib::​passStruct(StructTest st) {
 +    cout << st.name << " " << st.age << " " << st.score[2] << " in c++" << endl;
 +    return st;
 +}
 +
 +StructPtr TestLib::​passStructPtr(StructPtr p) {
 +    cout << p->name << " " << p->age << " " << p->​score[2] << " in c++" << endl;
 +    return p;
 +}
 +
 +StructPtr TestLib::​passStructArray(StructTest vst[], int size) {
 +    cout << vst[0].name << " in c++" << endl;
 +    cout << vst[1].name << " in c++" << endl;
 +    return &​vst[0];​
 +}
 +
 +extern "​C"​ {
 +    static TestLib obj;
 +
 +    DLL_EXPORT int passInt(int a) {
 +        return obj.passInt(a);​
 +    }
 +
 +    DLL_EXPORT double passDouble(double d){
 +        return obj.passDouble(d);​
 +    }
 +
 +    DLL_EXPORT char passChar(char c) {
 +        return obj.passChar(c);​
 +    }
 +
 +    DLL_EXPORT char* passString(char* s){
 +        return obj.passString(s);​
 +    }
 +
 +    DLL_EXPORT StructTest passStruct(StructTest st){
 +        return obj.passStruct(st);​
 +    }
 +
 +    DLL_EXPORT StructPtr passStructPtr(StructPtr p){
 +        return obj.passStructPtr(p);​
 +    }
 +
 +    DLL_EXPORT StructPtr passStructArray(StructTest vst[], int size){
 +        return obj.passStructArray(vst,​ size);
 +    }
 +}
 +</​code>​
 +
 +==== Python code ====
 +
 +<​code>​
 +import ctypes
 +from ctypes import cdll
 +lib = cdll.LoadLibrary('​test.dll'​)
 +
 +lib.test()
 +
 +# passInt
 +print('​passInt'​)
 +print(lib.passInt(100))
 +print('​--------------------'​)
 +
 +# passDouble
 +print('​passDouble'​)
 +lib.passDouble.restype = ctypes.c_double
 +print(str(lib.passDouble(ctypes.c_double(1.23))) + ' in python'​)
 +print('​--------------------'​)
 +
 +# passChar
 +print('​passChar'​)
 +lib.passChar.restype = ctypes.c_char
 +print(str(lib.passChar(ctypes.c_char(65))) + ' in python'​) # '​A'​
 +print(str(lib.passChar(ctypes.c_char(b'​A'​))) + ' in python'​) # '​A'​
 +print('​--------------------'​)
 +
 +# passString
 +print('​passString'​)
 +lib.passString.restype = ctypes.c_char_p
 +print(str(lib.passString(ctypes.c_char_p(b'​abcde'​))) + ' in python'​) # need add b
 +print('​--------------------'​)
 +
 +# passStruct
 +print('​passStruct'​)
 +class Struct(ctypes.Structure):​
 +    _fields_ = [('​name',​ ctypes.c_char_p),​
 +                ('​age',​ ctypes.c_int),​
 +                ('​score',​ ctypes.c_int * 3)]
 +lib.passStruct.restype = Struct
 +array = [1, 2, 3]
 +st = lib.passStruct(Struct(b'​xidui',​ 10, (ctypes.c_int * 3)(*array)))
 +# p = lib.passStruct(Struct(b'​xidui',​ 10, (ctypes.c_int * 3)(1, 2, 3)))
 +print(str(st.name) + ' ' + str(st.age) + ' ' + str(st.score[2]) + ' in python'​)
 +print('​--------------------'​)
 +
 +# passStructPointer
 +print('​passStructPointer'​)
 +lib.passStructPtr.restype = ctypes.POINTER(Struct)
 +lib.passStructPtr.argtypes = [ctypes.POINTER(Struct)] # need it
 +p = lib.passStructPtr(Struct(b'​xidui',​ 10, (ctypes.c_int * 3)(*array)))
 +print(str(p.contents.name) + ' ' + str(p.contents.age) + ' ' + str(p.contents.score[2]) + ' in python'​)
 +print('​--------------------'​)
 +
 +# passStructArray
 +print('​passStructArray'​)
 +lib.passStructArray.restype = ctypes.POINTER(Struct)
 +lib.passStructArray.argtypes = [ctypes.ARRAY(Struct,​ 2), ctypes.c_int]
 +array = [Struct(b'​xidui1',​ 10, (ctypes.c_int * 3)(1, 2, 3)),
 +    Struct(b'​xidui2',​ 10, (ctypes.c_int * 3)(1, 2, 3))]
 +p = lib.passStructArray(ctypes.ARRAY(Struct,​ 2)(*array), 2)
 +print(str(p.contents.name) + ' ' + str(p.contents.age) + ' ' + str(p.contents.score[2]) + ' in python'​)
 +print('​--------------------'​)
 +</​code>​