21

I have started to play around with boost python a bit and ran into a problem. I tried to expose a C++ class to python which posed no problems. But I can't seem to manage to implement the __str__ functionality for the class without getting build errors I don't understand.

I'm using boost 1_42 prebuild by boostpro. I build the library using cmake and the vs2010 compiler.

I have a very simple setup. The header-file (tutorial.h) looks like the following:

#include <iostream>
namespace TestBoostPython{
    class TestClass {
        private:
            double m_x;
        public:
            TestClass(double x);
            double Get_x() const;
            void Set_x(double x);
    };
    std::ostream &operator<<(std::ostream &ostr, const TestClass &ts);
};

and the corresponding cpp-file looks like:

#include <boost/python.hpp>
#include "tutorial.h"

using namespace TestBoostPython;

TestClass::TestClass(double x)
{
    m_x = x;
}

double TestClass::Get_x() const
{
    return m_x;
}
void TestClass::Set_x(double x)
{
    m_x = x;
}

std::ostream &operator<<(std::ostream &ostr, const TestClass &ts)
{
    ostr << ts.Get_x() << "\n";
    return ostr;
}

BOOST_PYTHON_MODULE(testme)
{
using namespace boost::python;
class_<TestClass>("TestClass", init<double>())
    .add_property("x", &TestClass::Get_x, &TestClass::Set_x)
    .def(str(self))
    ;
}

The CMakeLists.txt looks like the following:

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

project (testme)

FIND_PACKAGE( Boost REQUIRED )
FIND_PACKAGE( Boost COMPONENTS python REQUIRED )
FIND_PACKAGE( PythonLibs REQUIRED )

set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREAD ON)

INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
INCLUDE_DIRECTORIES ( ${PYTHON_INCLUDE_PATH} )

add_library(testme SHARED tutorial.cpp)
target_link_libraries(testme ${Boost_PYTHON_LIBRARY})
target_link_libraries(testme ${PYTHON_LIBRARY}

The build error I get is the following:

Compiling...
tutorial.cpp
    C:\Program Files (x86)\boost\boost_1_42\boost/python/def_visitor.hpp(31) : error C2780: 'void boost::python::api::object_operators::visit(ClassT &,const char *,const boost::python::detail::def_helper &) const' : expects 3 arguments - 1 provided
    with
    [
        U=boost::python::api::object
    ]
    C:\Program Files (x86)\boost\boost_1_42\boost/python/object_core.hpp(203) : see declaration of 'boost::python::api::object_operators::visit'
    with
    [
        U=boost::python::api::object
    ]
    C:\Program Files (x86)\boost\boost_1_42\boost/python/def_visitor.hpp(67) : see reference to function template instantiation 'void boost::python::def_visitor_access::visit,classT>(const V &,classT &)' being compiled
    with
    [
        DerivedVisitor=boost::python::api::object,
        classT=boost::python::class_,
        V=boost::python::def_visitor
    ]
    C:\Program Files (x86)\boost\boost_1_42\boost/python/class.hpp(225) : see reference to function template instantiation 'void boost::python::def_visitor::visit>(classT &) const' being compiled
    with
    [
        DerivedVisitor=boost::python::api::object,
        W=TestBoostPython::TestClass,
        classT=boost::python::class_
    ]
    .\tutorial.cpp(29) : see reference to function template instantiation 'boost::python::class_ &boost::python::class_::def(const boost::python::def_visitor &)' being compiled
    with
    [
        W=TestBoostPython::TestClass,
        U=boost::python::api::object,
        DerivedVisitor=boost::python::api::object
    ]

Does anyone have any idea on what went wrrong? If I remove the .def(str(self)) part from the wrapper code, everything compiles fine and the class is usable from python. I'd be very greatful for assistance.

Thank you, Rickard

edit: forgot a const

Rickard
  • 1,754
  • 1
  • 12
  • 17
  • Nice! I didn't look carefully initially and defined the operator<< in the class itself rather than into the namespace like you did, which made all the difference in getting it to work to call a print(myObj) in Python. Nice work Rickard. However I noticed I still need to explicitly call print to get my object to render as I wish in python. If I just enter my object it returns results as if I called type(myObj), that may be default behavior for Python, maybe I'm getting mixed up with experience in R or Matlab though. – jxramos Nov 06 '15 at 03:28

2 Answers2

34

I recently encountered this problem; The solution that worked was to explicitly resolve the str and self on this line:

.def(str(self))

So that it becomes:

.def(self_ns::str(self_ns::self))

I don't know why this is necessary, (knowing something of the overload-resolution complication that goes on in boost python, it's probably something there...) but it worked for me :)

James
  • 24,676
  • 13
  • 84
  • 130
  • 2
    Wonderful! Thanks alot. I love boost::python but when stuff goes haywire like this I think it might be easier to just hand-wrap the object in Python C-API. I would never have found this solution on my own. Thanks again. – Rickard Jun 22 '10 at 06:23
  • This still worked for me today. I wonder if this should not have been fixed long time ago...? – varantir Nov 11 '15 at 10:34
  • 4
    There is a boost::python::str, and a boost::python::self_ns::str, which are very different things. This is necessary because if your code has `using boost::python` (as many probably will be), the compiler will find the former, which mightily confuses it. – stochastic Jun 22 '16 at 16:09
  • The library isolates self_t in its own namespace, self_ns, in order to prevent the generalized operator templates which operate on it from being found by argument-dependent lookup in other contexts. This should be considered an implementation detail, since users should never have to mention self_t directly. https://www.boost.org/doc/libs/1_72_0/libs/python/doc/html/reference/high_level_components/boost_python_operators_hpp.html#high_level_components.boost_python_operators_hpp.class_self_ns_self_t – Pato Sandaña Jan 08 '20 at 11:11
  • boost::python::str is a string object while self_ns::str is a function definition. Also, they intended to have this as an implementation detail as explained above. – Pato Sandaña Jan 08 '20 at 11:15
11

I ran into the same thing. Adding this line (instead of qualifying str and self) also works:

using self_ns::str;
AndyJost
  • 1,085
  • 1
  • 10
  • 18