ctypes and building metaclasses: TypeError: *class* instance instead of LP_*class* instance

ctypes and building metaclasses: TypeError: *class* instance instead of LP_*class* instance

故事: 我正在使用 ctypes 从 python 与 C 进行通信,反之亦然。我也在制作我也试图连接的 C 共享库。在这个开发步骤中,它只是一个简单的库,用于在深入编写代码之前测试所有概念。它是用 C++ 编写的库,使用 extern "C" 公开函数,没什么特别的。我用原始参数/return 类型、指针和函数回调测试了函数。

现在我想传递结构。由于我是一个懒惰的程序员,我打算将 C++ 结构传递给统一的 C 表示(即简单字典和列表的组合)并将其处理为 python,这会将其转换为真正的 python 对象(即 python 字典和列表的组合)。

问题:为了实现这一点,我首先在 C++ 中定义了一个模板化字典,它的实现只是为了测试,一个键值对的链表,字典拥有根。然后,对于每个需要专门化的函数,该专门化的 typedef 用作 C 结构。

代码如下所示(不是实际代码):

#include <cstdlib>

template <typename key_t, typename value_t>
struct DictNode
{
    key_t key;
    value_t value;
};

template <typename key_t, typename value_t>
struct Dict
{
    typedef DictNode<key_t, value_t> node_t;
    node_t root;
};

typedef Dict<int, char> Dict_int_char;

extern "C" Dict_int_char* example_new()
{
    Dict_int_char* result;
    result = (Dict_int_char*)malloc(sizeof(Dict_int_char));
    return result;
}

extern "C" void example_delete(Dict_int_char* value)
{
    free(value);
}

现在,在 python 中,为了避免为每个专业创建 class,我采用了相同的方法。给定键值类型,一种方法将为我创建专门的 class。

代码如下所示(实际代码):

import types
import ctypes

# This is to provide some hiding of the module internals
# Suggestions on a more pythonic way are gladly accepted
class __Internals:
  """
  Creates class to interface with a C structure comming from a
  typedef'd C++ class template specialization. This method recieves
  the types of the template class, creates the ctypes classes to
  interface with the specialized class (which has been typedef'd)
  and returns them for usage with ctypes.
  """
  @staticmethod
  def __DictClassCreate__(key_t, value_t):
    # Foward declare the classes
    class InterfaceListNode(ctypes.Structure):
      pass;
    class InterfaceList(ctypes.Structure):
      pass;

    #### NODE
    # Node class
    nodeType = InterfaceListNode;
    # The pointer-to-node class 
    nodeTypePointerType = ctypes.POINTER(nodeType);
    # Fields of the node class (next, key, value)
    nodeType._fields_ = [("next", nodeTypePointerType),
                         ("key", key_t),
                         ("value", value_t) ];

    # Function to create a node pointer
    def nodeTypePointerCreate(cls, value=None):
      if(value is None):
        return nodeTypePointerType();
      else:
        return nodeTypePointerType(value);

    # Bind the function to the node class
    nodeType.pointer = types.MethodType(nodeTypePointerCreate, nodeType);

    #### DICT
    # Dict class
    dictType = InterfaceList;
    # The pointer-to-dict class 
    dictTypePointerType = ctypes.POINTER(dictType);
    # Useful for dict to know the types of it's nodes
    dictType._nodeType = nodeType;
    # Fields of the dict class (root)
    dictType._fields_ = [("root", ctypes.POINTER(nodeType))];

    # Function to create a dict pointer
    def dictTypePointerCreate(cls, value=None):
      if(value is None):
        return dictTypePointerType();
      else:
        return dictTypePointerType(value);

    # Bind the function to the dict class
    dictType.pointer = types.MethodType(dictTypePointerCreate, dictType);    

    # For debugging
    print 'Inside metaclass generator'
    print hex(id(nodeType));
    print hex(id(dictType));

    # Return just the dict class since it knows about it's node class.
    return dictType;

# Create a new specialized dict<c_uint, c_char>
dictType_1 = __Internals.__DictClassCreate__(ctypes.c_uint, ctypes.c_char);
# Obtain the node type of this dict
nodeType_1 = dictType_1._nodeType;

# For debugging
print 'In Script'
print hex(id(nodeType_1));
print hex(id(dictType_1));

# Try to instance this dictionary with 1 element
#(not NULL root, NULL root.next)
dict_1 = dictType_1(nodeType_1(nodeType_1.pointer(), 0, 'a'));

运行此代码时,将显示以下输出:

python SciCamAPI.py
Inside metaclass generator
0x249c1d8L
0x249c588L
In Script
0x249c1d8L
0x249c588L
Traceback (most recent call last):
  File "SciCamAPI.py", line 107, in <module>
    dict_1 = dictType_1(nodeType_1(nodeType_1.pointer(), 0, 'a'));
TypeError: incompatible types, InterfaceListNode instance instead of LP_InterfaceListNode instance

从打印的输出中我可以看到我正在使用相同的元classes 来实例化简单的字典及其节点作为方法中生成的节点。

我用谷歌搜索了错误中可能附加的 LP_,但搜索 LP_ python 仅 returns 线性问题求解器和 this answer。从对答案的理解来看,ctypes 正在从 nodeType_1.pointer() (最后一行)创建一个 C 样式的指针,但这就是当 node.next 被声明为 [("next", nodeTypePointerType),...] (在 nodeType.fields=... 中)。所以我很迷茫。

引用eryksun的评论:

dictType_1.root is an LP_InterfaceListNode field, i.e. POINTER(InterfaceListNode), but you're initializing it with an InterfaceListNode instead of a pointer to one. "LP" is for "long pointer" from back in the day of segmented architectures that had near (within segment) and far/long pointers. Windows types retain this prefix even though it's no longer meaningful, such as LPVOID and LPWSTR. ctypes was initially a Windows-only package

所以我错过了将 dictType_1.root 转换为指针。将最后一行更改为:

rootNode = nodeType_1(nodeType_1.pointer(), 0, 'a');
dict_1 = dictType_1(nodeType_1.pointer(rootNode));

问题解决。