boost python overload operator ()

binding, boost-python, operator-overloading

Solution

When exposing the `Queuer` class, define a `__call__` method for each `Queuer::operator()` member function. Boost.Python will handle the appropriate dispatching based on types. The only complexity is introduced with pointer-to-member-function syntax, as the caller is required to disambiguate `&Queuer::operator()`.

Additionally, when attempting to pass derived classes in Python to a C++ function with a parameter of the Base class, then some additional information needs to be exposed to Boost.Python:

- The base C++ class needs to be exposed with `class_`. For example, `class_<BaseType>("Base")`.

- The derived class needs to explicitly list its base classes when being exposed with `bases_`. For example, `class_<DerivedType, bases<BaseType> >("Derived")`. With this information, Boost.Python can do proper casting while dispatching.

Here is a complete example:

#include <iostream>

#include <boost/python.hpp>

// Mockup classes.
struct AgentBase   {};
struct MessageBase {};
struct QueueBase   {};
struct SpamBase    {};
struct Agent:   AgentBase   {};
struct Message: MessageBase {};
struct Queue:   QueueBase   {};
struct Spam:    SpamBase    {};

// Class with overloaded operator().
class Queuer
{ 
public:

  void operator()(const AgentBase&, const MessageBase&) const
  {
    std::cout << "Queuer::operator() with Agent." << std::endl;
  }

  void operator()(const QueueBase&, const MessageBase&) const
  {
    std::cout << "Queuer::operator() with Queue." << std::endl;
  }

  void operator()(const SpamBase&, const MessageBase&) const
  {
    std::cout << "Queuer::operator() with Spam." << std::endl;
  }
};

/// Depending on the overlaod signatures, helper types may make the
/// code slightly more readable by reducing pointer-to-member-function syntax.
template <typename A1>
struct queuer_overload
{
  typedef void (Queuer::*type)(const A1&, const MessageBase&) const;
  static type get(type fn) { return fn; }
};

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  // Expose only the base class types.  Do not allow the classes to be
  // directly initialized in Python.
  python::class_<AgentBase  >("AgentBase",   python::no_init);
  python::class_<MessageBase>("MessageBase", python::no_init);
  python::class_<QueueBase  >("QueueBase",   python::no_init);
  python::class_<SpamBase   >("SpamBase",    python::no_init);

  // Expose the user types.  These classes inerit from their respective
  // base classes.
  python::class_<Agent,   python::bases<AgentBase>   >("Agent");
  python::class_<Message, python::bases<MessageBase> >("Message");
  python::class_<Queue,   python::bases<QueueBase>   >("Queue");
  python::class_<Spam,    python::bases<SpamBase>    >("Spam");

  // Disambiguate via a varaible.
  queuer_overload<AgentBase>::type queuer_op_agent = &Queuer::operator();

  python::class_<Queuer>("Queuer")
    // Disambiguate via a variable.
    .def("__call__", queuer_op_agent)
    // Disambiguate via a helper type.
    .def("__call__", queuer_overload<QueueBase>::get(&Queuer::operator()))
    // Disambiguate via explicit cast.
    .def("__call__",
         static_cast<void (Queuer::*)(const SpamBase&, 
                                      const MessageBase&) const>(
             &Queuer::operator()))
    ;
}

And its usage:

>>> import example
>>> queuer = example.Queuer()
>>> queuer(example.Agent(), example.Message())
Queuer::operator() with Agent.
>>> queuer(example.Queue(), example.Message())
Queuer::operator() with Queue.
>>> queuer(example.Spam(), example.Message())
Queuer::operator() with Spam.

Problem

I would like to bind the operator() using Boost::Python but I don't really see how to do this. Consider the example: C++: ``` class Queuer { public: void Queuer::operator()(const qfc::Queue & iq, const qfc::Message & im) const; void Queuer::operator()(const qfc::Agent & ia, const qfc::Message & im) const; // some other overloaded operator() methods }; ``` So in a Python script, after importing the module I'm using (called qfc), I would like to do: Python: ``` >>> queuer = qfc.Queuer() // instantiating a Message an Agent and a Queue object >>> queuer(queue,message) >>> queuer(agent,message) >>> ... ``` Would you have any idea on how to do it? maybe with boost::python call<>? Thank you, Kevin

Original source