Usar un mapa STL de indicadores de función

Desarrollé un motor de scripting que tiene muchas funciones integradas, por lo que para llamar a cualquier función, mi código simplemente entró en un if .. else if .. else if miro el nombre pero me gustaría desarrollar una solución más eficiente.

¿Debo usar un hashmap con cadenas como claves y punteros como valores? ¿Cómo podría hacerlo usando un mapa STL?

EDITAR : Otro punto que se me vino a la mente: por supuesto, usar un mapa forzará al comstackdor a no incluir funciones en línea, pero mi enfoque ineficiente no generó ningún gasto generado por la necesidad de llamadas a funciones, simplemente ejecuta código.

Así que me pregunto si la sobrecarga generada por la llamada a la función será mejor que tener una cadena if..else … de lo contrario podría minimizar el número de comparaciones al verificar un carácter en el tiempo de ejecución (será más largo pero más rápido).

Cualesquiera que sean sus firmas de funciones:

 typedef void (*ScriptFunction)(void); // function pointer type typedef std::unordered_map script_map; // ... void some_function() { } // ... script_map m; m.emplace("blah", &some_function); // ... void call_script(const std::string& pFunction) { auto iter = m.find(pFunction); if (iter == m.end()) { // not found } (*iter->second)(); } 

Tenga en cuenta que el tipo ScriptFunction podría generalizarse a std::function para que pueda admitir cualquier elemento que se pueda llamar, no solo exactamente punteros de función.

También puede usar Boost.Function y Boost.Bind lo que incluso le permite, hasta cierto punto, tener un mapa de funciones heterogéneas :

 typedef boost::function fun_t; typedef std::map funs_t; funs_t f; void foo() {} void goo(std::string& p) {} void bar(int& p) {} f["foo"] = foo; f["goo"] = boost::bind(goo, "I am goo"); f["bar"] = boost::bind(bar, int(17)); 

También puede ser un mapa de funciones de prototipos compatibles, por supuesto.

En C ++ 11 puede hacer algo como esto: esta interfaz solo necesita el tipo de devolución y se encarga de todo lo demás desde el lado de la persona que llama.

 #include  #include  #include  #include  #include  #include  #include  void fun1(void){ std::cout< <"inside fun1\n"; } int fun2(){ std::cout<<"inside fun2\n"; return 2; } int fun3(int a){ std::cout<<"inside fun3\n"; return a; } std::vector fun4(){ std::cout< <"inside fun4\n"; std::vector v(4,100); return v; } // every function pointer will be stored as this type typedef void (*voidFunctionType)(void); struct Interface{ std::map> m1; template void insert(std::string s1, T f1){ auto tt = std::type_index(typeid(f1)); m1.insert(std::make_pair(s1, std::make_pair((voidFunctionType)f1,tt))); } template T searchAndCall(std::string s1, Args&&... args){ auto mapIter = m1.find(s1); /*chk if not end*/ auto mapVal = mapIter->second; // auto typeCastedFun = reinterpret_cast(mapVal.first); auto typeCastedFun = (T(*)(Args ...))(mapVal.first); //compare the types is equal or not assert(mapVal.second == std::type_index(typeid(typeCastedFun))); return typeCastedFun(std::forward(args)...); } }; int main(){ Interface a1; a1.insert("fun1",fun1); a1.insert("fun2",fun2); a1.insert("fun3",fun3); a1.insert("fun4",fun4); a1.searchAndCall("fun1"); int retVal = a1.searchAndCall("fun3",2); a1.searchAndCall("fun2"); auto temp = a1.searchAndCall>("fun4"); return 0; } 

Las respuestas anteriores parecen dar una visión completa, esto solo se refiere a su segunda pregunta:

La recuperación del elemento de mapa por clave tiene complejidad O (log n). La recuperación de Hashmap por clave tiene O (1) complejidad + un poco de material en el lateral en caso de colisión. Entonces, si hay una buena función hash para los nombres de sus funciones, úselo. Su implementación tendrá una estándar. Debería estar bien.

Pero ten en cuenta que cualquier cosa por debajo de cien elementos no se beneficiará demasiado.

El único inconveniente de un mapa hash es la colisión. En su caso, el hashmap será relativamente estático. Usted conoce los nombres de las funciones que admite. Así que te aconsejo que crees un caso de prueba simple, donde llamas a unordered_map < ...> :: hash_function con todas tus claves para asegurarte de que nada colisione. Después de eso, puedes olvidarte de eso.

Un google rápido para posibles mejoras en las funciones hash me llevó allí:

Una buena función hash

Tal vez, dependiendo de las convenciones de nomenclatura, pueda mejorar algunos aspectos de la función.

Bueno, puedes usar any_map para almacenar funciones con diferentes firmas (pero llamarlas será desordenado) y puedes usar int_map para llamar a funciones con una firma específica (se ve mejor).

 int FuncA() { return 1; } float FuncB() { return 2; } int main() { // Int map map int_map; int_map["A"] = FuncA; // Call it cout<  any_map; any_map["A"] = FuncA; any_map["B"] = FuncB; // Call cout< (any_map["B"])()<  

Intenté usar la segunda respuesta con c ++ 11. Tuve que cambiar la última línea de:
(* iter) ();
a:
(* iter-> second) ();

entonces el código ahora es:

  #include  typedef void (*ScriptFunction)(void); // function pointer type typedef std::map script_map; // ... void some_function(void) { } script_map m; void call_script(const std::string& pFunction) { script_map::const_iterator iter = m.find(pFunction); if (iter == m.end()) { // not found } (*iter->second)(); } int main(int argc, const char * argv[]) { //.. m.insert(std::make_pair("blah", &some_function)); call_script("blah"); //.. return 0; }