Boost logo

Boost Users :

Subject: Re: [Boost-users] a quick question regarding extending a class with boost::function
From: Steven Watanabe (watanabesj_at_[hidden])
Date: 2011-04-06 13:58:20


AMDG

On 04/06/2011 09:22 AM, Littlefield, Tyler wrote:
> Hello all:
> I am working on a mud engine (essentially a game engine), and I had a
> quick question. I have an event class (yes, I know about boost::signal,
> but I don't want to go back and it doesn't support delayed events). I
> would like to set up my event class so that it will take a function
> object as it's callback, which allows someone extending the engine to
> add their own content to use global functions, member functions,
> lambdas, etc etc.
>
> Now, here is my problem, and this may be more of a c++ question than a
> boost question; I'm not really sure.
>
> Right now, every time an event gets called, I pass in the object that
> called it, as well as an EventArgs pointer. So an event callback
> function might look like this:
> void OnMouseDown(Object* obj, OnMouseDownArgs* args);
> Now, it might be useful to have the x and y coordenates where the
> OnMouseDown event was called, which means I end up doing something like:
> class OnMouseDownArgs:public EventArgs
> {
> public:
> int x, y;
> };
> which essentially means that for every new type of event, I have to
> declare an EventArgs class for that.
> So here was my idea; I thought it would be kind of cool to do something
> like:
> Event* OnMouseDown<int, int> = new Event<int, int>();
> which leads me to a question. given that template, how would I define a
> function object with those arguments? better yet, how would I accept an
> arbitrary number of arguments to the template, then take those arguments
> and create a boost::function object with them, as well as extend the
> .Call method to take the arguments I passed in?
> So in summary, I am trying to extend the boost::function object to
> accept a set of arguments passed in through a template when the Event
> object was created, as well as extend the Call method on the Event
> object to take the two ints as in the MouseDownEvent as arguments.
>

So, first of all, I assume that you can't make
the Event handler a template? So, what you'd
like to be able to do is something like this:

EventHandler handler;
Object* o;

struct f {
     void operator()(Object* caller, int x, int y) const;
};

handler.set<int, int>(f());
handler.call(o, 1, 2);

Am I understanding you correctly?

It's impossible to make this perfectly type-safe
at compile time, since you can't tie the call
directly to the function object. However, it can
be implemented with a runtime type check:
(warning untested code)

template<class T, class F>
struct function_adapter
{
     void operator()(Object* o, const boost::any& arg)
     {
         return f(o, boost::any_cast<T>(arg));
     }
     F f;
};

class EventHandler
{
public:
     template<class T, class F>
     void set(F f)
     {
         function_adapter<T, F> impl = { f };
         f_ = impl;
     }
     template<class T>
     void call(Object* o, const T& arg)
     {
         f_(o, arg);
     }
private:
     boost::function<void(Object*, const boost::any&)> f_;
};

This solution only allows a single argument, but
we can combine multiple arguments into one fairly
easily:

EventHandler handler;
handler.set<boost::tuple<int, int> >(boost::fusion::make_fused(f()));
handler.call(o, boost::make_tuple(1, 2));

In Christ,
Steven Watanabe


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net