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