如何在 C++11 中创建指向静态成员函数的指针映射?
How to create map of pointers to static member functions in C++11?
我想创建一个 class,其中包含一些静态成员函数和一个包含指向这些函数的指针的映射。但是,它们可以采用不同数量和类型的参数。所以在 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);
但是您应该注意,您的地图包含不接受任何参数的函数对象。因此,您可能会收到呼叫不匹配错误。从 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;
}
如果转换让您担心,您可以将函数指针原型的 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。
正如承诺的那样,一个解释:
下面两行创建了两个type aliases(c++11语法,相当于一个typedef,但是把新标识符和类型明确分开了)。我将虚拟回调类型命名为我将存储所有指针的名称,以及我将存储在映射中的值对。
using cb_type = bool(*)();
using map_val = std::pair<cb_type, std::type_info const*>;
make_map_val
是一个模板函数,可以传递 returns 和 bool
的任何函数指针。每当我用函数指针调用它时,它都会使用 template argument deduction 来确定函数的参数类型。我不直接使用参数类型,而是依靠 decltype
来获取函数的类型。回想起来,我本可以直接将函数指针传递给 typeid
,因为它会以相同的方式推导类型。在这里使用临时函数的重要性在于自动捕获有关函数指针的尽可能多的静态类型数据,而无需询问 make_map_val
的调用站点。
call
再次使用模板参数类型推导来计算我们要调用的函数的参数类型。类型别名还用于方便地引用我们最终将转换为的函数指针类型,以及 typeid
的参数。别名和函数调用本身都是使用参数pack expansion. The parameters are passed to the function with a forwarding reference, and onward to the function pointer via a call to std::forward
编写的,都是为了保留参数的值类别。如果你的所有函数都只对小的、复制成本低的值进行操作,你可能可以通过按值传递所有内容来逃脱。
如果你想在一个容器中使用不同的对象类型,有两种方法:
1.继承+RTTI
2. 变体
我想创建一个 class,其中包含一些静态成员函数和一个包含指向这些函数的指针的映射。但是,它们可以采用不同数量和类型的参数。所以在 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);
但是您应该注意,您的地图包含不接受任何参数的函数对象。因此,您可能会收到呼叫不匹配错误。从 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;
}
如果转换让您担心,您可以将函数指针原型的 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。
正如承诺的那样,一个解释:
下面两行创建了两个type aliases(c++11语法,相当于一个typedef,但是把新标识符和类型明确分开了)。我将虚拟回调类型命名为我将存储所有指针的名称,以及我将存储在映射中的值对。
using cb_type = bool(*)(); using map_val = std::pair<cb_type, std::type_info const*>;
make_map_val
是一个模板函数,可以传递 returns 和bool
的任何函数指针。每当我用函数指针调用它时,它都会使用 template argument deduction 来确定函数的参数类型。我不直接使用参数类型,而是依靠decltype
来获取函数的类型。回想起来,我本可以直接将函数指针传递给typeid
,因为它会以相同的方式推导类型。在这里使用临时函数的重要性在于自动捕获有关函数指针的尽可能多的静态类型数据,而无需询问make_map_val
的调用站点。call
再次使用模板参数类型推导来计算我们要调用的函数的参数类型。类型别名还用于方便地引用我们最终将转换为的函数指针类型,以及typeid
的参数。别名和函数调用本身都是使用参数pack expansion. The parameters are passed to the function with a forwarding reference, and onward to the function pointer via a call tostd::forward
编写的,都是为了保留参数的值类别。如果你的所有函数都只对小的、复制成本低的值进行操作,你可能可以通过按值传递所有内容来逃脱。
如果你想在一个容器中使用不同的对象类型,有两种方法: 1.继承+RTTI 2. 变体