Если вы можете использовать Boost, рассмотрите возможность использования Boost.Signals2 , который обеспечивает функциональность сигналов-слотов / событий / наблюдателей.Это простой и удобный в использовании и довольно гибкий.Boost.Signals2 также позволяет регистрировать произвольно вызываемые объекты (например, функторы или функции связанного члена), что делает его более гибким и обладает множеством функций, помогающих правильно управлять временем жизни объектов.
ЕслиВы пытаетесь реализовать это самостоятельно, вы на правильном пути.Однако у вас есть проблема: что именно вы хотите делать со значениями, возвращаемыми каждой из зарегистрированных функций?Вы можете вернуть только одно значение из operator()
, поэтому вам нужно решить, хотите ли вы ничего не возвращать, или один из результатов, или каким-либо образом агрегировать результаты.
Предполагая, что мы хотим игнорировать результаты, этореализовать это довольно просто, но немного проще, если вы берете каждый из типов параметров в качестве отдельного параметра типа шаблона (в качестве альтернативы вы можете использовать что-то вроде Boost.TypeTraits, что позволяет легко разбирать тип функции):
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);
}
};
Для этого требуется, чтобы зарегистрированная функция имела тип возврата void
.Чтобы иметь возможность принимать функции с любым типом возврата, вы можете изменить FuncPtr
на
typedef std::function<void(TArg0)> FuncPtr;
(или использовать boost::function
или std::tr1::function
, если у вас нет версии C ++ 0xимеется в наличии).Если вы хотите что-то сделать с возвращаемыми значениями, вы можете взять возвращаемый тип в качестве другого параметра шаблона для MyEvent
.Это должно быть относительно просто сделать.
С вышеупомянутой реализацией должно работать следующее:
void test(float) { }
int main()
{
MyEvent<float> e;
e += test;
e(42);
}
Другой подход, который позволяет вам поддерживать различные наборы событий, будетиспользовать один тип параметра для типа функции и иметь несколько перегруженных operator()
перегрузок, каждая из которых принимает различное количество аргументов.Эти перегрузки должны быть шаблонами, в противном случае вы получите ошибки компиляции для любой перегрузки, не соответствующей фактической арности события.Вот работающий пример:
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);
}
};
(я использовал здесь std::add_pointer
из C ++ 0x, но этот модификатор типа также можно найти в Boost и C ++ TR1; он просто делает его немного чищеиспользовать шаблон функции, так как вы можете использовать тип функции напрямую; вам не нужно использовать тип указателя на функцию.) Вот пример использования:
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);
}