1

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

1 Answer 1

1

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".

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you very much for example code, it works perfectly. Can you explain to me why there is [0] in p[i][0]?
p[i] is a pointer. You have the option to use p[i].contents or to get the 0th element. Like I said, I'd rather use an array of structs. In that case p[i] would be a myStruct instance. But I'd have to rewrite the code to do it that way. I figured I'd present it closer to what you already had.
Can you suggest me how and at which point should I free memory. I have to do that because of result = new myStruct *[n]; , right?
Export another function to free the memory. It needs parameters for the array and its length. Loop through the array to delete[] the wchar_t arrays for each struct and then delete the struct. Finally, delete[] the array itself.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.