问题
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