views:

54

answers:

4

I'm making a Gui API for games. Basically I have event callbacks in my class which are function pointers. I thought of directly letting the user = the function pointer ex:

widget->OnPaintCallback = myPaintFunc;

But I don't like how I cannot check for NULL or do anything else. It also makes my class feel exposed.

I also thought of having a setter for each callback, but that will get messy in the class (I have over 50)

I then thought of a function that asks for a string indicating which event the handler is for, and its function pointer. But that would evolve needlessly referencing documentation to know the string, and even more confusing for custom undocumented widgets.

Is there a better, cleaner alternative?

Thanks

Could casablankca's solution have multiple arguments?

+1  A: 

I would suggest the Boost.Signals library. Something like this:

class Widget
{
public:
    boost::signal<void (Paint &)> onPaint;
    boost::signal<void (MouseMove &)> onMouseMove;
    // ... etc
};

// later...

Widget myWidget;
myWidget.onPaint.connect(myPaintFunc);

// and to fire the event:

void Widget::DoPaint()
{
    Paint data;
    data.whatever = foo;

    onPaint(data);
}

This has several advantages:

  1. You can combine it with boost::bind (or C++0x version of bind if your compiler supports it) to allow you to bind member functions to event handlers.
  2. You can attach multiple handlers to a single event. If you just use function pointers, then only a single function pointer can be assigned at a time.
  3. The signals are strong-typed and flexible. You can have signals which take different types and numbers of parameters, and they'll all be resolved at compile-time.
Dean Harding
Is there a non boost alternative?
Milo
A: 

You can do an interface for each type of event handler you need. Not unlike Java does it. So for example you would have

class PaintCallback {
public:
  virtual void paint() = 0;
};

Your event handler would inherit from the abstract class and implement the paint method. In the widget class you would keep a pointer (or a collection) for each handler.

Aleph
No, I really want event handlers, I don't think a plugin-like design is good for this, I want more like .Net
Milo
A: 

But I don't like how I cannot check for NULL or do anything else

How about making the callback (OnPaintCallback) an object of a class that overloads operator =, that way you can do any additional checking and throw an exception if something goes wrong. You can also overload operator () so that you can call this object as if it were a simple function pointer.

Update: As for variable number of function arguments, there is no general way to do it, but if your maximum N is limited and small, you could use template specializations, for example: (I've omitted constructors, operator = and other details for clarity)

template<typename T, int N>
class Callback {
};

template<typename T>
class Callback<T, 1> {
  T func;

  template<typename A1>
  void operator ()(A1 arg1) {
    func(arg1);
  }
};

template<typename T>
class Callback<T, 2> {
  T func;

  template<typename A1, typename A2>
  void operator ()(A1 arg1, A2 arg2) {
    func(arg1, arg2);
  }
};

I know this is a hacky way to do it but at least your users won't see any of this: they'll get the same interface for all callbacks.

casablanca
@casablanca How do I make it so I can have N arguments?
Milo
@Milo: Do you mean that different callbacks can have different number of arguments?
casablanca
@casablanca Yes, paint might not need any but click might need the location
Milo
@Milo: See my updated answer for a possible solution.
casablanca
Thanks, Ill just use templates and 1 argument
Milo
+1  A: 

I recommend taking a look at the boost.signals library, or libsigc++. These are very general libaries for managing things like event handlers. They do a lot more than what you are trying to do, but they'll give you ideas for what you may want from your design that you haven't thought of yet.

The more you use your callbacks the more you'll realize that you want more of the features in those libraries (like registering multiple callbacks, binding arguments, being more flexible with types, etc.) So even if you end up doing something simpler, it will be helpful to learn from mature designs.

DS