apepkuss
apepkuss

Reputation: 23

Segmentation fault 11 when pass a Python tuple to C++ function with ctypes

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.

Upvotes: 0

Views: 285

Answers (1)

Mark Tolonen
Mark Tolonen

Reputation: 177891

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.

Upvotes: 1

Related Questions