-1

I have a .py file, that holds a 3d lists of signed floats.

test.py

def myfunc():
    tabToReturn = [[[-1.03,5.68],[4.16,-78.12]],[[74.1,8.95],[59.82,1.48]],[[74.1,8.95],[59.82,87.4]]]
    print(tabToReturn)
    return tabToReturn

I want to call that .py, make it return this 3d list, and convert it to a 3D vector for my c++ program.

Here is an example to check if I get what we need:

program.cpp

#include <iostream>
std::vector<std::vector<std::vector<float>>> callPython();
int main(int argc, char** argv)
{


    std::vector<std::vector<std::vector<float>>> my3DVector = callPython();
    

    std::cout << "Result we got : "<< std::endl;

    for (int i = 0; i < my3DVector.size(); i++)
    {


        for (int j = 0; j < my3DVector[i].size(); j++)
        {

            for (int k = 0; k < my3DVector[i][j].size(); k++)
            {
                std::cout << my3DVector[i][j][k];
                std::cout << " ";

            }


        }
        std::cout << "" << std::endl;
    }
}

The output should be :

Result we got :
-1.03 5.68
4.16 -78.12
74.1 8.95
59.82 1.48
74.1 8.95
59.82 87.4

I have already seen some questions on this subject, here and here

The problem is that one of the questions has an answer I don't understand at all, and the 2nd is only about a float to return, and I need a 3D vector, I didn't find what I need here

Also, this tutorial does not explain how to pass a 3d list, or even a list at all.

Gaston
  • 185
  • 7
  • Are you allowed to cheat and treat the Python array output as JSON? – Botje Jul 29 '21 at 14:23
  • @Botje Yes, I am allowed to do everything. But it is better to avoid that, if possible, because I have no knowledge on JSON. – Gaston Jul 29 '21 at 14:25
  • A bit hacky, but you could have the script output to a file, and then C++ could read the file and create the vector from it. One line would be one of your pair of numbers. Or you could output to some format (JSON was mentionned, but something else could be used too). – ShadowMitia Jul 29 '21 at 14:37

3 Answers3

2

On the Python side:

json.dump(mylist, fp=open("out.json", "w"))

On the C++ side, using nlohmann::json, a header-only library:

system("python mypyfile.py");

using nlohmann::json;
std::ifstream jsonfile("out.json");
json mylist;
jsonfile >> mylist;

std::vector<std::vector<std::vector<float>>> matrix;
for (auto& dim1: mylist) {
  std::vector<std::vector<float>> dim1v;
  for (auto& dim2: dim1) {
    std::vector<float> dim2v;
    for (auto& elem: dim2) {
      dim2v.push_back(elem.get<float>());
    }
    dim1v.push_back(dim2v);
  }
  matrix.push_back(dim1v);
}
Botje
  • 26,269
  • 3
  • 31
  • 41
0

Even though what has answered @Botje is (probably) right, I got a solution without using json.

After having struggle a bit, I have discovered the source of the C-API :

Thanks to that I found a solution to solve this no matter the dimension of your vector.

Here is the details :

#include <Python.h> //first to import
#include <iostream>
std::vector<std::vector<std::vector<float>>> callPython()
{
    std::cout << "Initialize the library" << std::endl;
    Py_Initialize();

    std::cout << "I get the python file name" << std::endl;
    PyObject* pyFile = PyUnicode_FromString((char*)"test");


    std::cout << "I import it" << std::endl;
    PyObject* myModule = PyImport_Import(pyFile);

    auto mdict = PyModule_GetDict(myModule);



    std::cout << "I get the name of the function I want to use from my python file" << std::endl;
    PyObject* myFunction = PyDict_GetItemString(mdict, "myfunc");


    std::cout << "I create the parameter I need to use" << std::endl;
    
    
    std::cout << "INFO : According to the API, we do not use PyString_ since Python 3.X anymore, but PyBytes" << std::endl;
    //----
    std::cout << "We call the function (with the appropriate parameter), we have imported and store the result in my result" << std::endl;
    auto myResult = PyObject_CallFunction(myFunction, NULL);

    if (!PyList_Check(myResult))
    {
        std::cout << "(!) (!) (!) THAT IS NOT A LIST (!) (!) (!)" << std::endl;
    }


    //----Getting the element givent by the python program, and store them in a 3d vector

    std::cout << "We iterate through the list stored in myResult and store everything in a 3D vector" << std::endl;
    //--the 3d vector we need, you must change its dimension if you work with different dimensions
    std::vector<std::vector<std::vector<float>>> my3DVector;
    //--each list in another list is a Python Object that we should extract
    
for (Py_ssize_t i = 0; i < PyList_Size(myResult); i++)
{
    PyObject* firstDim = PyList_GetItem(myResult, i);

    std::vector<std::vector<float>> secondDimCpp; 

    for (Py_ssize_t j = 0; j < PyList_Size(firstDim); j++)
    {
        PyObject* secondDim = PyList_GetItem(firstDim, j);

        std::vector<float> lastDimCpp; 


        for (Py_ssize_t k = 0; k < PyList_Size(secondDim); k++)
        {
            PyObject* thirdDim = PyList_GetItem(secondDim, k); //we get the value of an axis

            float value = PyFloat_AsDouble(thirdDim); //we convert it to a double

            lastDimCpp.push_back(value); //we store it in a vector

        }

        secondDimCpp.push_back(lastDimCpp);
    }
    my3DVector->push_back(secondDimCpp);
}



    return my3DVector;
}

note : I return the vector by value here, this is not efficient, you might prefer to return it by a smart pointer

Also, since this is a multidimensionnal list, we must use the ListObjects

Gaston
  • 185
  • 7
0
#creating 3d list

3D_list= [[ [A,B], [C,D] ] ,[ [E,F], [G,H] ], [ [I,J],[K,L] ] ]

#printing 3d list

print(3D_list)
naqviO7
  • 41
  • 4