Recently, I've added event handling, mapping keycodes or mouse button ids to delegates. The first pass is usable, but it can be improved a good deal. D's delegates are great for this sort of thing. But delegates aren't what I want to talk about today.
Until now, my test app has only been using the keyboard. With the inclusion of the event handler, I added mouse support. While testing it out, I kept getting a puzzling result. Allegro has no constants for mouse buttons. They are numbered from 1 to whatever the maximum number is (which you can find out via al_get_mouse_num_buttons()). But my event handler kept getting button 0 passed to it, no matter which button I pushed. al_get_mouse_num_buttons was correctly reporting 3 (I dumped my 8-button gaming mouse some time ago). After several minutes of digging around, I started to suspect my DerelictAllegro binding. Sure enough, I had left out 4 fields from the struct declaration. I patched that up and all is well.
Looking at the binding source reminded me of a D feature that would be worth mentioning here. In my last post, I briefly mentioned D's string mixins. There is another type of useful mixin in D that I've made use of in the Allegro binding: the template mixin.
Allegro's event structs are all declared with the following macro at the top:
#define _AL_EVENT_HEADER(srctype) \
ALLEGRO_EVENT_TYPE type; \
srctype *source; \
double timestamp;
So the mouse event structure looks like this:
typedef struct ALLEGRO_MOUSE_EVENT
{
_AL_EVENT_HEADER(struct ALLEGRO_MOUSE)
struct ALLEGRO_DISPLAY *display;
int x, y, z, w;
int dx, dy, dz, dw;
unsigned int button;
float pressure;
} ALLEGRO_MOUSE_EVENT;
D has no #defines, and no preprocessor at all. But this C idiom can easily be replicated with template mixins. Here's what I did in the DerelictAllegro binding:
// First, declare the template that you intend to use as a mixin.
template _AL_EVENT_HEADER(T)
{
ALLEGRO_EVENT_TYPE type;
T* source;
double timestamp;
}
// Then, mix it in.
struct ALLEGRO_MOUSE_EVENT
{
mixin _AL_EVENT_HEADER!(ALLEGRO_MOUSE);
ALLEGRO_DISPLAY* display;
int x, y, z, w;
int dx, dy, dz, dw;
uint button;
float pressure;
}
Simple. String mixins could also be used here.
// You would want to use a compile time function with a signature something like this
string allegroEventHeader(string type) { ... }
// Then call it like so:
mixin(allegroEventHeader("ALLEGRO_MOUSE"));
Personally, I prefer template mixins in situations like this.
Take a look at the template mixin documentation over at the shiny, new d-programming-language.org website for more info.