Вопрос

I have successfully returned pointer to struct(that contains wchar_t*) from c++ dll into Python like this: C++ code:

...
typedef struct myStruct{
    wchar_t* id; 
    wchar_t* content; 
    wchar_t* message;
} myStruct;

DLLAPI myStruct* DLLApiGetStruct(){
    myStruct* testStruct = new myStruct();
    testStruct->id = _T("some id"); 
    testStruct->content = _T("some content"); 
    testStruct->message = _T("some message");
    return testStruct;
}

Python code:

class MyPyStruct(Structure):
    _fields_ = [
        ("id", c_wchar_p),
        ("content", c_wchar_p),
        ("message", c_wchar_p)
        ]
...
...
myDLL = cdll.LoadLibrary('myDLL.dll')
myDLL.DLLApiGetStruct.restype = POINTER(MyPyStruct)
result = myDLL.DLLApiGetStruct().contents
print result.id, result.content, result. message# those are valid values

Ok, that works fine, the PROBLEM is that now I need to return pointer on vector of pointers to those structures. I've tried this:

C++ code:

typedef std::vector<myStruct*> myVector;
...
DLLAPI myVector* DLLApiGetVector(){
    myVector* testVektor = new myVector();
    for(i=0; i< 5; i++){
        myStruct* testStruct = new myStruct();
        testStruct->id = _T("some id"); 
        testStruct->content = _T("some content"); 
        testStruct->message = _T("some message");
        testVektor->push_back(testStruct);
    }
    return testVektor;// all values in it are valid
}

Python code:

#I think that first and second lines are incorrect (is that proper way to make restype??):

vectorOfPointersType = (POINTER(DeltaDataStruct) * 5)  #5 is number of structures in vector
myDLL.DLLApiGetVector.restype = POINTER(vectorOfPointersType)
vectorOfPointersOnMyStruct= myDLL.DLLApiGetVector.contents
for pointerOnMyStruct in vectorOfPointersOnMyStruct:
   result = pointerOnMyStruct.contents
   print result.id, result.content, result.message

Values in last row are NOT valid - it's some random parts of memory I guess. This is error I get:

UnicodeEncodeError: 'charmap' codec can't encode characters in position 0-11: character maps to <undefined>
Это было полезно?

Решение

A vector is C compatible, but you need to pass the C caller (or ctypes) the address of the first element. That said, you'd have to hold on to the pointer to the vector to free it later. I think you'll be better off using an array from the start. You can pass the function an int out parameter to receive the length of the array. Since you're using new to allocate, remember to catch a bad_alloc exception should the allocation fail.

Personally, I'd use an array of structs instead an array of pointers, such that the data is in single contiguous block. This yields a cleaner interface in ctypes. With an array of pointers you have to dereference twice to get at the struct.

C++:

#include <new>
#include <cwchar>

typedef struct myStruct {
    wchar_t *id;
    wchar_t *content;
    wchar_t *message;
} myStruct;

const wchar_t ID[] = L"some id";
const wchar_t CONTENT[] = L"some content";
const wchar_t MESSAGE[] = L"some message";

DLLAPI myStruct **DLLApiGetArray(int *size)
{
    int i, n = 5;
    myStruct **result;
    try {
        result = new myStruct *[n];
        for(i = 0; i < n; i++) {
            myStruct *tmp = new myStruct();
            tmp->id = new wchar_t[wcslen(ID) + 1];
            tmp->content = new wchar_t[wcslen(CONTENT) + 1];
            tmp->message = new wchar_t[wcslen(MESSAGE) + 1];
            wcscpy(tmp->id, ID);
            wcscpy(tmp->content, CONTENT);
            wcscpy(tmp->message, MESSAGE);
            result[i] = tmp;
        }
    } catch (std::bad_alloc &ba) {
        *size = -1; return NULL;
    }
    *size = n; return result;
}

Python:

from ctypes import *

class myStruct(Structure):
    _fields_ = [
        ("id", c_wchar_p),
        ("content", c_wchar_p),
        ("message", c_wchar_p)
    ]

myDLL = cdll.myDLL
myDLL.DLLApiGetArray.restype = POINTER(POINTER(myStruct))
myDLL.DLLApiGetArray.argtypes = [POINTER(c_int)]

n = c_int()
p = myDLL.DLLApiGetArray(byref(n))
n = n.value

Example looping through the result:

>>> for i in range(n):
...     print i, p[i][0].id
...
0 some id
1 some id
2 some id
3 some id
4 some id

FYI, it's incorrect to use the _T macro with explicit wchar_t arrays. That's for Microsoft's TCHAR type, for compiling as ANSI vs Unicode. Use L"wide character string literals".

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top