Py_InitModule with multiple functions - invalid conversion from int to PyCFunction

1.7k views Asked by At

I am transitioning to removing the boost-python dependencies in my code, and I have made to to the "final step" of this transition (I removed all other boost dependencies, and when I comment out the boost code below I get ImportError: dynamic module does not define init function (initMy_module).

Here is the code as it stands

#include <boost/python.hpp>
namespace bpn = boost::python::numeric;
using namespace boost::python;
BOOST_PYTHON_MODULE(My_module)
{
  using namespace bpn;
  import_array();
  array::set_module_and_type("numpy", "ndarray");

  register_exception_translator<int>(&translator);

  def("run_My_module", run_My_module, "run my moduule");
  def("run_My_module_another_way", run_My_module_another_way, "run my module another way");
}

From my understanding of the python/C API, I believe that the following code should superficially link my C and Python code.

static PyMethodDef myModule_methods[] = {
    {"run_My_module", run_My_module, METH_VARARGS},
    {"run_My_module_another_way", run_My_module_another_way, METH_VARARGS},
    {NULL, NULL}
    };

void initmyModule(void)
{
    // Init module.
    (void) Py_InitModule("myModule", myModule_methods);

}

However, this produces the following errors:

myModule_python_binding.cpp:126:5: error: invalid conversion from 'int (*)(char*, char*, char*, PyObject*) {aka int (*)(char*, char*, char*, _object*)}' to 'PyCFunction {aka _object* (*)(_object*, _object*)}' [-fpermissive]
     };
     ^
myModule_python_binding.cpp:126:5: error: invalid conversion from 'int (*)(char*, ompi_communicator_t*&, char*, char*, PyObject*) {aka int (*)(char*, ompi_communicator_t*&, char*, char*, _object*)}' to 'PyCFunction {aka _object* (*)(_object*, _object*)}' [-fpermissive]

both functions are of this form. They both return integers indicating their level of success.

int run_myModule(char *infile, char *outfile, char *errfile, pyObject *exc)
{...};

int run_myModule_another_way(char *infile, int &_exvar, char *outfile, char *errfile, pyObject *exc)
{...};

Why is my "superficial" connection failing?

1

There are 1 answers

0
mshildt On

You are getting the compile time error because the function pointers that you are using in myModule_methods[] are of the wrong type/signature. The functions that you want to call directly from Python need to have the following signature:

PyObject *foo(PyObject *self, PyObject *args)

So if you want to call your run_my_Module and run_my_Module_another_way functions from Python, you need to create the necessary "glue" between your functions and Python by wrapping them in a function that has the above signature. For example:

static PyObject *run_My_module_wrapper(PyObject *self, PyObject *args)
{
    // Parse arguments from Python out of the args parameter using
    // PyArg_ParseTuple. i.e. somthing like:
    const char *infile, *outfile, *errfile;
    PyObject *exc;

    if (!PyArg_ParseTuple(args, "sssO", &infile, &outfile, &errfile, &exc)) {
        return NULL; // Invalid arguments
    }

    //
    // Now you can call your function with the arguments from the Python call.
    //
    int r = run_My_module(infile, outfile, errfile, exc);

    if (r == SUCCESSFUL_VALUE) {
        Py_RETURN_NONE;
    }
    else {
        return NULL;
    }
}

// Similar setup for run_My_module_another_way.

static PyMethodDef myModule_methods[] = {
    {"run_My_module", run_My_module_wrapper, METH_VARARGS},
    {"run_My_module_another_way", run_My_module_another_way_wrapper, METH_VARARGS},
    {NULL, NULL}
};

void initmyModule(void)
{
    // Init module.
    (void) Py_InitModule("myModule", myModule_methods);

}

See Parsing Arguments for more info on parsing arguments passed in from Python. Plus Extending and Embedding the Python Interpreter for all of the general info you need.