0

EDIT: This question is resolved. In working out the small example below, everything works as expected. I have left it in case anyone finds it helpful. Obviously, my problem is stemming from elsewhere.

I am using Cython to write a Python interface for a C++ program. The C++ program uses a contiguous array of structs as input. I can create an array or vector of these structs in Cython and pass it to the C++ program. While these containers are supposed to be contiguous, when C++ iterates over them (via incrementing the pointer to the first element), it becomes clear that they are not contiguous. Printing out one of the fields of the structs reveals lots of garbage output.

As an example of the Python interface:

cdef extern from "program.h" namespace "Thing":
    struct Thing:
        int id
        int data


cdef extern from "program.h" namespace "Thing":
    cdef cppclass Program:
        int attribute_1
        int attribute_2
        void Program(int attr1, int attr2) except +
        void Main(Thing* Things)

cdef class PyProgram:
    cdef Program* c_Program
    def __cinit__(self, int attr1, int attr2):
        self.c_Program = new Program (attr1, attr2)

    cpdef void Main(self, list things):
        cdef vector[Thing] Things # This should be contiguous!
        for t in things:
            Things.push_back(self.make_a_thing(t[0], t[1]))
        self.c_Program.Main(&Things[0], len(Things))

   cdef make_a_thing(self, int id, int data):
       cdef Thing c = Thing(id, data)
       return c

Then here is some example C++ code. First "program.h"

#include <vector>
#include <iostream>
namespace Thing{
struct Thing{
    int id;
    int data;
};

class Program{
public:
     int attr1;
     int attr2;
     Program(int attr1, int attr2): attr1(attr1), attr2(attr2){};
     void Main(Thing* Thing, int size);
};
};    

Now "program.cpp"

#include "program.h"
#include <iostream>
using namespace Thing;
using namespace std;
void Program::Main(Thing* Things, int size){
    for (int i=0; i<size; ++i){
         cout << (Things+i)->id << endl;
    }
}
int main(){
    return 0;
}

Now suppose I run the following Python code

MyObject = PyProgram(0, 0)
MyObject.Main([(1, 2), (3, 4), (5, 6)])

This prints 1,3, 5 as expected. So I don't know why this simplified example works but my real program spits out garbage. I guess I'll leave this example here in case anyone else finds it useful, but it seems silly to answer my own question at this point.

batconjurer
  • 101
  • 4

1 Answers1

0

To more accurately reflect my code, here is what not to do.

Suppose that we define Program as follows:

class Program{
public:
    int attr1;
    int attr2;
    Thing* thing;
    Program(int attr1, int attr2, Thing* thing): attr1(attr1), attr2(attr2),      
                                                 thing(thing){};
    void Main(int size);

};

Then in "program.cpp"

void Program::Main(int size){
    for (int i=0; i<size; ++i){
         cout << (Things+i)->id << endl;
    }
}

After making the appropriate changes in the interface, the problem will begin to occur. The proper way to do this is in the toy example above. I made that change and everything worked.

I have put this as an answer, which is a bit backwards since this post contains the mistake and the original post contains the answer. If the community deems this better off as a comment, I will change it.

batconjurer
  • 101
  • 4
  • Do I understand it correctly, you stored the pointer to a temporary object and used it after the object was already deleted? – ead Jul 09 '18 at 13:37
  • I'm not entirely sure, because of the convolution in my code, but that is my best guess. – batconjurer Jul 09 '18 at 13:39