0

So I'm writing a C++ application that uses embedded calls to Python to run some computations. I have code working for running any script in a specified directory, as well as passing and returning one value between the C++ application and the Python interpreter.

I am, however, having a problem when I try to pass more than one value as a parameter to Python. The following is my code for running a Python script:

double Script::run_script(string moduleName, string funcName, double *params, 
              int numberOfParams)
{
  PyObject *pName, *pModule, *pDict, *pFunc;
  PyObject *pArgs, *pValue;
  int i;

  // Set aside memory for the path to my scripts to add to sys.path
  char dir[255];
  memset(dir, 0, sizeof(dir));
  /* ********************************************* */
  sprintf(dir, "/home/bmalone/workspace/NumericalAnalysis/Testing"); //Put path into dir
   /* ********************************************* */

  Py_Initialize();

  PyObject *dictModule = PyImport_GetModuleDict();
  PyObject *sys = PyDict_GetItemString(dictModule, "sys");

  // Sys NULL?
  if (sys == NULL)
    {
      cerr << "Cannot find sys in dict!" << endl;
      return -1;
    }

  dictModule = PyModule_GetDict(sys);
  PyObject *path = PyDict_GetItemString(dictModule, "path");

  PyObject *pPath = PyUnicode_FromString(dir);
  PyList_Append(path, pPath);

  if (pPath);
    //cerr << "PATH > " << PyString_AsString(PyObject_Repr(path)) << endl;
  else
    cerr << "pPath IS NULL" << endl;


  /* Import the module that contains the function/script */
  pModule = PyImport_ImportModule(moduleName.c_str());

  if (pModule);
    //cerr << "pModule ~ " << PyString_AsString(PyObject_Repr(pModule)) << endl;
  else
    cerr << "pModule is NULL!!" << endl;

  //cerr << "Getting dict for argv[1]..." << endl;
  dictModule = PyModule_GetDict(pModule);
  if (dictModule);
    //cerr << "DictModule ~ " << PyString_AsString(PyObject_Repr(dictModule))  << endl;
  else
    cerr << "DictModule is NULL!!" << endl;

  // CALL THE FUNCTION!
  /*
   * argv[1] is the MODULE name, but argv[2] SHOULD BE the function name!
   */
  /* Get a PyObject referencing the function from the module 'pModule' */
  //pFunc = PyObject_GetAttrString(pModule, funcName.c_str());
  pFunc = PyDict_GetItemString(dictModule, funcName.c_str());

  if (pFunc)
    {
      PyObject *objRep = PyObject_Repr(pFunc);
      const char* str = PyString_AsString(objRep);
      cerr << "Func is a real boy, too! - " << str  << endl;
    }
  else
    cerr << "pFunc is NULL!" << endl;

  /* Setup PyArgs */
  pArgs = PyTuple_New(numberOfParams);
  // Add the data as doubles
  cerr << "<parameters> ~ params size is " << numberOfParams  << endl;
  for (int i = 0; i < numberOfParams; i++)
    {
      if (params)
    {
      /* **************************** */
      //pValue = PyDouble_AsDouble(params[i]);
      pValue = PyFloat_FromDouble(params[i]);
      /* **************************** */

      cerr << " " << params[i] << endl;
      PyObject* objRep = PyObject_Repr(pValue);
      const char* strRep = PyString_AsString(objRep);
      cerr << ">> pValue = " << strRep << endl;
      PyTuple_SetItem(pArgs, i, pValue);
    }
      else
    break;
    }

  cerr << "<Module> - " << moduleName << ", is null: " << (pModule == NULL) << endl;
  cerr << "<Function> - " << funcName << ", is null: " << (pFunc == NULL) << endl;

  // Get the retun value
  cerr << "<Calling Function>" << endl;
  //pValue = PyObject_CallFunction(pFunc, "f", 15.0); // **WORKS**
  pValue = PyObject_CallObject(pFunc, pArgs); // **WORKS**

  //pValue = PyObject_CallObject(pFunc, NULL);
  //pValue = PyEval_CallObject(pFunc, NULL);
  //pValue = PyObject_CallFunction(pFunc, NULL);
  //pValue = PyObject_CallObject(pFunc, args);

  //pValue = PyEval_CallObject(pFunc, args);

  //cerr << "ANSWER ---> " << PyFloat_AsDouble(pValue) << endl;

  return PyFloat_AsDouble(pValue);
  Py_Finalize();
}

EDIT: In my original post, I made a change I forgot I made, and it was a bug that prevented any script from running. The above code correctly runs any script, except it only works if pArgs has --1-- element. Any time I try to pass more than one element, even to the same script just to be printed out, the script never even runs. But if it's just 1 then it works perfectly.

I can't seem to figure out what I'm doing wrong to pass more than 1 parameter to my scripts. This is the script I'm trying to run from C++ (the one that works for 1 parameter):

#!/usr/bin/python
'''
Created on Jul 12, 2012

@author: bmalone
'''
import sys


def doubleTwo(x, y):
    print str(sys.argv)
    print "Can haz doubling?\n"


if __name__ == '__main__':
    doubleTwo()

Any help would be appreciated!

Mister R2
  • 861
  • 5
  • 12
  • 22
  • Sorry if I overlooked anything, but did you include the C++ code that calls `Script::run_script()`? The error might be related to how you construct the array of doubles that you pass as `params`. – jogojapan Jul 13 '12 at 03:22

1 Answers1

0

I figured out the problem I was having. In my functions, I'm trying to access command line arguments, whereas in writing my functions I'm specifying they're being passed arguments -- which I'm not doing.

In the future, for anyone finding this having trouble with embedded Python, I found that there's an extremely helpful error section in the Python-C API that, if PyErr_Occurred() is true, PyErr_Print() prints the error to stderr.

I either needed to fix passing actual parameters, or (what I ended up going with) is using "PySys_SetArgv" before I call the function with "PyObject_CallObject" and access the parameters I need in Python by indexing on sys.argv.

Mister R2
  • 861
  • 5
  • 12
  • 22