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?

7
  • Possible duplicate: stackoverflow.com/questions/20581679/… Commented Nov 2, 2015 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? Commented Nov 2, 2015 at 12:48
  • Read it again. Your problem is unique_ptr, not virtual functions. Commented Nov 2, 2015 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? Commented Nov 2, 2015 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? Commented Nov 2, 2015 at 14:44

1 Answer 1

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))
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.