jeudi 22 août 2019

Why does returning a boost::shared_ptr to Python side generate a new object?

I am writing a Python interface for a C++ library. One of the methods needs to return a pointer to an object, so the Boost::Python module returns a boost::shared_ptr. My understanding was that this allows Python to keep track of the object without making a duplicate (copy) of it. However, something very strange is happening which is illustrated by the code below.

class base_object() {
  void some_common_functionality() const { /* ... */ }
};

class result : public base_object {
public:
  result(double val) { /* ... */ std::cout << "double ctor" << std::endl; }
  result(array arr) { /* ... */ std::cout << "array ctor" << std::endl; }

  virtual ~result() { std::cout << "dtor" << std::endl; }

  void some_specific_functionality() const { /* ... */ }

private:
  result() {}
};

class generator : public base_object {
public:
   result* get() { /* ... */ return new result((double)1.0); }
};


boost::shared_ptr<result> __result_dbl_ctor(double val) {
  return boost::shared_ptr<result>(new result(val));
}

boost::shared_ptr<result> __result_arr_ctor(array arr) {
  return boost::shared_ptr<result>(new result(arr));
}

BOOST_PYTHON_MODULE(pymodule) {

class_<base_object, boost::shared_ptr<base_object>, boost::noncopyable>(
      "base_object", "an object", no_init)
   .def("some_common_functionality", &base_object::some_common_functionality);


class_<result, bases<base_object>, boost::shared_ptr<result>, boost::noncopyable>("result", no_init)
   .def("__init__", make_constructor(&__result_dbl_ctor))
   .def("__init__", make_constructor(&__result_arr_ctor))
   .def("some_specific_functionality", &result::some_specific_functionality);

class_<generator, bases<base_object>, boost::shared_ptr<generator>, boost::noncopyable>("generator", no_init)
   .def("get", +[](generator& self) { return boost::shared_ptr<result>(self.get()); });

g = generator();
r = g.get();

and I see the following output

double ctor
array ctor
dtor

and importantly, the value for the result (set to 1.0, in this example) which is handled in the constructor for result (confirmed with std::cout on the C++ side), is not correct when accessed on the Python side, indicating that somehow a new object copy is created - also indicated by the second ctor output - and using the other constructor for reasons I can't determine.

It's very strange. I have used almost identical code in the past and it worked as expected.

Any ideas where to begin? Am I using the boost::shared_ptr so incorrectly that I would expect basically random results?

Aucun commentaire:

Enregistrer un commentaire