如何解决带有成员用法的模板化函数上的typedef
我有两个需要在类中公开的函数,它们看起来像这样(更多内容将在后面介绍):
void print_a(std::string s);
void print_b(std::string s,int val);
“深入了解”他们正在做同样的事情,即在映射中进行查找,并将调用参数传递给映射所检索的函数指针:
#include <stdint.h>
#include <iostream>
#include <string>
#include <map>
class Thing{
private:
void do_a(){
std::cout << "hello";
}
//there might be also a method do_a_extended() which has a different key in the map
void do_b(int age){
std::cout << "my age is " << age;
}
typedef void (Thing::*do_stuff_a)();
typedef void (Thing::*do_stuff_b)(int);
std::map<std::string,do_stuff_a> a_table;
std::map<std::string,do_stuff_b> b_table;
public:
void print_a(std::string s){
do_stuff_a handler = a_table[s];
if(handler){
(this->*handler)();
}
}
void print_b(std::string s,int val){
do_stuff_b handler = b_table[s];
if(handler){
(this->*handler)(val);
}
}
};
我不喜欢涉及很多样板代码的事实。我想知道是否可以将成员传递到模板中,以便做到这一点:
class Thing{
private:
void do_a(){
std::cout << "hello";
}
void do_b(int age){
std::cout << "my age is " << age;
}
typedef void (Thing::*do_stuff_a)();
typedef void (Thing::*do_stuff_b)(int);
std::map<std::string,do_stuff_b> b_table;
template<<MAP_MEMBER>,typename ... PP>
void print_x(std::string s,PP &&... pp){
auto handler = <MAP_MEMBER>[s];
if(handler){
(this->*handler)(std::forward<PP>(pp) ...);
}
}
public:
typedef decltype(print_x<a_table>) print_a;
typedef decltype(print_x<b_table>) print_b;
};
任何关于如何摆脱样板的想法都值得赞赏。
解决方法
无需复杂,只需将打印机用作将成员传递给通用打印方法的包装器,如下所示:
class Foo
{
int a;
char b;
template <typename M>
void Print (M & member)
{
// complicated function
}
public:
void PrintA ()
{
Print(a);
}
void PrintB ()
{
Print(b);
}
};
因此在您的示例中,公共打印功能成为包装器功能:
class Thing
{
// ...
template <typename T,typename ... PP>
void print (T & table,const std::string & key,PP && ... pp)
{
auto method = table[key];
if (method)
(this->*method)(std::forward<PP>(pp)...);
}
public:
template <typename ... PP>
void print_a (PP && ... pp)
{
print(a_table,std::forward<PP>(pp)...);
}
template <typename ... PP>
void print_b (PP && ... pp)
{
print(b_table,std::forward<PP>(pp)...);
}
};
如果您使用-O3
优化,则应该内联这些公共方法。
这是一个运行中的解决方案,具有更少的样板且不需要元编程:
#include <iostream>
#include <string>
#include <map>
class Thing
{
void do_a_1 ()
{
std::cout << "hello" << std::endl;
}
void do_a_2 ()
{
std::cout << "goodbye" << std::endl;
}
void do_b (int age)
{
std::cout << "my age is " << age << std::endl;
}
template <typename ... PP>
using MapMethod = std::map<std::string,void (Thing::*)(PP...)>;
MapMethod<> a_table;
MapMethod<int> b_table;
template <typename T>
void insert (T) {}
template <typename T,typename M,typename ... PP>
void insert (T & table,M && method,PP && ... pp)
{
table.insert({key,method});
insert(table,pp...);
}
template <typename T,typename ... PP>
void print (const T & table,PP && ... pp)
{
auto result = table.find(key);
if (result != table.end())
{
auto method = result->second;
(this->*method)(pp...);
}
}
public:
Thing ()
{
insert(a_table,"apple",&Thing::do_a_1,"banana",&Thing::do_a_2);
insert(b_table,"ostrich",&Thing::do_b);
}
void print_a (const std::string & key)
{
print(a_table,key);
}
void print_b (const std::string & key,int val)
{
print(b_table,key,val);
}
};
int main ()
{
Thing t;
t.print_a("apple");
t.print_b("ostrich",12);
t.print_a("banana");
t.print_a("Do nothing");
}
如果包装方法在实际问题中不可避免地重复(也许完美的转发变得烦人),则可以使用宏进一步减少样板以制作打印方法:
class Thing
{
// private code is the same,except:
// there's no need for the generic print method anymore
public:
Thing ();
#define PRINT_MACRO(FUNCTION_NAME,MEMBER_TABLE) \
template <typename ... PP> \
void FUNCTION_NAME (const std::string & key,PP && ... pp) \
{ \
auto result = MEMBER_TABLE.find(key); \
if (result != MEMBER_TABLE.end()) \
{ \
auto method = result->second; \
(this->*method)(pp...); \
} \
}
PRINT_MACRO(print_a,a_table)
PRINT_MACRO(print_b,b_table)
#undef PRINT_MACRO
};
最后,您确定要使用std::map
而不是std::unordered_map
吗?这个问题表明您不关心订购。
要定义需要在标识符中进行级联的内容,可以使用宏或代码生成。
特别是,由于您还需要在生成的代码中处理任意数量的参数,因此我会进行代码生成。
,您可以将地图提取到一个额外的类,该类还可以处理搜索和调用条目:
template <typename C,typename K,typename...Args>
class MemberFunctionCaller
{
private:
using FunctionType = void(C::*)(Args...);
using MapType = std::map<K,FunctionType>;
MapType registry;
public:
void Register(const K& key,FunctionType value)
{
registry[key] = value;
}
void Call(const K& key,C* self,const Args&...args)
{
auto iter = registry.find(key);
if(iter != registry.end())
{
FunctionType func = iter->second;
(self->*func)(args...);
}
}
};
简单的typedef可以简化“事物”类中的用法:
template <typename...Args>
using ThingFunctionCaller = MemberFunctionCaller<Thing,std::string,Args...>;
事物类可能看起来像这样:
class Thing{
template <typename...Args>
using ThingFunctionCaller = MemberFunctionCaller<Thing,Args...>;
private:
void do_a(){
std::cout << "hello" << std::endl;
}
void do_b(int age){
std::cout << "my age is " << age << std::endl;
}
ThingFunctionCaller<> a_table;
ThingFunctionCaller<int> b_table;
public:
void print_a(std::string s){
a_table.Call(s,this);
}
void print_b(std::string s,int val){
b_table.Call(s,this,val);
}
};
这就是操作中的样子:https://gcc.godbolt.org/z/KM5a85
,您可以使用静态模板注册表来存储名称和成员函数之间的关系。 这还有其他缺点,但是可以在您的特定用例中使用。
(使用一些额外的编码,您甚至可以使这项工作更“自然”)。
template <typename C,typename...Args>
using MemberFunctionPointer = void(C::*)(Args...);
template <typename C,typename...Args>
class MemberFunctionCaller
{
private:
using FunctionType = MemberFunctionPointer<C,Args...>;
using MapType = std::map<std::string,FunctionType>;
static MapType& GetRegistry(){
static MapType registry;
return registry;
}
public:
static void Register(const std::string& key,FunctionType function)
{
auto& registry = GetRegistry();
registry[key] = function;
}
static void Call(const std::string& key,const Args&...args)
{
auto& registry = GetRegistry();
auto iter = registry.find(key);
if(iter != registry.end())
{
FunctionType func = iter->second;
(self->*func)(args...);
}
}
};
template <typename C>
class MemberFunctionRegistry
{
public:
template <typename...Args>
static void Register(const std::string& key,MemberFunctionPointer<C,Args...> function)
{
MemberFunctionCaller<C,Args...>::Register(key,function);
}
template <typename...Args>
static void Call(const std::string& key,const Args&...args)
{
MemberFunctionCaller<C,Args...>::Call(key,self,args...);
}
};
您的Thing类可能如下所示:
class Thing{
private:
void do_a(){
std::cout << "hello" << std::endl;
}
void do_a_extended(){
std::cout << "hello world" << std::endl;
}
void do_b(int age){
std::cout << "my age is " << age << std::endl;
}
MemberFunctionRegistry<Thing> registry;
public:
static void RegisterMemberFunctions()
{
MemberFunctionRegistry<Thing>::Register("A",&Thing::do_a);
MemberFunctionRegistry<Thing>::Register("AX",&Thing::do_a_extended);
MemberFunctionRegistry<Thing>::Register("B",&Thing::do_b);
}
template <typename...Args>
void print_x(std::string s,Args...args){
registry.Call(s,args...);
}
};
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。