我想创建一个类,其中包含一些静态成员函数和一个包含指向这些函数的指针的映射。但是,它们可以采用不同数量和类型的参数。所以关注this thread我试过类似的东西:
class BeliefCondFunc
{
static std::unordered_map<std::string, std::function<bool()>> FuncMap;
static bool Greater(int A, int B)
{
return A > B;
}
static bool Between(float A, float B, float C)
{
return A > B && A < C;
}
static void InitMap()
{
FunctionMap["Greater"] = std::bind(&BeliefCondFunc::Greater, ???);
FunctionMap["Between"] = std::bind(&BeliefCondFunc::Between, ???);
}
};
现在,我需要用什么替换上面代码中的 ???
才能使其正常工作?最后我希望能够这样调用它:
BeliefCondFunc::FuncMap["Greater"](1, 2);
BeliefCondFunc::FuncMap["Between"](1.0f, 2.0f, 3.0f);
如果不使用像 boost 这样的外部库,这在 C++11 中是否可行?
最佳答案
您需要将 ???
替换为占位符,以将闭包的参数转发给函数。
std::bind(&BeliefCondFunc::Greater, _1, _2);
但是您应该注意,您的 map 包含不接受任何参数的函数对象。因此,您可能会收到调用不匹配错误。从 C++14 开始,甚至可能尝试对新构造的值进行赋值。
我也觉得你把解决方案复杂化了。对于静态成员函数,一个简单的函数指针类型就足够了。然后您需要执行一些转换,但是可以将其封装在另一个模板化静态成员函数中。现在,转换函数指针类型不是很安全(想想当你把数字参数弄错时会发生什么)。但是您似乎已经打算回避类型系统,所以这里是:
#include <unordered_map>
#include <string>
#include <utility>
struct BeliefCondFunc
{
using cb_type = bool(*)();
static std::unordered_map<std::string, cb_type> const FuncMap;
static bool Greater(int A, int B)
{
return A > B;
}
static bool Between(float A, float B, float C)
{
return A > B && A < C;
}
template<typename ...Args>
static bool call(std::string const& key, Args&&... args){
using prototype = bool(*)(Args...);
return reinterpret_cast<prototype>(FuncMap.at(key))(std::forward<Args>(args)...);
};
};
std::unordered_map<std::string, BeliefCondFunc::cb_type> const BeliefCondFunc::FuncMap {
{"Greater", reinterpret_cast<cb_type>(&BeliefCondFunc::Greater) },
{"Between", reinterpret_cast<cb_type>(&BeliefCondFunc::Between) }
};
int main() {
BeliefCondFunc::call("Greater", 1, 2);
return 0;
}
如果转换让您担心,您可以将函数指针原型(prototype)的 std::type_info
指针与其一起存储。然后比较两者并在运行时在不匹配时抛出异常是一件简单的事情。像这样:
#include <unordered_map>
#include <string>
#include <utility>
#include <typeinfo>
#include <stdexcept>
#include <iostream>
struct BeliefCondFunc
{
using cb_type = bool(*)();
using map_val = std::pair<cb_type, std::type_info const*>;
static std::unordered_map<std::string, map_val> const FuncMap;
static bool Greater(int A, int B)
{
return A > B;
}
static bool Between(float A, float B, float C)
{
return A > B && A < C;
}
template<typename ...Args>
static map_val make_map_val(bool (*func)(Args...)) {
return std::make_pair(reinterpret_cast<cb_type>(func),
&typeid(decltype(func)));
}
template<typename ...Args>
static bool call(std::string const& key, Args&&... args){
using prototype = bool(*)(Args...);
auto& func_n_typeid = FuncMap.at(key);
if (typeid(prototype) != *func_n_typeid.second )
throw std::domain_error("Prototype mismatch");
return reinterpret_cast<prototype>(func_n_typeid.first)(std::forward<Args>(args)...);
};
};
std::unordered_map<std::string, BeliefCondFunc::map_val> const BeliefCondFunc::FuncMap {
{"Greater", make_map_val(&BeliefCondFunc::Greater) },
{"Between", make_map_val(&BeliefCondFunc::Between) }
};
int main() {
BeliefCondFunc::call("Greater", 1, 2);
try {
BeliefCondFunc::call("Lesser", 1, 2);
} catch (std::out_of_range&) {
std::cout << "No such function\n";
}
try {
BeliefCondFunc::call("Between", 1, 2);
} catch (std::domain_error&) {
std::cout << "Wrong number of arguments\n";
}
return 0;
}
再一次,see it live .
正如 promise 的那样,一个解释:
下面两行创建两个type aliases (c++11 语法,等同于 typedef,但明确将新标识符与类型分开)。我将存储所有指针的虚拟回调类型命名为,并将存储在映射中的值对命名为。
using cb_type = bool(*)(); using map_val = std::pair<cb_type, std::type_info const*>;
make_map_val
是一个模板函数,可以传递任何返回bool
的函数指针。每当我用函数指针调用它时,它都会使用 template argument deduction找出函数的参数类型。我不直接使用参数类型,而是依靠decltype
来获取函数的类型。回想起来,我本可以直接将函数指针传递给typeid
,因为它会以相同的方式推导类型。在这里使用临时函数的重要性在于自动捕获有关函数指针的尽可能多的静态类型数据,而无需为它询问make_map_val
的调用站点。call
再次使用模板参数类型推导来确定我们要调用的函数的参数类型。类型别名还用于方便地引用我们最终将转换为的函数指针类型,以及typeid
的参数。别名和函数调用本身是使用参数 pack expansion 编写的.参数通过转发引用传递给函数,并通过调用std::forward
传递给函数指针。 , 都是为了保留参数的值类别。如果你的所有函数都只对小的、复制成本低的值进行操作,你可能可以通过按值传递所有内容来逃脱。
关于c++ - 如何在 C++11 中创建指向静态成员函数的指针映射?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41102974/