Embedding Python in C++. Passing vector of strings receving a list of lists
Question: I have a Windows application that is written in C++. I have a vector of
strings that I need to pass to a python Script for processing. I know how to
embed python in C++ with simple type but I am looking at how to create a
python object equivalent to a vector of string. I am looking at something
like:

    
    
    for (size_t i = 0; i < myvector.size(); ++i)
    {
        PyUnicode_FromString.append(myvector[i]);
    }
    

or maybe

    
    
    for (size_t i = 0; i < myvector.size(); ++i)
    {
        pValue = PyUnicode_FromString(myvector[i]);
        pyTuple_SetItem(myvector.size(), i, pValue);
    }
    

The vector will hardly ever get very large (100 items max I would say). At the
moment I am just saving a text file and I have the python script opening it
and processing it which is obviously not very good but it tells me that
everything else works as planned. The processing from the Python script
produce 4 values per item(3 strings and 1 integer(long)). I also need to
return this the the main C++ program and I have no idea how to go about that.
(EDIT) After reviewing the possible options, I am thinking of a list of lists
(since dictionaries are not ordered and require more parsing operations)
should do the trick but I don't know how I would go about decoding this back
inside the C++ program (again for now this is done with file writing/reading
so I know it works). If anyone has done this, can you please provide small
code snippets so I can adapt it to my needs. I should also mention that I
cannot use the boost library(and preferably no SWIG either) - so, basically
done in Python C-API. All examples I have seen talks about subprocesses or
NumPy which I do not believe (maybe incorrectly) apply to my case.


Answer: Alright, I managed to figure out what I wanted. Below is a fully working
example of what I was trying to do. The answer is however not complete as
there are quite a few error checking missing and more importantly a few
Py_DECREF missing. I will try to catch them and update as I figure it out but
if someone fluent in that sort of thing can help, that would be much
appreciated. Here goes:

Python script: (testingoutput.py) This script receives a list of strings, For
each string, it returns 3 random strings (from a provided list) and one random
integer. Format is: [[sssi],]

    
    
    import random
    import string
    
    def get_list_send_list(*input_list):  
        outer_list = []   
        for key in input_list:        
            inner_list = []
            # from here, your program should produce the data you want to retrieve and insert it    in a list
            # for this example, a list comprised of 3 random strings and 1 random number is returned
            for i in range (0, 3):
                some_words = random.choice(["red", "orange", "yellow", "green", "blue", "purple", "white", "black"])
                inner_list.append(some_words)        
            inner_list.append(random.randint(1,10))
            outer_list.append(inner_list)
        return outer_list
    

And this is the cpp file. Largely the same as the Python C API example but
slightly modified to accomodate lists. I did not need this for my needs but I
put a few type checking here and there for the benefit of anyone who might
need that sort of thing.

    
    
    #include <Python.h>
    #include <iostream>
    #include <vector>
    
    int main(int argc, char *argv[])
    {
        PyObject *pName, *pModule, *pFunc;
        PyObject *pArgs, *pValue;
        PyObject *pList, *pListItem, *pyString; 
    
        char* strarray[] = {"apple", "banana", "orange", "pear"};
        std::vector<std::string> strvector(strarray, strarray + 4);
        std::string pyFile = "testingoutput";
        std::string pyFunc = "get_list_send_list";
    
        Py_Initialize();
        pName = PyUnicode_FromString(pyFile.c_str());
        /* Error checking of pName left out */
    
        pModule = PyImport_Import(pName);
        Py_DECREF(pName);
    
        if (pModule != NULL)
        {
            pFunc = PyObject_GetAttrString(pModule, pyFunc.c_str());
            /* pFunc is a new reference */
    
            if (pFunc && PyCallable_Check(pFunc)) {
                pArgs = PyTuple_New(strvector.size());
          for (size_t i = 0; i < strvector.size(); ++i)
                {
                    pValue = PyUnicode_FromString(strvector[i].c_str());
                    if (!pValue)
                    {
                        Py_DECREF(pArgs);
                        Py_DECREF(pModule);
                        fprintf(stderr, "Cannot convert argument\n");
                        return 1;
                    }
                    /* pValue reference stolen here: */         
                    PyTuple_SetItem(pArgs, i, pValue);
                }           
    
                pValue = PyObject_CallObject(pFunc, pArgs);
                Py_DECREF(pArgs);
    
          if (pValue != NULL)
          {
        int py_list_size = PyList_Size(pValue);
        int sub_list_size = 0;
        std::cout << "Retrieving content..."<< "\n";
        for(Py_ssize_t i = 0; i < py_list_size; ++i)
        {
            pList = PyList_GetItem(pValue, i);
            sub_list_size = PyList_Size(pList);
            // verify if the subitem is also a list - if yes process it
            if(PyList_Check(pList))
            {
                std::cout << "********** " << i << " **********\n";
                for(Py_ssize_t j = 0; j < sub_list_size; ++j)
                {
                    pListItem = PyList_GetItem(pList, j);
                    // verify if the item is a string or a number
                    if(PyUnicode_Check(pListItem))
                    {
                         // "Error ~" does nothing here but it should be defined to catch errors
                         pyString = PyUnicode_AsEncodedString(pListItem, "utf-8", "Error ~");
                         const char *tmpCstChar =  PyBytes_AS_STRING(pyString);
                         std::cout << "Item " << j << ": " << tmpCstChar << "\n";
                     }
                     else if(PyLong_Check(pListItem))
                     {
                         int pyNumber = PyLong_AsLong(pListItem);
                         std::cout << "Item " << j << ": " << pyNumber << "\n";
                     }
                 }
             }
             else
             {
                 std::cout << "This item is not a list\n";
             }
         }
         Py_DECREF(pValue);
         }
                else
                {
                    Py_DECREF(pFunc);
                    Py_DECREF(pModule);
                    PyErr_Print();
                    fprintf(stderr,"Call failed\n");
                    return 1;
                }
            }
            else
            {
                if (PyErr_Occurred())
                    PyErr_Print();
                fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
            }
            Py_XDECREF(pFunc);
            Py_DECREF(pModule);
        }
        else
        {
            PyErr_Print();
            fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
            return 1;
        }
        Py_Finalize();
        return 0;
    }
    

Again, there are a few error checks missing and significantly more important,
a few Py_DECREFs missing. i.e. THIS PROGRAM IS LEAKING MEMORY. If you know how
to fix this, your help would be appreciated.

