Da li vam se nekad desilo da imate takav slucaj, da vam trebaju klase (koje trenutno ne znate da li će postojati) koje prate neki šablon, ali ih je nemoguće izraziti template-ovima?
Daću primer koji sam ja imao večeras. Imam apstraktne listener klase :
Kod:
struct mouse_listener
{
virtual void mouse_down (const SDL_MouseButtonEvent &e, Uint32 time, Uint32 rtime) = 0;
virtual void mouse_up (const SDL_MouseButtonEvent &e, Uint32 time, Uint32 rtime) = 0;
};
struct mouse_motion_listener
{
virtual void mouse_move (const SDL_MouseMotionEvent &e, Uint32 time, Uint32 rtime) = 0;
virtual void mouse_drag (const SDL_MouseMotionEvent &e, Uint32 time, Uint32 rtime) = 0;
};
E, sad, za svaki od ovih listenera, hocu da napravim emitor klasu, koja ce biti izvedena iz date listener klase, i treba da se ponasa slicno kao event u C# : dodajem listenere eventu, pa kad pozovem neki njegov metod, on automatski pozove taj metod za svakog listenera. Na primer :
Kod:
class mouse_event_emitter : public mouse_listener
{
mouse_listener_t listeners;
public:
void add_mouse_listener( mouse_listener *_listener )
{
listeners.push_back(_listener);
}
void remove_mouse_listener( mouse_listener *_listener )
{
mouse_listener_t::iterator iter = std::find(listeners.begin(), listeners.end(), _listener);
if( iter != listeners.end() )
listeners.erase( iter );
}
void mouse_down (const SDL_MouseButtonEvent &e, Uint32 now, Uint32 rnow)
{
for( mouse_listener_t::iterator iter = listeners.begin(); iter != listeners.end(); ++iter )
(*iter)->mouse_down( e, now, rnow );
}
void mouse_up (const SDL_MouseButtonEvent &e, Uint32 now, Uint32 rnow)
{
for( mouse_listener_t::iterator iter = listeners.begin(); iter != listeners.end(); ++iter )
(*iter)->mouse_up( e, now, rnow );
}
};
Prilično je jasno da je ovo naporan posao, treba napisati event emitor za svaki event listener, i funkciju za svaki event (dosta kopiranja koda, podložno greškama). Da bih rešio ovaj problem, napisao sam par makroa, pomocu kojih sam definisao "jezik" za pravljenje ovih emitor klasa. Na taj način sam olakšao posao (ima tu dosta listener klasa, nisam napisao sve, naravno). Evo makroa :
Kod:
#define OPEN_CLASS(name)\
class name##_event_emitter : public name##_listener\
{ name##_listener_t listeners;\
public:\
void add_##name##_listener( name##_listener *_listener )\
{\
listeners.push_back(_listener);\
}\
void remove_##name##_listener( name##_listener *_listener )\
{\
name##_listener_t::iterator iter = std::find(listeners.begin(), listeners.end(), _listener);\
if( iter != listeners.end() )\
listeners.erase( iter );\
}
#define CLOSE_CLASS\
};
#define EVENT(name, eventname, SDLEVENT)\
void eventname (const SDLEVENT &e, Uint32 now, Uint32 rnow)\
{\
for( name##_listener_t::iterator iter = listeners.begin(); iter != listeners.end(); ++iter )\
(*iter)->eventname( e, now, rnow );\
}
Sada, sve što treba da uradim da bih definisao emitor klasu za bilo koju listener klasu je (na primeru par emitora koji bi inace bili katastrofa posao):
Kod:
OPEN_CLASS(mouse)
EVENT(mouse, mouse_down, SDL_MouseButtonEvent)
EVENT(mouse, mouse_up, SDL_MouseButtonEvent)
CLOSE_CLASS
OPEN_CLASS(mouse_motion)
EVENT(mouse_motion, mouse_move, SDL_MouseMotionEvent)
EVENT(mouse_motion, mouse_drag, SDL_MouseMotionEvent)
CLOSE_CLASS
OPEN_CLASS(key)
EVENT(key, key_down, SDL_KeyboardEvent)
EVENT(key, key_up, SDL_KeyboardEvent)
EVENT(key, key_press, SDL_KeyboardEvent)
CLOSE_CLASS
OPEN_CLASS(update)
EVENT_NOCLASS(update, update)
CLOSE_CLASS
//I kompajler ce resiti ostalo :)
Jeste li vi nekad imali sličan problem? Ako jeste, kako ste ga rešili? Ako imate, dajte neki interesantan primer korisnog korišćenja C/C++ makroa.