pybind11: преобразование типов данных из Python в C ++ не работает - PullRequest
1 голос
/ 17 марта 2019

Проблема

Я пытаюсь преобразовать список списков, возвращаемых функцией python, вызываемой внутри кода C ++.Хотя библиотека pybind11 позволяет преобразовывать типы из типов данных Python в типы данных C ++, моя попытка преобразовать список списков, возвращаемых python, в std::list из std::list строк C ++, каждый раз терпит неудачу.

Код

Вот функция python (функция возвращает список списка, содержащий строковые значения):

def return_sheet(self):

     """Returns the sheet in a list of lists

     """

     dataTable = []

     for r in range(self._isheet.nrows):

         datalist = []

         for c in range(self._isheet.ncols):

             datalist.append(self._isheet.cell_value(r,c))

         dataTable.append(datalist)

 return dataTable

И здесь я вызываю его в C ++, используя pybind11 :

py::list obj = _tool.attr("return_sheet")();

data = py::cast<SheetData>(obj); // This is where the problem lies, This cast crashes the program

Где SheetData - это typedef для:

typedef std::list<std::list<std::string> > SheetData;

Во время отладки я обнаружил, что на самом деле происходит сбой программы приэта строка:

py::object dataTable = _tool.attr("return_sheet")(); // Where _tool.attr("return_sheet")() gives an py::object which is a list of list of str

Кто-нибудь знает, как я могу успешно преобразовать список списков Python в std::list из std::list C ++?

EDIT

Вот файл программы Python, который я встраиваю в c ++ [xlanalyser.py]: https://pastebin.com/gARnkMTv

А вот код c ++ [main.cpp]: https://pastebin.com/wDDUB1s4

Примечание: все другие функции в xlanalyser.py не вызывают сбой при встраивании в c ++ [только функция return_sheet () cauSES крушение]

1 Ответ

0 голосов
/ 24 марта 2019

Вы можете использовать API Python / C в качестве обходного пути (функция проверки CastToSheetData).Ниже приведен полный пример:

program.py

def return_matrix():

    dataTable = []
    for r in range(0,2):
        datalist = []

        for c in range(0,2):
            datalist.append(str(r+c))

        dataTable.append(datalist)
    return dataTable

main.cpp

#include <pybind11/embed.h>
#include <iostream>
#include <list>
#include <string>

typedef std::list<std::list<std::string> > SheetData;

namespace py = pybind11;

SheetData CastToSheetData(PyObject *obj)
{
    SheetData data;
    PyObject *iter = PyObject_GetIter(obj);

    if (!iter)
        return data;
    while (true) {
        std::list<std::string> aux_list;
        PyObject *next = PyIter_Next(iter);
        if (!next) {
            // nothing left in the iterator
            break;
        }
        PyObject *iter2 = PyObject_GetIter(next);
        if (!iter2)
            continue;
        while(true) {
            PyObject *next2 = PyIter_Next(iter2);
            if (!next2) {
                // nothing left in the iterator
                break;
            }
            PyObject* pyStrObj = PyUnicode_AsUTF8String(next2); 
            char* zStr = PyBytes_AsString(pyStrObj); 
            std::string foo(strdup(zStr));
            aux_list.push_back(foo);
            Py_DECREF(pyStrObj);
        }
        data.push_back(aux_list);
    }

    return data;
}


int main()
{
    py::scoped_interpreter guard{};
    py::module calc = py::module::import("program");
    py::object result = calc.attr("return_matrix")();

    SheetData data = CastToSheetData(result.ptr());

    for (auto l : data)
    {
        std::cout << "[ ";
        for(auto s : l)
            std::cout << s <<  " ";
        std::cout << "]" << std::endl;
    }

    return 0;
}

Вывод:

[ 0 1 ]
[ 1 2 ]

Возможно,лучший способ сделать это - создать пользовательский type_caster , используя код, подобный коду в функции CastToSheetData внутри load метода

...