1

I'm trying to wrap a DLL written in c++ using cython. In the end I will not have access to the source code, so the c++ source itself cannot be used in the process of compiling the wrapper, jus the .dll, the .lib and the .h. All is compiling well. However, I immediately found that strings are not behaving well. For instance, dll functions that return a simple string are not working correctly: the cython code always gets an empty string. I'm on windows 7. I've verified the following.

  • The DLL has been build by Visual Studio 2015 (i.e. v 1900) with the 2015 toolset in debug mode. I tried both compiling with /MTd and /MDd flags. The resulting .lib and .dll file are put in the same folder as all other files below.
  • My python 3.5 distribution has been built by VS 2015 as well, for 32 bit.

    $ python -iu
    Python 3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:38:48) [MSC v.1900 32 bit (Intel)] on win32
    
  • When I compile with a dummy c++ source that contains such a function that returns a string, it just works. Using the dll it doesn't work.

setup.py:

from distutils.core import setup
from Cython.Build import cythonize

setup(ext_modules=cythonize(
    "test.pyx",                 # our Cython source
))

test.pyx:

# distutils: language = c++
# distutils: libraries = MyDLL
# distutils: include_dirs = .
# distutils: library_dirs = .
from libcpp.string cimport string

cdef extern from "MyDLL.h" namespace "somenamespace":

    int ReturnSomeInt()
    string ReturnSomeString()

def run():
    cdef string s = string(b'abcdefg')
    print(s)
    cdef int i = ReturnSomeInt()
    print(i)
    cdef string problem = ReturnSomeString()
    print(problem)

MyDLL.h:

__declspec(dllexport) int ReturnSomeInt();
__declspec(dllexport) std::string ReturnSomeString();

Snippet of C++ code that was used to compile MyDLL:

__declspec(dllexport) int ReturnSomeInt() { return 42; }
__declspec(dllexport) std::string ReturnSomeString() { cout << "debug..." << endl; return "Hello world!"; }

main.py:

from test import run
run()

I compile using the command

$ python setup.py build_ext --inplace --force && python -u main.py

Running this prints

b'abcdefg'
42
debug... # printed by the dll function ReturnSomeString()
b'' # Should've printed: b'Hello World!'

We can verify that the ReturnSomeString() from MyDLL is actually called, as it sends some text to stdout.

What didn't I check?

chtenb
  • 14,924
  • 14
  • 78
  • 116
  • 2
    My guess is that there is something DLL related happening, for example http://stackoverflow.com/questions/22797418/how-do-i-safely-pass-objects-especially-stl-objects-to-and-from-a-dll and http://stackoverflow.com/questions/3564985/returning-stdstring-stdlist-from-dll – J.J. Hakala Jul 15 '16 at 10:37
  • 1
    Also [answer](http://stackoverflow.com/a/22797588/5781248) that hints that the compiler version and the settings have to be the same. – J.J. Hakala Jul 15 '16 at 10:41
  • Those links are useful! – chtenb Jul 15 '16 at 11:26

1 Answers1

0

Thanks to this answer pointed to by @hakala I discovered that Debug/Release mode matters. It didn't enter my mind that it could. I quote:

The C++ standard library has its own set of ABI issues. There is no guarantee that a given STL type is laid out the same way in memory, nor is there a guarantee that a given STL class has the same size from one implementation to another (in particular, debug builds may put extra debug information into a given STL type). Therefore, any STL container will have to be unpacked into fundamental types before being passed across the DLL boundary and repacked on the other side.

Indeed, compiling the DLL in Release mode made the example in the OP work.

Community
  • 1
  • 1
chtenb
  • 14,924
  • 14
  • 78
  • 116