2016-02-24 121 views
1

故事:我使用ctypes從python通信到C和其他方式。我也在製作我正在嘗試連接的C共享庫。在這個開發階段,它只是一個簡單的庫,在深入代碼之前測試所有的概念。它是用C++編寫的,用extern「C」暴露函數,沒什麼特別。我用原始參數/返回類型,指針和函數回調來測試函數。ctypes和構建元類:類型錯誤:*類*實例,而不是LP_ *類*實例

現在我想通過結構。由於我是一個懶惰的程序員,我打算將C++結構傳遞給一個統一的C表示形式(即簡單字典和列表的組合)並將其處理爲python,然後將其轉換爲真正的python對象(即.a 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); 
} 

現在,蟒蛇,以避免產生一類爲每一個專業化的,我下面的相同的方法。一個方法將爲我創建給定鍵值類型的專用類。

的代碼看起來是這樣的(實際的代碼):

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 

從打印輸出我可以看見我使用相同的元類來實例化簡單字典,並且它的節點與方法中生成的節點一樣。

我已經在搜索錯誤中附加了LP_,但搜索LP_ python只返回線性問題求解器和this answer。從理解的答案ctypes是從nodeType_1.pointer()(最後一行)創建一個C風格的指針,但那是當node.next被聲明爲[(「next」,nodeTypePointerType) ,...](在nodeType中,字段 = ...)。所以我很迷路。

+0

'dictType_1.root'是一個'LP_InterfaceListNode'字段,即'POINTER(InterfaceListNode)',但是你用一個'InterfaceListNode'而不是一個指針來初始化它。 「LP」是指在分段架構的那一天(在段內)和遠/長指針之後的「長指針」。 Windows類型保留此前綴,即使它不再有意義,例如'LPVOID'和'LPWSTR'。 ctypes最初是一個僅限Windows的軟件包。 – eryksun

+0

謝謝,就是這樣!將最後一行替換爲:'rootNode = nodeType_1(nodeType_1。指針(),0,'a'); dict_1 = dictType_1(nodeType_1.pointer(rootNode));',這樣做。你應該發佈一個接受它的接受者,或者我引用你的話嗎? – jabozzo

回答

0

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

解決問題。

相關問題