Question

I am using templated vistors to define unified interface for several classes. For the purposes of definint the __str__ and __repr__ methods, I would like to programatically obtain class name (the "X" in the referenced documentation) from the boost::python::class object. Is that possible?

It is easy to work it around by passing the class name as argument to the visitor's constructor, but having it automatically would be more elegant.

Was it helpful?

Solution

boost::python::class_ derives from boost::python::object, so it is possible to query the __name__ attribute from the class object.

Here is a simple example:

#include <boost/python.hpp>

class X {};

BOOST_PYTHON_MODULE(example)
{ 
  namespace python = boost::python;
  python::object x_class = python::class_<X>("X");

  // Extract the class' __name__ attribute, printing whatever was passed to
  // class_ constructor.
  std::cout << boost::python::extract<std::string>(x_class.attr("__name__"))()
            << std::endl;
}

And the output:

>>> import example
X

To expand upon this approach, the visitor deriving from boost::python::def_visitor will either need to:

  • Define __str__ and/or __repr__ as a function that generically returns a boost::python::object's class' name via o.__class__.__name__.
  • Extract and store the class name, then define __str__ and/or __repr__ as a function that accesses the stored value.

The following example demonstrates both of these approaches.

#include <boost/python.hpp>

/// @brief Helper type used to set and get a boost::python::class_
///        object's type name.
template <typename ClassT>
class typed_class_name
{
public:
  /// @brief Extract and store the __name__ from a 
  ///        boost::python::class_ objct.
  static void set(const ClassT& c)
  {
    name_ = boost::python::extract<std::string>(c.attr("__name__"));
  }

  /// @brief Return the stored name.
  static std::string get(const typename ClassT::wrapped_type&)
  {
    std::cout << "typed" << std::endl;
    return name_;
  }
private:
  static std::string name_;
};

template <typename ClassT> std::string typed_class_name<ClassT>::name_;

/// @brief Generically get a python object's class name. 
struct generic_class_name
{
  static boost::python::object get(const boost::python::object& self)
  {
    std::cout << "generic" << std::endl;
    return self.attr("__class__").attr("__name__");
  }
};

class my_def_visitor
  : public boost::python::def_visitor<my_def_visitor>
{
  friend class boost::python::def_visitor_access;

  template <class ClassT>
  void visit(ClassT& c) const
  {
    // Store the class name.
    typed_class_name<ClassT>::set(c);

    c 
      .def("__str__",  &typed_class_name<ClassT>::get) // typed
      .def("__repr__", &generic_class_name::get)       // generic
      ;
  }
};

class X {};

BOOST_PYTHON_MODULE(example)
{ 
  namespace python = boost::python;
  python::class_<X>("X")
    .def(my_def_visitor())
    ;
}

And the usage:

>>> import example
>>> x = example.X()
>>> str(x)
typed
'X'
>>> repr(x)
generic
'X'
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top