Si puede usar Boost, considerar el uso de Boost.Signals2, que proporciona señales de ranuras/eventos/funcionalidad observadores. Es sencillo y fácil de usar, y es bastante flexible. Boost.Signals2 también le permite registrar objetos invocables arbitrarios (como funtores o funciones miembro enlazadas), por lo que es más flexible y tiene una gran cantidad de funcionalidades para ayudarlo a administrar las vidas útiles de los objetos.
Si está tratando de implementarlo usted mismo, está en el camino correcto. Sin embargo, tiene un problema: ¿qué quiere exactamente hacer con los valores devueltos por cada una de las funciones registradas? Solo puede devolver un valor desde operator()
, por lo que debe decidir si desea devolver nada, o uno de los resultados, o de alguna manera agregar los resultados.
Suponiendo que queremos ignorar los resultados, es bastante sencillo implementar esto, pero es un poco más fácil si toma cada uno de los tipos de parámetro como un parámetro de tipo de plantilla separado (alternativamente, podría usar algo como Boost.TypeTraits, que le permite diseccionar fácilmente un tipo de función):
template <typename TArg0>
class MyEvent
{
typedef void(*FuncPtr)(TArg0);
typedef std::deque<FuncPtr> FuncPtrSeq;
FuncPtrSeq ls;
public:
MyEvent& operator +=(FuncPtr f)
{
ls.push_back(f);
return *this;
}
void operator()(TArg0 x)
{
for (typename FuncPtrSeq::iterator it(ls.begin()); it != ls.end(); ++it)
(*it)(x);
}
};
Esto requiere la función registrada a tener un tipo void
retorno. Para ser capaz de aceptar las funciones con cualquier tipo de retorno, se puede cambiar para ser FuncPtr
typedef std::function<void(TArg0)> FuncPtr;
(o utilizar boost::function
o std::tr1::function
si no tiene la versión de C++ 0x disponible). Si desea hacer algo con los valores devueltos, puede tomar el tipo de devolución como otro parámetro de plantilla al MyEvent
. Eso debería ser relativamente sencillo de hacer.
Con la implementación anterior, lo siguiente debe funcionar:
void test(float) { }
int main()
{
MyEvent<float> e;
e += test;
e(42);
}
Otro enfoque, que le permite soportar diferentes arities de eventos, sería utilizar un único parámetro de tipo para el tipo de función y tiene varias sobrecargas operator()
sobrecargadas, cada una teniendo un número diferente de argumentos. Estas sobrecargas tienen que ser plantillas, de lo contrario obtendrás errores de compilación para cualquier sobrecarga que no coincida con la realidad del evento. Aquí está un ejemplo viable:
template <typename TFunc>
class MyEvent
{
typedef typename std::add_pointer<TFunc>::type FuncPtr;
typedef std::deque<FuncPtr> FuncPtrSeq;
FuncPtrSeq ls;
public:
MyEvent& operator +=(FuncPtr f)
{
ls.push_back(f);
return *this;
}
template <typename TArg0>
void operator()(TArg0 a1)
{
for (typename FuncPtrSeq::iterator it(ls.begin()); it != ls.end(); ++it)
(*it)(a1);
}
template <typename TArg0, typename TArg1>
void operator()(const TArg0& a1, const TArg1& a2)
{
for (typename FuncPtrSeq::iterator it(ls.begin()); it != ls.end(); ++it)
(*it)(a1, a2);
}
};
(He usado std::add_pointer
de C++ 0x aquí, pero este tipo modificador también ser encontrado en Boost y C++ TR1, sino que sólo lo hace un poco más limpio para utilizar la función . plantilla ya que se puede utilizar un tipo de función directamente, que no tiene que utilizar un tipo de puntero de función) Este es un ejemplo de uso:
void test1(float) { }
void test2(float, float) { }
int main()
{
MyEvent<void(float)> e1;
e1 += test1;
e1(42);
MyEvent<void(float, float)> e2;
e2 += test2;
e2(42, 42);
}
Por favor, no sobrecargar 'operador + =' como C# hace, me parece que muy confuso . – fredoverflow
Vea http://www.codeproject.com/KB/cpp/ImpossiblyFastCppDelegate.aspx –
Uno de su problema es que C++ no admite directamente a los delegados, que son una parte central de los eventos de estilo C#. http://www.codeproject.com/KB/cpp/ FastDelegate.aspx Implementa Delegados de una manera no estándar, pero compatible con la mayoría de los compiladores. – CodesInChaos