0

I am trying to invoke some C++ functions in Python code with Python ctypes. I have two code files: one is core_lib.cpp, which defines a class and a member method; the other is demo.py, which instantiates the class and calls the member method.

core_lib.cpp is defined as below.

#include <iostream>
#include <tuple>

using namespace std;

class A {
    private:
        string _name;
        tuple<int, int> _size; 

    public:
        A() {
            this->_name = "";
            this->_size = make_tuple(1, 1);
            cout << "Init values of _size: " << get<0>(this->_size) << ", " << get<1>(this->_size) << endl;
        }

        void set_size(int* size) {
            int a = *size;
            int b = *(size+1);

            this->_size = make_tuple(a, b);
            cout << "New values of _size: " << get<0>(this->_size) << ", " << get<1>(this->_size) << endl;
        }
};

// bridge C++ to C
extern "C" {
    A* create_A() {
        return new A();
    }

    void set_size(A* a, int* size) {
        a->set_size(size);
    }
}

demo.py is defined as below.

from ctypes import *
import os

# load C++ lib
core_lib = cdll.LoadLibrary(os.path.abspath('test/stackoverflow/core_lib.so'))

class A(object):
    def __init__(self):
        self.a = core_lib.create_A()

    def set_size(self, size):
        core_lib.set_size(self.a, size)

if __name__ == "__main__":
    asize = (3, 3)
    size = (c_int * 2)(*asize)
    a = A()
    a.set_size(size)

To reproduce the issue, I list my steps here:

  1. Compile core_lib.cpp: g++ core_lib.cpp -fPIC -shared -std=c++11 -o core_lib.so
  2. Run the python script: python demo.py

The Python version is 2.7.15, and runs on MacOS Mojave.

According to my investigation, the issue is caused by the code line in core_lib.cpp:

this->_size = make_tuple(a, b)

I tried to google the issue, but I didn't find an answer. I would appreciate any comment that would help understand the issue and how to fix it.

1 Answer 1

1

Define .argtypes and .restype for your functions. The default type is c_int (32-bit) and you are probably using an OS where pointers are 64-bit, truncating the A* return value.

core_lib.create_A.argtypes = None
core_lib.create_A.restype = c_void_p
core_lib.set_size.argtypes = [c_void_p,POINTER(c_int)]
core_lib.set_size.restype = None

c_void_p is sufficient for an opaque pointer. You can get better type checking by declaring a pointer to a class, and declaring the size pointer to be an array of two ints:

class A(Structure):
    pass

core_lib.create_A.argtypes = None
core_lib.create_A.restype = POINTER(A)
core_lib.set_size.argtypes = POINTER(A),POINTER(c_int * 2)
core_lib.set_size.restype = None

If you have a choice, I recommend defining set_size as void set_size(A* a, int x, int y) so you can call as a.set_size(3,3) directly instead of creating a (c_int * 2)(3,3) to pass.

Sign up to request clarification or add additional context in comments.

1 Comment

Your answer is really helpful. I compared the pointers on both Python and C++ sides. As you said, the pointer in Python (a.a) is 64 bit; after it is passed through core_lib.set_size, the pointer is converted into 32 bit. Really thank you for your answer and sample code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.