Вот общий подход, который является типобезопасным и заставляет клиентский код быть правильным.
class Manager {
public:
typedef int /* or whatever */ ID;
template <typename Function>
static void save (Function * f, ID id) {
functions <Function> () .add (id, f);
}
template <typename Function>
static Function * get (ID id) {
return functions <Function> () .get (id);
}
private:
template <typename Function>
class FunctionStore {
public:
void add (Function *, ID);
Function * get (ID);
private:
// or vector, if you know ID is int.
std :: map <ID, Function *> m_functions;
};
// type_index is C++11 but you can implement it in C++03.
// void* here is unpleasant but you can improve it, RAII it.
typedef std :: map <std :: type_index, void *> Store;
static Store m_store;
template <typename Function>
FunctionStore <Function> & functions () {
FunctionStore <Function> * fs;
Store :: iterator i = m_store .find (typeid Function);
if (m_store .end () == i) {
fs = new FunctionStore <Function> ();
m_store [typeid Function] = fs;
}
else {
// This void* cast is OK because it's internally controlled
// and provably correct.
// We don't have to trust the library to not abuse it.
fs = static_cast <FunctionStore<Function>*> (i -> second);
}
return *fs;
}
};
// In the library
void foo1 (Foo *);
void bar1 (Bar *);
void foo2 (Foo *);
void bar2 (Bar *);
void init () {
Manager :: save (foo1, 1);
Manager :: save (foo2, 2);
Manager :: save (bar1, 1);
Manager :: save (bar2, 2);
Manager :: get <void(Foo*)> (1) (new Foo ()); // OK, calls foo1
Manager :: get <void(Foo*)> (1) (new Bar ()); // Will not compile
Manager :: get <void(Bar*)> (2) (new Bar ()); // OK, calls bar2
}
Если вы не хотите накладных расходов на поиск в m_store
(и/или хотите избежать void
в Manager::Store
), вы можете сделать Manager
самим классом шаблона, недостатком является то, что теперь вам нужно следить за вашими статическими определениями m_store
. Это нормально, если вы знаете, что клиенты будут использовать только заданный набор Function
подписей.
void init () {
Manager <void(Foo*)> :: save (foo1, 1);
Manager <void(Foo*)> :: save (foo2, 2);
Manager <void(Foo*)> :: save (bar1, 1); // Won't compile
Manager <void(Bar*)> :: save (bar1, 1);
Manager <void(Bar*)> :: save (bar2, 2);
Manager <void(Foo*)> :: get (1) (new Foo ()); // OK, calls foo1
Manager <void(Foo*)> :: get (1) (new Bar ()); // Will not compile
Manager <void(Bar*)> :: get (2) (new Bar ()); // OK, calls bar2
}
Функция init
иллюстрирует важный момент, который я подчеркивал в комментариях к другим сообщениям: если вы знаете, с какими типами собираетесь вызывать функцию, то вы знаете какие набор функций для выборки. Нет необходимости пытаться втиснуть их все вместе, и это только навредит вашей безопасности типов.
person
spraff
schedule
08.09.2011
dynamic_cast
вас, вам нужно не помещать разные типы функций в один и тот же контейнер. Разделите эти типы в своем дизайне. - person spraff   schedule 07.09.2011