[C++-sig] Re: got stuck starting with boost.python

David Abrahams dave at boost-consulting.com
Tue Nov 2 16:18:31 CET 2004


Chris Niekel <chris at niekel.net> writes:

> Hi,
>
> I just started with boost.python, and I'm already stuck.
> The C++ classes provide a 'readonly' view of the objects, and the user of
> these classes can't create the objects, they're created and destroyed by some
> friend-class (say, Manager).
>
> So, there's a class Entity 
> {
> protected:
>     Entity();
>     ~Entity();
>
>     std::string getId();
>     ...
> };
>
> class Manager
> {
> public:
>     std::map<std::string, Entity*> getEntityMap();
>     ...
> }
>
> First problem was that the Entity destructor had to be public. 
> So I changed that in the headerfile 
> Q1: is there a better way to fix that?

Use boost::noncopyable when wrapping Entity.

> I've translated the getEntityMap into a getEntities, using a new function
> that creates a boost::python::list and walks through the map, adding the
> elements. That works now.
>
> What I want to do next is:
>
> class_<Entity, Entity*>("Entity", no_init)
>     .def_readonly("id", &Entity::getId)
>     ;
>
> That gives me a weird errors:
> /usr/people1/smf/include/boost-1_31/boost/python/data_members.hpp:285:
> conversion
>    from `boost::is_member_pointer<const
>    std::string&(Entity::*)() 
>       const>' to non-scalar type `boost::mpl::bool_<false>' requested

def_readonly only works on pointers-to-data-members. Try 
                                       ^^^^

    .add_property("id", &Entity::getId)

you may yet need to use a return value policy as in:

    .add_property(
        "id"
      , make_function(
             &Entity::getId
           , return_value_policy<copy_const_reference>()))

> which I don't understand. So working around the problem, I
>     .def("id", &Entity::getId)
> That also gives an error:
> boost/python/detail/invoke.hpp:89: no match 
>    for call to `(const 
>       boost::python::detail::specify_a_return_value_policy_to_wrap_functions_returning<const
>          std::string&>) (const std::basic_string<char,
>          std::char_traits<char>, 
>             std::allocator<char> >&)'
>
> Also beyond my comprehension. 

As the message says, you have to "specify a return value policy to wrap
functions returning const std::string&."  I suggest you use 

  return_value_policy<copy_const_reference>

> If I do:
>     .def("id", &Entity::getId, return_internal_reference<>())
> it compiles, but I get the runtime exception:
> TypeError: No Python class registered for C++ class std::string.

Right.  You're not wrapping std::string with a class_<...> (and you
don't want to) so Boost.Python has no way of creating a python object
that refers to the referenced std::string.

> So I've now created an extra method in the Entity: const char*
> getIdCStr() which returns the id.c_str(). That sorta works.
> I can do in python: for i in getEntities(): print i.id()
>
> With .add_property("id", &Entity::getIdCStr) it works as I want it. 

Why didn't you use add_property in the first place?  I ask because it
speaks to a deficiency in the documentation.

> Q2: This requires me to add methods to the Entity class (there are
> several std::string attributes I need).  Is there a more elegant way
> to solve this?

A free function taking an initial X& parameter always works just as
well as a member function of X.

HTH,

-- 
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com




More information about the Cplusplus-sig mailing list