否则怎么实现“模板函数指针”?
-
05-09-2019 - |
题
是否有可能建立一组模板函数指针,而无需人工手动操作的麻烦?下面就来说明到底什么我谈论的例子。
让我们说我有一个经常调用的函数“写”,其中我有两个实现(写0和WRITE1),我希望能够动态地之间切换。这些写函数模板的参数类型。这样做的一种方式是只是有一个模板前端功能写(),其在内部使用if语句。
这原来是足够快的我的需求,但现在我在想,如果我可以在相同的使用功能指针(只是为了好玩)做的。这种方法的问题是,建立函数指针是一件麻烦事。是否有任何其他方式来实现基本的写理想(),但没有条件(直接静态调度)?
(其他的“规则”:我不能更改消息类具有写()方法,以及我无法改变使用现场码与消息数适配器替换消息)
FWIW,我发现一>基本上说,我在这里说同样的事情。
#include <iostream>
using namespace std;
template<typename T> void write0(T msg) { cout << "write0: " << msg.name() << endl; }
template<typename T> void write1(T msg) { cout << "write1: " << msg.name() << endl; }
// This isn't so bad, since it's just a conditional (which the processor will
// likely predict correctly most of the time).
bool use_write0;
template<typename T> void write(T msg) { if (use_write0) write0(msg); else write1(msg); }
struct MsgA { const char *name() { return "MsgA"; } };
struct MsgB { const char *name() { return "MsgB"; } };
struct MsgC { const char *name() { return "MsgC"; } };
struct MsgD { const char *name() { return "MsgD"; } };
// This doesn't work: templates may not be virtual.
#if 0
struct Writer { template<typename T> virtual void write(T msg) = 0; };
struct Writer0 { template<typename T> virtual void write(T msg) { cout << "write0: " << msg.name() << endl; } };
struct Writer1 { template<typename T> virtual void write(T msg) { cout << "write0: " << msg.name() << endl; } };
#endif
int main(int argc, char **argv) {
use_write0 = argc == 1;
// I can do this:
write(MsgA());
// Can I achieve the following without the verbosity (manual setup, named
// template instantiations, etc.)?
void (*pwriteA)(MsgA) = use_write0 ? (void(*)(MsgA)) write0<MsgA> : (void(*)(MsgA)) write1<MsgA>;
void (*pwriteB)(MsgB) = use_write0 ? (void(*)(MsgB)) write0<MsgB> : (void(*)(MsgB)) write1<MsgB>;
void (*pwriteC)(MsgC) = use_write0 ? (void(*)(MsgC)) write0<MsgC> : (void(*)(MsgC)) write1<MsgC>;
void (*pwriteD)(MsgD) = use_write0 ? (void(*)(MsgD)) write0<MsgD> : (void(*)(MsgD)) write1<MsgD>;
pwriteA(MsgA());
pwriteB(MsgB());
pwriteC(MsgC());
pwriteD(MsgD());
return 0;
}
解决方案
如果要切换记录功能来回在程序运行时,我想你必须手动设置对于每种类型的函数指针。
如果这是不够的,只是选择在启动日志记录功能,可以在一个完全通用的方式完成的,甚至不知道对于这类型的功能将在后面叫:
// writer functions
template<typename T> void write0(T msg) { std::cout << 0; };
template<typename T> void write1(T msg) { std::cout << 1; };
// global flag
bool use_write0;
// function pointers for all types
template<typename T>
struct dispatch {
typedef void (*write_t)(T);
static write_t ptr;
};
// main write function
template<typename T>
inline void write(T msg) {
(*dispatch<T>::ptr)(msg);
}
// the fun part
template<typename T>
void autoinit(T msg) {
if (use_write0)
dispatch<T>::ptr = &write0<T>;
else
dispatch<T>::ptr = &write1<T>;
// call again for dispatch to correct function
write(msg);
}
// initialization
template<typename T>
typename dispatch<T>::write_t dispatch<T>::ptr = &autoinit<T>;
// usage example
int main(int argc, char **argv) {
use_write0 = (argc == 1);
write("abc");
return 0;
}
有关T
到write<T>()
第一呼叫决定哪个写入功能应当使用每种类型。以后调用,那么直接使用函数指针到该函数。
其他提示
您也可以使用唐Clugston的 FastDelegates 头。任何不产生运行时开销,真正面向对象的代表。虽然使用它们的语法是不完美的,它比使用原始函数指针摆弄简单一些。
你为什么不使用函数指针数组?
#include <iostream>
using namespace std;
template<typename T> void write0(T msg) { cout << "write0: " << msg.name() << endl; }
template<typename T> void write1(T msg) { cout << "write1: " << msg.name() << endl; }
template<typename T> struct WriteSelector
{
static void(* const s_functions[])(T msg);
};
template<typename T> void(* const WriteSelector<T>::s_functions[])(T msg)=
{
&write0<T>,
&write1<T>
};
unsigned write_index=0;
template<typename T> void write(T msg)
{
WriteSelector<T>::s_functions[write_index](msg);
}
struct MsgA { const char *name() { return "MsgA"; } };
struct MsgB { const char *name() { return "MsgB"; } };
struct MsgC { const char *name() { return "MsgC"; } };
struct MsgD { const char *name() { return "MsgD"; } };
void Test()
{
write(MsgA());
write(MsgB());
write(MsgC());
write(MsgD());
}
int main()
{
Test();
write_index=1;
Test();
return 0;
}
有书面变异两个轴系:所述写0 / WRITE1选择和MSGA / B / C ....选择。
概念上,这意味着需要一个write
函数的N×M个实施方式。当然,如果写的实现添加,或添加一个消息类型,这导致RESP。要添加M或N的额外功能。
对于这两个轴系,你可以选择是否使用静态或动态的多态性,以实现它们。静态多态性可以使用模板或使用功能覆盖来进行。
它可以通过创建N元素类层次结构,在每个M级写入功能来完成。但它很快将成为一个维护的噩梦。除非邮件内容也运行时多态性。但问题是有关静态多态性的邮件。
由于运行时多态性的,因为太复杂排除了(你不能有一个模板函数虚,这将减少覆盖的详细程度),我们需要实现一个小类型常规调度,运行时转换成信息编译-time信息。
更具体地讲:模板化的主要作用(在称为Tmain
的例子)与作家使用的,并且与来自“真实” main
正确的模板参数调用它
此省略了使用一个“全局”选择可变的,但是面向对象的,简洁。
// twodimensionalpolymorph.cpp
//
#include <iostream>
using namespace std;
class Write0 {
public:
template< typename tMsg >
void operator()( /*const*/ tMsg& msg ) { cout << "write0: " << msg.name() << endl; };
};
class Write1 {
public:
template< typename tMsg >
void operator()( /*const*/ tMsg& msg ) { cout << "write1: "<< msg.name() << endl; };
};
struct MsgA { const char *name() { return "MsgA"; } };
struct MsgB { const char *name() { return "MsgB"; } };
struct MsgC { const char *name() { return "MsgC"; } };
struct MsgD { const char *name() { return "MsgD"; } };
// the Tmain does the real action
//
template< typename Writer >
int Tmain( Writer& write, int argc, char** args ) {
write( MsgA() );
write( MsgB() );
write( MsgB() );
write( MsgD() );
return 0;
}
// the main merely chooses the writer to use
//
int main( int argc, char** args ) {
if( argc==1 )
return Tmain( Write0(), argc, args);
else
return Tmain( Write1(), argc, args);
}