C++ 使用函数指针的 STL 映射
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2136998/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using a STL map of function pointers
提问by Hyman
I developed a scripting engine that has many built-in functions, so to call any function, my code just went into an if .. else if .. else if
wall checking the name but I would like to develop a more efficient solution.
我开发了一个具有许多内置函数的脚本引擎,因此要调用任何函数,我的代码只是在if .. else if .. else if
墙上检查名称,但我想开发一个更有效的解决方案。
Should I use a hashmapwith strings as keys and pointers as values? How could I do it by using an STL map?
我应该使用字符串作为键和指针作为值的哈希图吗?我怎么能通过使用 STL 映射来做到这一点?
EDIT: Another point that came into my mind: of course using a map will force the compiler not to inline functions, but my inefficient approach didn't have any overhead generated by the necessity of function calls, it just executes code.
编辑:我想到的另一点:当然,使用映射会强制编译器不内联函数,但我的低效方法没有因函数调用的必要性而产生任何开销,它只是执行代码。
So I wonder if the overhead generated by the function call will be any better than having an if..else
chain.. otherwise I could minimize the number of comparisons by checking a character at runtime (will be longer but faster).
所以我想知道函数调用产生的开销是否会比使用if..else
链更好……否则我可以通过在运行时检查一个字符来最小化比较次数(会更长但更快)。
回答by GManNickG
Whatever your function signatures are:
无论您的函数签名是什么:
typedef void (*ScriptFunction)(void); // function pointer type
typedef std::unordered_map<std::string, ScriptFunction> 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)();
}
Note that the ScriptFunction
type could be generalized to std::function</* whatever*/>
so you can support any callable thing, not just exactly function pointers.
请注意,该ScriptFunction
类型可以推广到,std::function</* whatever*/>
因此您可以支持任何可调用的东西,而不仅仅是函数指针。
回答by mloskot
You can also use Boost.Functionand Boost.Bindwhat even allows you, to some degree, to have map of heterogeneousfunctions:
您还可以使用Boost.Function和Boost.Bind甚至在某种程度上允许您拥有异构函数的映射:
typedef boost::function<void, void> fun_t;
typedef std::map<std::string, fun_t> 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));
It can be a map of functions of compatible prototypes as well, of course.
当然,它也可以是兼容原型的功能映射。
回答by Mohit
In C++11 you can do something like this : This Interface needs only the return type and it takes care of everything else from the caller side.
在 C++11 中,你可以做这样的事情:这个接口只需要返回类型,它负责调用方的所有其他事情。
#include <string>
#include <iostream>
#include <map>
#include <vector>
#include <typeinfo>
#include <typeindex>
#include <cassert>
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<int> fun4(){
std::cout<<"inside fun4\n";
std::vector<int> v(4,100);
return v;
}
// every function pointer will be stored as this type
typedef void (*voidFunctionType)(void);
struct Interface{
std::map<std::string,std::pair<voidFunctionType,std::type_index>> m1;
template<typename T>
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<typename T,typename... Args>
T searchAndCall(std::string s1, Args&&... args){
auto mapIter = m1.find(s1);
/*chk if not end*/
auto mapVal = mapIter->second;
// auto typeCastedFun = reinterpret_cast<T(*)(Args ...)>(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>(args)...);
}
};
int main(){
Interface a1;
a1.insert("fun1",fun1);
a1.insert("fun2",fun2);
a1.insert("fun3",fun3);
a1.insert("fun4",fun4);
a1.searchAndCall<void>("fun1");
int retVal = a1.searchAndCall<int>("fun3",2);
a1.searchAndCall<int>("fun2");
auto temp = a1.searchAndCall<std::vector<int>>("fun4");
return 0;
}
回答by AndreasT
Above answers seem to give a complete overview, this regards only your second question:
以上答案似乎给出了完整的概述,这仅涉及您的第二个问题:
Map element retrieval by key has O(log n) complexity. Hashmap retrieval by key has O(1) complexity + a little stuff on the side in case of collisions. So if theres a good hash function for your function names, use it. Your implementation will have a standard one. It should be fine.
通过键检索映射元素具有 O(log n) 复杂度。通过键检索 Hashmap 具有 O(1) 复杂度 + 发生冲突时的一些小东西。因此,如果您的函数名称有一个好的散列函数,请使用它。您的实现将有一个标准的。应该没问题。
But be aware, that anything below a hundred elements will not benefit all too much.
但请注意,低于一百元素的任何东西都不会受益太多。
The only downside of a hash map is collision. In your case, the hashmap will be relatively static. You know the function names you support. So I advise you to create a simple test case, where you call unordered_map<...>::hash_function with all your keys to make sure that nothing collides. After that, you can forget about it.
哈希映射的唯一缺点是冲突。在您的情况下,哈希图将是相对静态的。您知道您支持的函数名称。因此,我建议您创建一个简单的测试用例,在其中使用所有键调用 unordered_map<...>::hash_function 以确保没有任何冲突。在那之后,你可以忘记它。
A quick google for potential improvements on hash functions got me there:
一个关于哈希函数潜在改进的快速谷歌让我到了那里:
Maybe, depending on your naming conventions, you can improve on some aspects of the function.
也许,根据您的命名约定,您可以改进函数的某些方面。
回答by Jacob
Well, you can use any_map
to store functions with different signatures (but calling it will be messy) and you can use int_map
to call functions with a specific signature (looks nicer).
好吧,您可以使用any_map
存储具有不同签名的函数(但调用它会很麻烦),并且您可以使用int_map
调用具有特定签名的函数(看起来更好)。
int FuncA()
{
return 1;
}
float FuncB()
{
return 2;
}
int main()
{
// Int map
map<string,int(*)()> int_map;
int_map["A"] = FuncA;
// Call it
cout<<int_map["A"]()<<endl;
// Add it to your map
map<string, void(*)> any_map;
any_map["A"] = FuncA;
any_map["B"] = FuncB;
// Call
cout<<reinterpret_cast<float(*)()>(any_map["B"])()<<endl;
}
回答by EckhardN
I tried to use the second answer with c++11. I had to change the last line
from:
(*iter)();
to:
(*iter->second)();
我尝试在 c++11 中使用第二个答案。我不得不改变最后一行:
(*iter)();
到:
(*iter->second)();
so the code is now:
所以代码现在是:
#include <map>
typedef void (*ScriptFunction)(void); // function pointer type
typedef std::map<std::string, ScriptFunction> 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;
}
回答by Ia Rigby
I've managed to modify the example from Mohitto work on member function pointers:
#include <string>
#include <iostream>
#include <map>
#include <vector>
#include <typeinfo>
#include <typeindex>
#include <cassert>
template <typename A>
using voidFunctionType = void (A::*)(void);
template <typename A>
struct Interface{
std::map<std::string,std::pair<voidFunctionType<A>,std::type_index>> m1;
template<typename T>
void insert(std::string s1, T f1){
auto tt = std::type_index(typeid(f1));
m1.insert(std::make_pair(s1,
std::make_pair((voidFunctionType<A>)f1,tt)));
}
template<typename T,typename... Args>
T searchAndCall(A a, std::string s1, Args&&... args){
auto mapIter = m1.find(s1);
auto mapVal = mapIter->second;
auto typeCastedFun = (T(A::*)(Args ...))(mapVal.first);
assert(mapVal.second == std::type_index(typeid(typeCastedFun)));
return (a.*typeCastedFun)(std::forward<Args>(args)...);
}
};
class someclass {
public:
void fun1(void);
int fun2();
int fun3(int a);
std::vector<int> fun4();
};
void someclass::fun1(void){
std::cout<<"inside fun1\n";
}
int someclass::fun2(){
std::cout<<"inside fun2\n";
return 2;
}
int someclass::fun3(int a){
std::cout<<"inside fun3\n";
return a;
}
std::vector<int> someclass::fun4(){
std::cout<<"inside fun4\n";
std::vector<int> v(4,100);
return v;
}
int main(){
Interface<someclass> a1;
a1.insert("fun3",&someclass::fun3);
someclass s;
int retVal = a1.searchAndCall<int>(s, "fun3", 3);
return 0;
}
回答by BaraBashkaD
I try modify example to be Interface as member of class and wrap call to searchAndCall , however inside function fun1 stack is corrupted , a and b variables wrong
actually its failed on assert assert(mapVal.second == std::type_index(typeid(typeCastedFun)));
because different types
How correct to write wrapper ?
我尝试将示例修改为接口作为类的成员并包装对 searchAndCall 的调用,但是内部函数 fun1 堆栈已损坏,a 和 b 变量错误实际上它在断言时失败,assert(mapVal.second == std::type_index(typeid(typeCastedFun)));
因为不同的类型编写包装器如何正确?
template <typename A>
using voidFunctionType = void (A::*)(void);
template <typename A>
struct Interface {
std::map<std::string, std::pair<voidFunctionType<A>, std::type_index>> m1;
template<typename T>
void insert(std::string s1, T f1) {
auto tt = std::type_index(typeid(f1));
m1.insert(std::make_pair(s1,
std::make_pair((voidFunctionType<A>)f1, tt)));
}
template<typename T, typename... Args>
T searchAndCall(A* a, std::string s1, Args&&... args) {
auto mapIter = m1.find(s1);
auto mapVal = mapIter->second;
auto typeCastedFun = (T(A::*)(Args ...))(mapVal.first);
auto type = std::type_index(typeid(typeCastedFun));
assert(mapVal.second == std::type_index(typeid(typeCastedFun)));
return (a->*typeCastedFun)(std::forward<Args>(args)...);
}
};
class someclass2
{
Interface<someclass2> a1;
public:
someclass2()
{
a1.insert("fun1", &someclass2::fun1);
}
int fun1(int a, int b)
{
return a + b;
}
void masterfunction(int a, int b)
{
int result = a1.searchAndCall<int>(this, "fun1", a,b);
std::cout << "Result " << result << std::endl;
}
};
int main()
{
someclass2 s1;
s1.masterfunction(1, 2);
return 0;
}