Reputation: 292
I would like to send extended Struct/Pointer(In the example below PY_LayerExtended) from Python to C++ but I've got an error.
My C++ code supposed to be like this:
typedef struct PY_LayerBase {
} PY_LayerBase;
typedef struct PY_LayerExtended : PY_LayerBase {
} PY_LayerExtended;
typedef struct PY_Layer {
PY_LayerBase* layerBase;
} PY_Layer;
// My Method
void parseTest(PY_Layer *py_layer) {
// I cast PY_LayerBase to PY_LayerExtended
PY_LayerExtended* py_layerExt = static_cast<PY_LayerExtended*>(py_layer->layerBase);
}
And my Python code looks like this:
import ctypes
from ctypes import *
class PY_LayerBase (ctypes.Structure):
_fields_ = []
class PY_LayerExtended (PY_LayerBase):
_fields_ = []
class PY_Layer(ctypes.Structure):
_fields_ = [("layerBase", PY_LayerBase)]
my_lib = cdll.LoadLibrary('mylib.dll')
py_layer_ext = PY_LayerExtended()
py_layer = PY_Layer(pointer(py_layer_ext))
parseTest = my_lib.parseTest
parseTest.argtypes = (POINTER(PY_Layer))
# Run C++
my_ptr = parseTest(pointer(py_layer))
And when I run Python I've got an error:
TypeError: incompatible types, PY_LayerExtended instance instead of PY_LayerBase instance
The error happens on the line: py_layer = PY_Layer(pointer(py_layer_ext))
Upvotes: 0
Views: 787
Reputation: 177971
ctypes
doesn't understand C++ inheritance, even though you can declare the ctypes Structures similarly using Python inheritance.
You can solve the issue one of two ways. Below I've adjusted the C++ code to give some feedback that the structures are accessed properly:
test.cpp:
#ifdef _WIN32
# define API __declspec(dllexport)
#else
# define API
#endif
typedef struct PY_LayerBase {
int a; // add element
} PY_LayerBase;
typedef struct PY_LayerExtended : PY_LayerBase {
int b; // also here, if cast works properly we'll return this value
} PY_LayerExtended;
typedef struct PY_Layer {
PY_LayerBase* layerBase;
} PY_Layer;
extern "C" API
int parseTest(PY_Layer *py_layer) {
PY_LayerExtended* py_layerExt = static_cast<PY_LayerExtended*>(py_layer->layerBase);
return py_layerExt->b;
}
Option 1: Cast the pointer to the type needed:
from ctypes import *
class PY_LayerBase (Structure):
_fields_ = ('a',c_int),
def __init__(self,a):
self.a = a
class PY_LayerExtended(PY_LayerBase): # using Python inheritance to mimic C++ inheritance
_fields_ = ('b',c_int),
def __init__(self,a,b):
super().__init__(a)
self.b = b
class PY_Layer(Structure):
_fields_ = ("layerBase", POINTER(PY_LayerBase)), # Was missing POINTER
my_lib = cdll.LoadLibrary('./test')
py_layer_ext = PY_LayerExtended(5,7)
py_layer = PY_Layer(cast(pointer(py_layer_ext),POINTER(PY_LayerBase))) # cast to required type
parseTest = my_lib.parseTest
parseTest.argtypes = POINTER(PY_Layer),
parseTest.restype = c_int
# Run C++
print(parseTest(pointer(py_layer)))
Option 2: Declare the structures in Python like you would in pure C and pass a pointer to the base strucure. This will have the same layout as a base class using only C data types.
from ctypes import *
class PY_LayerBase (Structure):
_fields_ = ('a',c_int),
def __init__(self,a):
self.a = a
class PY_LayerExtended(Structure): # don't use inheritance,
_fields_ = (('base',PY_LayerBase), # make base structure a member
('b',c_int))
def __init__(self,a,b):
self.base.a = a
self.b = b
class PY_Layer(Structure):
_fields_ = ("layerBase", POINTER(PY_LayerBase)),
my_lib = cdll.LoadLibrary('./test')
py_layer_ext = PY_LayerExtended(5,7)
py_layer = PY_Layer(pointer(py_layer_ext.base)) # pass pointer to base
parseTest = my_lib.parseTest
parseTest.argtypes = POINTER(PY_Layer), # added comma to make this a sequence as required
parseTest.restype = c_int
# Run C++
print(parseTest(pointer(py_layer)))
Output (both options):
7
Upvotes: 3