2

within a framework of a simulation of some biophysical model I have a C++ class that implements my model with a member function that needs return a PyArrayObject*. The class is defined in the header file ng_networks.h which reads something like:

#include <stdio.h>
#include <math.h>
#include <Python.h>
#include <numpy/arrayobject.h> /* Numpy as seen from C */

struct ngn{...};  /* Structure of model parameter */  

class ngn_rate{   /* Model Class */
    int NEQ;
    ngn pars;
    double *y,*dy;
    public:
        ngn_rate();   // Constructor
        ~ngn_rate();  // Destructor
        PyObject* bifsystem();
}

ngn_rate::ngn_rate(){
    // Set ngn pars default values 
    ...

    // Allocate memory for model variables and RHS of equations
    y = (double*)calloc(NEQ,sizeof(double));
    dy = (double*)calloc(NEQ,sizeof(double));
}

ngn_rate::~ngn_rate{
    free(y);
    free(dy);
}

PyObject* ngn_rate::bifsystem(){
    long int NUMEL = NEQ; // PyArray creation function requires (long int*)
    // Does some operations on y,dy
    ...

    // Build PyObject* from dy
    // Create Python Array Object...
    PyObject* out_array = PyArray_SimpleNew(1,&NUMEL,NPY_DOUBLE);

    // ... and make C pointer to point to it
    double* dy_aux = (double*)((PyArrayObject*)out_array)->data;

    // Copy dy into PyObject out_array
    for(int i=0;i<NUMEL;i++) dy_aux[i] = dy[i];

    return out_array; 
}

As you may guess this class is eventually called from a Python module. With this regard I am using scipy.weave to interface my C code with Python. So the calling Python module looks something like:

def ngn_rate_py():
    support_code = """
                   #include <Python.h>
                   #include "ng_networks.h" 
                   """
    source_files = [...] # A list of C/CPP source file names
    libs = ['m']
    dirs = [...]         # A list of #include dirs
    # My C code to interface with Python
    code = """
       //Initialize Model 
       ngn_rate network();

       //Return dy_dt
       return_val = network.bifsystem();
       """
    vars = []
    dy   = weave.inline(code,
                        vars,
                        support_code = support_code,
                        sources = source_files,
                        libraries = libs,
                        library_dirs = dirs,
                        include_dirs = dirs,
                        runtime_library_dirs = dirs,
                        type_converters = converters.blitz,
                        compiler = 'gcc',
                        extra_compile_args = ['-std=c++11'],
                        force = 1)
    return dy

When I run the above module unfortunately it produces a segmentation fault. After some debugging and trial-and-error I figured out that the problem is caused for some reason by the initialization of PyObject* out_array in ng_networks.h. Indeed when I create the PyObject* in C code in weave it works perfectly: that is I modify the ngn_rate::bifsystem() class member function so that it returns a double* and then I build the PyObject* from the latter within the weave interface:

class ngn_rate{   /* Model Class */
    ...
    public:
        ...
        double* bifsystem();
}

double* ngn_rate::bifsystem(){
    long int NUMEL = NEQ; // PyArray creation function requires (long int*)
    // Does some operations on y,dy
    ...

    return dy;
}

And then in the weave interface:

def ngn_rate_py():
    support_code = """
                   #include <Python.h>
                   #include "ng_networks.h" 
                   """
    code = """
           //Initialize Model 
           ngn_rate network();

           //Create temporary dy  
           double *dy = network.bifsystem();

           //Create PyObject*
           PyObject* out_array = PyArray_SimpleNew(1, &NUMEL, NPY_DOUBLE);
           double* dy_aux = (double*) ((PyArrayObject*) out_array)->data;

           //Assign dy to PyObject            
           for(int i=0;i<NUMEL;i++) dy_aux[i]=dy[i];

           return_val = out_array;

I cannot figure out why the above works whereas I get a segmentation fault if I make PyObject* be returned by my class. Noteworthy is that in some other code I had just a static/non-member C-function that returned a PyObject* that when called from weave worked just fine. So my guess is that there are some issues with PyObject* used within C class object. But I cannot figure out what. And although I can work with the above code creating the PyObject* within the weave interface, for portability I'd rather have it directly provided by my class ngn_rate.

Thanks in advance for your feedback.

M

maurizio
  • 745
  • 1
  • 7
  • 25
  • I am no python expert, but what does this line do exactly: `ngn_rate network();`? If it is supposed to call a function and that function returns ngn_rate by value, then that may explain the seg fault. Your ngn_rate class is not safely copyable as it does not follow "the rule of 3". On the other hand if it is C++ code, then that line does not call a function -- it only declares a function called `network()` that returns an `ngn_rate`. So for the C++ only folks here, please give a brief explanation of that suspicious (to me) line of code. – PaulMcKenzie Apr 24 '15 at 16:13
  • You need to initialize the numpy C API for your code to work, see [this](http://stackoverflow.com/questions/7730717/numpy-c-api-example-gives-a-segfault). – Jaime Apr 24 '15 at 16:41
  • @PaulMcKenzie I am not sure I get your comment: `ngn_rate network()` just initializes an C++ object of `ngn_rate` class type. In practice the class constructor contains some value assignments for default model parameters and the `calloc` for the heap where my variables are going to be stored. So you should look at it just as C++ code. Then `network.bifsystem()` returns the value/array that I want in Python... – maurizio Apr 24 '15 at 17:14
  • @Jaime I tried indeed to include `Py_Initialize()` either in my class constructor or as first line in the body of `ngn_rate::bifsystem()` or within my `code` block in the `weave` interface but it does not solve the issue. – maurizio Apr 24 '15 at 17:17
  • Did you also add the `import_array`? – Jaime Apr 24 '15 at 17:51
  • @UlrichEckhardt `NEQ` is a private variable of the class `ngn_rate`. – maurizio Apr 24 '15 at 18:23
  • @Jaime Thanks. This apparently solved the issue. I am posting and answer for completeness. Yet I have not figure out yet why I need `Py_Initialize()` and `import_array()` if I use `PyObject*` as a return argument from a member function whilst I don't in the case of a static/non-member function. – maurizio Apr 24 '15 at 18:25
  • @maurizio `ngn_rate network();` In C++, this does not call a function, trust me. http://herbsutter.com/2013/05/09/gotw-1-solution/ check item `b)` in that link. – PaulMcKenzie Apr 25 '15 at 01:22
  • @maurizio Also, note you cannot safely copy your `ngn_rate` object from instance to instance. You have member variables that handle dynamically allocated memory, and to perform copies, you need to implement a user defined copy constructor and assignment operator. You can't just get away with a destructor that deletes the memory -- you have to properly handle your resources. See http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three I know this gets away from the Python aspect of your question, just letting you know the holes in the C++ code. – PaulMcKenzie Apr 25 '15 at 01:30
  • @PaulMcKenzie Sincerely thanks for pointing this out! The two links were really enlightening. – maurizio Apr 26 '15 at 04:24

1 Answers1

1

A solution to the segmentation fault is - as suggested by the comments - to make sure to initialize the Python-C API and also the Numpy array. This can be done by the Py_Initialize() and import_array() macros, opportunely included in the constructor of the class ngn_rate, i.e.,

#include <Python.h>
#include <numpy/arrayobject.h>

class ngn_rate{
   ...
   public:
      ngn_rate();
      ...
};

ngn_rate::ngn_rate{
    Py_Initialize();
    import_array();

    ...
}
maurizio
  • 745
  • 1
  • 7
  • 25