Can Python call C++ DLL libraries compiled using extern “C” with ctypes?

ⅰ亾dé卋堺 提交于 2021-01-28 07:03:38

问题


There are many articles on the internet that say that Python cannot access C++ compiled DLLs using ctypes, and there are other contradictory articles which say that it is possible to access the DLL so long as the DLL is compiled with the extern "C" method. So I just want clarification. Is it possible if extern "C" is used ?


回答1:


There are C++-compiled dlls which can be used with ctypes and C++-compiled dlls which cannot be used with ctypes directly.

If API exposes only plain-old-data (e.g. int, int *, char * and similar) and is wrapped in extern "C" then it can be consumed from ctypes.

If non-POD-classes are used (for example std::vector or std::string) in interfaces or the API isn't wrapped in extern "C" then the dll cannot be used with ctypes (at least in a portable way and without much ado).


Why extern "C" is needed?

Following declarations are valid in C++ but not in C:

void my_fun(int a);
void my_fun(double a);

Because C doesn't perform name mangling, both functions would be mapped to a symbol named my_fun in the resulting object file, which is problematic.

C++ would create two different symbols with mangled names, for example gcc would create symbols Z6my_funi and _Z6my_fund. MSVC has another naming scheme - so the resulting symbols in the dll (or shared object) depends on the compiler with which it was built.

It is not impossible to find these symbols in the dll with help of ctypes - it is only not as straight forward as with C-names because one needs additional information - with which compiler the dll was build.

C-names work out of the box with ctypes, and wrapping declarations into extern "C" switches the name mangling off, thus

extern "C" {
  void my_fun(int a);
  void my_fun(double a);
}

would no longer compile, as compiler will ensure that there are no multiple definitions of a symbol.

Why more complicated C++-classes cannot be used in interfaces?

extern "C" doesn't forbid usage of std::vector<> and other C++-classes:

extern "C" {
  void my_fun(std::vector<double> a);
}

compiles, it only produces symbol my_func instead of ?my_fun@@YANV?$vector@NV?$allocator@N@std@@@std@@@Z (with MSVC) or _Z6my_funSt6vectorIdSaIdEE (with gcc) in the resulting object file.

While for PODs, let's say double[10], the memory layout is clear, the memory layout of e.g. a std::vector is implementation dependent and is not known to ctypes. Another issue are - something ctypes doesn't handle as well - constructors/destructors which can be more complicated than simple initialization of PODs.

Thus the functionality of my_fun(std::vector<double>) cannot be used out-of-the-box with ctypes. However, if one knows the memory layout of the classes, one could emulate them with ctypes, initialize objects by calling (right) constructors (btw. symbols of class-methods are always mangled, even if wrapped in extern "C"), call the function my_fun and then destroy objects by calling (right) destructors.



来源:https://stackoverflow.com/questions/63688553/can-python-call-c-dll-libraries-compiled-using-extern-c-with-ctypes

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!