0

I'm trying to access a C++ class from python using ctypes. Wrapper functions to access the C++ functionality are exposed in a DLL.

I have a C++ data class. An instance of the class is made in the first function call from python to C++, and a pointer to this instance is passed back. The second python function call takes the pointer as an arg and prints some of its data.

The problem is that the pointer address is truncated. When printing the pointer just after the class instance is made, it looks like "000001A05127F110". Then when the pointer is printed again within C++ in the second function call, it looks like "000000005127F110".

I'm using python 3.6.8 x64, MSVC x64.

I've tried setting the argtypes and the restypes to various ctypes builtin types, but it has not fixed the problem.

Interestingly, this is the second time that I have tried to do this. In the past the same solution that I am using here worked fine under the same (or similar) circumstances.

data_wrapper.h

#include <string>

class DataWrapper
{
public:

    DataWrapper()
    {
        m_temp = std::string("hi");
    }

    virtual ~DataWrapper()
    {
        std::cout << "Deleting" << std::endl;
    }

    std::string m_temp;

};

wrapper.cpp

#include <iostream>
#include <string>

#include "cogitant/data_wrapper.h"

#define DLLEXPORT extern "C" __declspec(dllexport)

DLLEXPORT void checkData(DataWrapper * d)
{
    std::cout << d << std::endl;
    //std::cout << d->m_temp << std::endl;
}

DLLEXPORT DataWrapper * makeData()
{
    DataWrapper * d = new DataWrapper();
    std::cout << d << std::endl;
    return d;
}

main.py

import ctypes

CPP_LIB_DLL_LOC = "<my path>/data_wrapper.dll"
LIB = ctypes.cdll.LoadLibrary(CPP_LIB_DLL_LOC)

def main():
    print("Starting. . .")

    #make new data class
    make_func = LIB.makeData
    make_func.argtypes = []
    make_func.restypes = ctypes.c_void_p
    dataclass = make_func()
    print(dataclass)
    print(type(dataclass ))

    #check the dataclass pointer is valid
    check_func = LIB.checkData
    check_func.restypes = None
    check_func.argtypes = [ctypes.c_void_p]
    check_func(dataclass)

    print("Finished.")

if __name__ == "__main__":
    main()

Here is the python output:

Starting. . .
000001ED0E612320
241247008
<class 'int'>
000000000E612320
Finished.

I'd expect these two addresses to be the same. If I uncomment the line in wrapper.cpp then I see the following error:

  File "<my path>/python/main.py", line 21, in main
    check_func(dataclass)
OSError: exception: access violation reading 0x000000000E612320
Kevin Kraft
  • 150
  • 1
  • 11
  • As an aside, I seem to recall that `ctypes` doesn't work well with C++ libraries as it doesn't know what type of name mangling was used by the compiler that created the library. You could fudge it by using the same version of MSVC that your version of Python was compiled with. That means your code will not work on a different OS, or even on Windows with a different version of Python. – Dunes Jan 28 '19 at 15:48
  • Possible duplicate of [Python ctypes cdll.LoadLibrary, instantiate an object, execute its method, private variable address truncated](https://stackoverflow.com/questions/52268294/python-ctypes-cdll-loadlibrary-instantiate-an-object-execute-its-method-priva) – CristiFati Jan 28 '19 at 15:53
  • @dunes Isn't that why `extern "C"` is used? – Kevin Kraft Jan 29 '19 at 09:14
  • It means the function will use C linkage, yes. But `extern "C"` doesn't effect how a compiler lays out a class, so you are left with an opaque pointer. This means you have to write a C library of helper functions to use and access the C++ class' methods, members and related functions. You also have to write a helper module in Python to enable use of the C++ class. You also need to take into consideration how exceptions at the C++ level will be communicated to the Python level. At this point the amount of effort involved means approaches like SWIG or Cython are justified and less error prone. – Dunes Jan 29 '19 at 10:36

1 Answers1

-1

It is restype not restypes.

This solves the problem I was having.

Can confirm that:

LIB.makeData.fuck_ctypes

does indeed cause an error.

Kevin Kraft
  • 150
  • 1
  • 11