I am using SIP version 6.0.1 with python 3.8 and have a given c++11 API. (on Ubuntu 18.04)
The goal is to store e.g. custom struct pointers (the structs I also translated with sip) in std::list using python.
I constructed a tiny example to make my case:
std_list.sip file
%Module(name=std_list, language="C++")
%Include type_list.sip
%DefaultEncoding "UTF-8"
struct TestStruct {
%TypeHeaderCode
#include <std_list.h>
%End
int test1;
int test2;
void printStruct() const;
TestStruct();
};
typedef std::list<TestStruct*> ListTestStruct;
struct StructContainerLvl2 {
%TypeHeaderCode
#include <std_list.h>
%End
ListTestStruct listTestStruct;
StructContainerLvl2();
};
type_list.sip file for the list translation
template<TYPE *>
%MappedType std::list<TYPE *> /TypeHint="List[TYPE]"/ {
%TypeHeaderCode
#include <list>
%End
%ConvertFromTypeCode
PyObject *l;
const sipTypeDef* kpTypeDef = sipFindType("TYPE");
if (!kpTypeDef) {
return NULL;
}
// Create the Python list of the correct length.
if ((l = PyList_New(sipCpp->size())) == NULL) {
return NULL;
}
int i = 0;
for(std::list<TYPE *>::iterator iter = sipCpp->begin(); iter != sipCpp->end(); iter++) {
TYPE *cpp = *iter;
PyObject *pobj;
// Get the Python wrapper for the Type instance, creating a new
// one if necessary, and handle any ownership transfer.
if ((pobj = sipConvertFromType(cpp, kpTypeDef, sipTransferObj)) == NULL) {
// There was an error so garbage collect the Python list.
Py_XDECREF(l);
return NULL;
}
// Add the wrapper to the list.
PyList_SET_ITEM(l, i++, pobj);
}
// Return the Python list.
return l;
%End
%ConvertToTypeCode
const sipTypeDef* kpTypeDef = sipFindType("TYPE");
if (!kpTypeDef) {
return 0;
}
// Check if type is compatible
if (sipIsErr == NULL) {
if (!PyList_Check(sipPy)) {
return 0;
}
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(sipPy); ++i) {
PyObject *item = PyList_GET_ITEM(sipPy, i);
if (!sipCanConvertToType(item, kpTypeDef, SIP_NOT_NONE)) {
return 0;
}
}
return 1;
}
// Convert Python list of TYPE to std::list<TYPE*>
std::list<TYPE*> *l = new std::list<TYPE*>();
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(sipPy); ++i) {
int state;
PyObject *item = PyList_GET_ITEM(sipPy, i);
TYPE* p = static_cast<TYPE*>(sipConvertToType(item, kpTypeDef, NULL, SIP_NOT_NONE, &state, sipIsErr));
if (*sipIsErr) {
sipReleaseType(p, kpTypeDef, state);
delete l;
return 0;
}
sipTransferTo(item, item);
l->push_back(p);
sipReleaseType(p, kpTypeDef, state);
}
*sipCppPtr = l;
return sipGetState(sipTransferObj);
%End
};
std_list.h file
#include <list>
struct TestStruct {
int test1{};
int test2{};
void printStruct() const;
TestStruct() = default;
};
typedef std::list<TestStruct*> ListTestStruct;
struct StructContainerLvl2 {
ListTestStruct listTestStruct;
StructContainerLvl2() = default;
};
test.py file
import std_list
if __name__ == '__main__':
testStruct1 = std_list.TestStruct()
testStruct1.test1 = 1
testStruct1.test2 = 2
testStruct2 = std_list.TestStruct()
testStruct2.test1 = 21
testStruct2.test2 = 22
temp_structContainerLvl2 = std_list.StructContainerLvl2()
print(temp_structContainerLvl2.listTestStruct)
temp_structContainerLvl2.listTestStruct.append(testStruct1)
temp_structContainerLvl2.listTestStruct.append(testStruct2)
print(temp_structContainerLvl2.listTestStruct)
for item in temp_structContainerLvl2.listTestStruct:
print("List item {}".format(item))
print()
The problem is that the list is always empty. No matter what I try to store in it. The error is probably in the type_list.sip file. I updated an example I found, but I'm not sure if its right.
Can someone help?
Thanks Johnny
The problem is not in the type_list.sip file, but in the test.py file.
The cmd of appending something to a translated python list with append like ...
temp_structContainerLvl2.listTestStruct.append(testStruct1)
can not be used for the sip translation of a python list.
As a workaround, one can use
temp_structContainerLvl2.listTestStruct = [testStruct1, testStruct2]
and
temp_structContainerLvl2.listTestStruct += [testStruct3]
I imagine it is not that efficient, but all I've got for now.