D-BUS C++: method marshalling objects

Murray Cumming murrayc at murrayc.com
Fri Oct 14 01:17:30 PDT 2005


> Murray Cumming, Paul Durante, and others who might be interested:
>
> After studying Paul's "cbus" code I have made numerous improvements to
> my own C++ wrapper for D-BUS. It is still very much a work in progress
> but I have implemented an IMHO quite neat feature that you might know
> how to make even better.
>
> The template juggling is partly inspired by libsigc++ so Murray will
> probably have ideas for improvement.

Well done. I chose to delay thinking about this particular problem. It's
something that's probably much easier in dynamic languages like python.

How about actually depending on libsigc++? It's not a controversial
dependency. I think we have a base class that could be used in the API as
a generic method/callback parameter.

If necessary, we can think about API additions to libsigc++.

> About my design:
>
> I have one abstract base class for each interface I expose via D-BUS,
> for example "Device". I have an implementation of this interface, for
> example "DeviceImpl".
>
> For each interface I expose via D-BUS I have a class (e.g.
> "DeviceHandler") to handle method calls made to this interface for any
> object.
>
> In order to make this letter as short as possible I have left out a lot,
> maybe too much. If you want to know more, just ask.

I'd like to see what the macros do. Could you put a tarball or cvs
repository online somewhere?

> Improvements:
>
> Previously I had a "HandleMessage" method in the handler class (for
> example "DeviceHandler") that examined the member property of the
> incoming message and called a method to marshall that method.
>
> After looking at Paul's code I pulled myself together and replaced the
> "HandleMessage" method with code to register the functions that
> marshalled the individual D-BUS method calls.
>
> However, I wasn't satisfied. The marshalling functions always looked the
> same:
>
> 1. Decode parameters from incoming message
> 2. Call the corresponding method on the implementation object (eg. a
> "DeviceImpl" object) with those parameters
> 3. Create reply message
> 4. Write return value (if any) to reply message
>
> So I asked myself: how can I avoid writing that code again and again?
>
> I had this function to register a method handler:
>
>  template<class U>
>    void RegisterMethod(
>      const std::string& name,
>      Message_ptr (U::*handler)(Object_ptr, Message_ptr))
>
> But I wanted something like this:
>
>   void RegisterMethod(const std::string& name, method)
>
> After some thinking and looking at libsigc++ I came up with multiple
> functions like this, where T is the interface (e.g. "Device"):
>
>  template <class T_return>
>    void RegisterMethod(const std::string& name, T_return (T::*handler)())
>
>  template <class T_return, class T_arg1>
>    void RegisterMethod(const std::string& name, T_return
> (T::*handler)(T_arg1))
>
> These functions create something I call (in lack of better knowledge of
> the English language) "method marshalling objects", for example:
>
>     template <class T_object, class T_return,
>              class T_arg1>
>       class Method1 : public MethodBase<T_object>
>       {
>         public:
>           typedef boost::shared_ptr<T_object> Object_ptr;
>           typedef T_return (T_object::*HandlerType)(
>               T_arg1);
>
>         private:
>           HandlerType mHandler;
>
>         public:
>           Method1(
>               const std::string& name,
>               HandlerType handler) :
>             MethodBase<T_object>(name),
>             mHandler(handler)
>           {
>           }
>
>           virtual ~Method1()
>           {}
>
>           virtual Message_ptr HandleMessage(
>               Object_ptr object,
>               Message_ptr message)
>           {
>             T_arg1 arg1;
>
>             *message >> arg1;
>
>             Message_ptr reply(
>                 Message::NewMethodReturn(message) );
>
>             *reply << (*object.*mHandler) (arg1);
>
>             return reply;
>           }
>       };
>
>
> And voila! As long as I have a RegisterMethod function and a method
> marshalling object for the right number of parameters, it's very simple
> to add or remove methods on my D-BUS objects. Example from the
> "DeviceHandler" constructor:
>
> #define REGISTER_METHOD(method) RegisterMethod(#method, &Device::method)
>   REGISTER_METHOD(Disconnect);
>   REGISTER_METHOD(GetAddress);
>   REGISTER_METHOD(GetName);
>   REGISTER_METHOD(GetProperties);
>   REGISTER_METHOD(GetState);
> #undef REGISTER_METHOD
>
>
> One obstacle was void methods, so I have special RegisterMethod
> functions and a method marshalling objects for those.
>
> Another obstacle was that some of my interfaces passed parameters by
> reference, for example const std::string&. This obviously does not work
> with the implementation of my method marshalling objects seen above, so
> I changed my interfaces to pass the strings by value instead.
>
> The method marshalling objects would probably benefit from being
> generated (like for example signal.h in libsigc++) but that's a future
> improvement.
>
> Is this any good or am I wasting my time?
>
> --
> Regards,
>                -\- David Eriksson -/-


Murray Cumming
murrayc at murrayc.com
www.murrayc.com
www.openismus.com



More information about the dbus mailing list