2

I try to expose two different classes to python, but I don't get it to compile. I tried to follow the boost::python example, which works quite well. But if I try to write the wrapper classes for my classes it doesn't work. I have provided two minimal examples below:

struct Base
{
    virtual ~Base() {}
    virtual std::unique_ptr<std::string> f() = 0;
};

struct BaseWrap : Base, python::wrapper<Base>
{
    std::unique_ptr<std::string> f()
    {
        return this->get_override("f")();
    }
};

and

struct Base
{
    virtual ~Base() {}
    virtual void f() = 0;
};

struct BaseWrap : Base, python::wrapper<Base>
{
    void f()
    {
        return this->get_override("f")();
    }
};

The first one does not compile because of the unique pointer(I think boost::python does not use unique pointers?) and the second example complains about the return statement inside the void function. Can someone help me how to solve this problems?

gariepy
  • 3,576
  • 6
  • 21
  • 34
Ventu
  • 780
  • 1
  • 12
  • 25
  • Possible duplicate: http://stackoverflow.com/questions/20581679/boost-python-how-to-expose-stdunique-ptr – n. m. could be an AI Nov 02 '15 at 12:43
  • I see why this seems to be a dublicate. But I have already red this post. There is nothing about the pure virtual functions what seems to be my problem.. I do not know how to write the wrapper classes. Is there anything about this in the possible dublicate? – Ventu Nov 02 '15 at 12:48
  • Read it again. Your problem is unique_ptr, not virtual functions. – n. m. could be an AI Nov 02 '15 at 13:39
  • If you know how to do it, please help me.. I have tried to use the solution from that post.. I have to write my wrapper and the `get_override` function does not convert a `unique_ptr` so what do I have to pass to the `get_override` function? – Ventu Nov 02 '15 at 13:44
  • You cannot use unique-ptr, I think the linked answer says that quite clearly. Why do you need unique_ptr? Why not just return the string by value? – n. m. could be an AI Nov 02 '15 at 14:44
  • You need get_overrider if you want to inherit your class *in Python*, override the virtual function, and then call the overriden version of your function *from C++*. How would you write a Python function that returns a unique_ptr? You can't. – n. m. could be an AI Nov 02 '15 at 14:48
  • I did not understand that its not possible, thanks for your adivce! Its a string only in my example. I have a interface with returns `uniqe_ptr` I only used std::string to show my problem. – Ventu Nov 02 '15 at 14:59

1 Answers1

2

The examples are failing to compile because:

  • The first example attempts to convert an unspecified type (the return type of override::operator()) to an incompatible type. In particular, Boost.Python does not currently support std::unique_ptr, and hence will not convert to it.
  • The second example attempts to return the unspecified type mentioned above when the calling function declares that it returns void.

From a Python perspective, strings are immutable, and attempting to transferring ownership of a string from Python to C++ violates semantics. However, one could create a copy of a string within C++, and pass ownership of the copied string to C++. For example:

std::unique_ptr<std::string> BaseWrap::f()
{
  // This could throw if the Python method throws or the Python
  // method returns a value that is not convertible to std::string.
  std::string result = this->get_override("f")();

  // Adapt the result to the return type.
  return std::unique_ptr<std::string>(new std::string(result));
}

The object returned from this->get_override("f")() has an unspecified type, but can be used to convert to C++ types. The invocation of the override will throw if Python throws, and the conversion to the C++ type will throw if the object returned from Python is not convertible to the C++ type.


Here is a complete example demonstrating two ways to adapt the returned Python object to a C++ object. As mentioned above, the override conversion can be used. Alternatively, one can use boost::python::extract<>, allowing one to check if the conversion will fail before performing the conversion:

#include <memory> // std::unique_ptr
#include <boost/algorithm/string.hpp> // boost::to_upper_copy
#include <boost/python.hpp>

struct base
{
  virtual ~base() {}
  virtual std::unique_ptr<std::string> perform() = 0;
};

struct base_wrap : base, boost::python::wrapper<base>
{
  std::unique_ptr<std::string> perform()
  {
    namespace python = boost::python;
    // This could throw if the Python method throws or the Python
    // method returns a value that is not convertible to std::string.
    std::string result = this->get_override("perform")();

    // Alternatively, an extract could be used to defer extracting the
    // result.
    python::object method(this->get_override("perform"));
    python::extract<std::string> extractor(method());

    // Check that extractor contains a std::string without throwing.
    assert(extractor.check());

    // extractor() would throw if it did not contain a std::string.
    assert(result == extractor());

    // Adapt the result to the return type.
    return std::unique_ptr<std::string>(new std::string(result));
  }
};

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<base_wrap, boost::noncopyable>("Base", python::init<>())
    .def("perform", python::pure_virtual(&base::perform))
    ;

  python::def("make_upper", +[](base* object) {
    auto result = object->perform(); // Force dispatch through base_wrap.
    assert(result);
    return boost::to_upper_copy(*result);
  });
}

Interactive usage:

>>> import example
>>> class Derived(example.Base):
...     def perform(self):
...         return "abc"
...        
>>> derived = Derived()
>>> assert("ABC" == example.make_upper(derived))
Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169