Ctypes:扩展Struct/Pointer(继承)

Ctypes: Extended Struct/Pointer (Inheritance)

我想将 Struct/Pointer(在下面的示例中 PY_LayerExtended)从 Python 发送到 C++,但我有一个错误。

我的 C++ 代码应该是这样的:

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);

}

我的 Python 代码如下所示:

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))

当我 运行 Python 我得到一个错误:

TypeError: 类型不兼容,PY_LayerExtended 实例而不是 PY_LayerBase 实例

错误发生在行:py_layer = PY_Layer(pointer(py_layer_ext))

ctypes 不理解 C++ 继承,即使您可以使用 Python 继承类似地声明 ctypes 结构。

您可以通过以下两种方式之一解决问题。下面我调整了 C++ 代码以提供一些关于正确访问结构的反馈:

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;
}

选项 1:将指针转换为所需类型:

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)))

选项 2:像在纯 C 中那样声明 Python 中的结构,并传递指向基本结构的指针。这将与仅使用 C 数据类型的基础 class 具有相同的布局。

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)))

输出(两个选项):

7