0

I am having problems passing back an <std::vector <std::vector <boost::shared_ptr Detection>>> and I was hoping someone could help me out. I am able to pass the vector of vector but when I try and access the Detection, I get a TypeError that there is no python class registered for C++ class boost::shared_ptr which is in the Module definition.

My C++ code is as follows:

class Detection {
public: 
    Detection () {};
    ~Detection() {};
    
    double getR () {return r_;};
    double getA () {return a_;};

    double r_;
    double a_;
}

class Detector {
public
    std::vector <std::vector <boost::shared_ptr <Detection> > > getDetections();
}

std::vector <std::vector <boost::shared_ptr <Detection> > > 
Detector::getDetections () {

    std::vector <std::vector <boost::shared_ptr <Detection> > > allVecDetections;

    // Build 4 vectors containing 2 detections each
    for (unsigned int i=0; i < 4; i++) {
        std::vector<boost::shared_ptr<Detection> > vecDetections;
        for (unsigned int j = 0; j < 2; j++ ) {
            boost::shared_ptr<Detection> dt (new Detection (j+i, ts));
            dt->r_ = (j+i) + (j*i);
            dt->a_ = (j+i) * (j*i);
            vecDetections.push_back (dt);
        }
        allVecDetections.push_back (vecDetections);
    }
    return allVecDetections;
}
// Module.cpp
BOOST_PYTHON_MODULE (myModule) {
  
   boost::python::class_<Detector>("Detector")
      .def("getDetections",  &getDetections)
    ;

    boost::python::class_<Detection>("Detection")
      .def("getA",  &getA)
      .def("getR",   &getR)
     ;
  
    // Map list of Detections 
    boost::python::class_<std::vector <boost::shared_ptr <Detection> > > ("DetectionVector")
        .def(vector_indexing_suite<std::vector <boost::shared_ptr <Detection> > > ());

    // Map list of lists of Detections 
    boost::python::class_<std::vector <std::vector <boost::shared_ptr <Detection> > > > ("DetectionVectorVector")
        .def(vector_indexing_suite<std::vector<std::vector <boost::shared_ptr <Detection> > > > ());

    // Register our shared pointers with Python
    register_ptr_to_python<boost::shared_ptr<Detection> >(); 
  
}

I am able to call the getDetections from Python and get back 4 vectors with each one containing 2 Detections. Here is the Python code:

import myModule
...
plots = det.getDetections()
numvectors = len(plots)
print (f'Received {numvectors} vectors ')
jdx = 0
pdx = 0
while jdx < numvectors:
    detections = plots[jdx]
    pdx = 0
    numPlots = len(detections)
    print (f'Received {numPlots} extractions')
    while pdx < numPlots:
        print ("Extraction " + str (pdx) + ":")
        detect = detections[jdx]

This runs but I get the following output:

Received 4 vectors  
Received 2 extractions  
Extraction 0:  
Traceback (most recent call last):  
  File "scripts/test.py", line 87, in <module>
    foo = detections[jdx].getA()  
TypeError: No Python class registered for C++ class boost::shared_ptr<Detection>  

So, why am I getting complaints about Python having no registered class for boost::shared_ptr?

Thanks for any help you can give me. (The above code has been trimmed so typos may have been introduced during the writing of the question).

Taking Valeca's suggestion, I re did the work with passing back a vector<vector> (eliminating the boost::shared_ptr). Now something is unhappy with the module as I seg fault when importing the module in the Pyhton script.

Here are the new changes:

class Detection {
public: 
    Detection () {};
    ~Detection() {};

    Detection (const Detection &D) {
      : r_ (D.r_),
        a_ (D.a_)
    {};

    void operator = (const Detection &D) {
        r_ = D.r_;
        a_ = D.a_;
    };

    double getR () {return r_;};
    double getA () {return a_;};

    double r_;
    double a_;

    friend bool operator== (const Detection &d1, const Detection &d2);
    friend bool operator!= (const Detection &d1, const Detection &d2);
};

bool operator== (const Detection &d1, const Detection &d2) {
    return (d1.r_ == d2.r_ && d1.a_ == d2.a_);
}

bool operator!= (const Detection &d1, const Detection &d2) {
    return !(d1.r_ == d2.r_ && d1.a_ == d2.a_ );
}

class Detector {
public
    std::vector <std::vector <Detection> > getDetections();
}

std::vector <std::vector <Detection> > 
Detector::getDetections () {

    std::vector <std::vector <Detection> > allVecDetections;

    // Build 4 vectors containing 2 detections each
    for (unsigned int i=0; i < 4; i++) {
        std::vector<Detection> vecDetections;
        for (unsigned int j = 0; j < 2; j++ ) {
            Detection dt (j+i, ts);
            dt.r_ = (j+i) + (j*i);
            dt.a_ = (j+i) * (j*i);
            vecDetections.push_back (dt);
        }
        allVecDetections.push_back (vecDetections);
    }
    return allVecDetections;
}

// Module.cpp
BOOST_PYTHON_MODULE (myModule) {

   boost::python::class_<Detector>("Detector")
      .def("getDetections",  &getDetections)
    ;

    boost::python::class_<Detection>("Detection")
      .def("getA",  &getA)
      .def("getR",   &getR)
     ;

    // Map list of Detections 
    boost::python::class_<std::vector <Detection> > ("DetectionVector")
        .def(vector_indexing_suite<std::vector <Detection> > ());

    // Map list of lists of Detections 
    boost::python::class_<std::vector <std::vector<Detection> > > ("DetectionVectorVector")
        .def(vector_indexing_suite<std::vector<std::vector <Detection> > > ());

    // Register our shared pointers with Python
    // register_ptr_to_python<boost::shared_ptr<Detection> >(); 

}

And now the Python side seg faults on start up. I may have added more overloaded operators than needed but without them, I would see:

*/usr/include/c++/7/bits/predefined_ops.h:241:17: error: no match for ‘operator==’ (operand types are ‘Detection’ and ‘const Detection’) { return __it == _M_value; }

when compiling my module.

Thanks for any light you can shed on this.

Natasha
  • 1
  • 1
  • I don’t see the reason for using shared pointers. Could you explain why you use them? Did you try to define the vector as std::vector vecDetections and pushing back objects? Using vector as items for vector itself better be avoided. – Valeca Mar 19 '21 at 14:52
  • Per your suggestion, I also tried it vector> which also then required the implementation of the ==, != = operators on the Detection class (complaints at compile time in Module.cpp without it). When I run now, I seg fault before loading the module which generally means that Python doesn't know how to instantiate my vector. So I must be missing some required overloaded methods in order for Python to know what to do upon receipt. That was one of the reasons I started using the shared_ptr to begin with. – Natasha Mar 21 '21 at 17:54
  • There was in some way similar question to what you initially faced using shared_ptr https://stackoverflow.com/questions/5055443/boost-python-how-to-pass-a-c-class-instance-to-a-python-class. – Valeca Mar 21 '21 at 20:39
  • I suppose it is. I am able to pass back objects from C++ to Python using boost::shared_ptr and have it work as expected. I am also able to pass back a vector>> and have that work as expected. I'm not sure why the boost::shared_ptr or the Detection class is having issues - the only thing I can think of is that I am missing a kep mapping in the module definition. – Natasha Mar 21 '21 at 20:53

1 Answers1

0

I got it working with vector< vector <boost::shared_ptr > > It turns out I was missing the NoProxy override when I registered my vector. See fixed code in Module.cpp.

    boost::python::class_<std::vector <boost::shared_ptr <Detection> > > ("DetectionVector")
        .def(vector_indexing_suite<std::vector <boost::shared_ptr <Detection> >,true > ());

    // Map list of lists of Detections 
    boost::python::class_<std::vector <std::vector <boost::shared_ptr <Detection> > > > ("DetectionVectorVector")
        .def(vector_indexing_suite<std::vector<std::vector <boost::shared_ptr <Detection> > >, true > ());


I remember seeing a Stack Overflow message about this in which the author said that the true really matters. It really does. Thanks to all who helped with suggestions.

Natasha
  • 1
  • 1