是否有可能使用模板元编程在编译时选择类将要执行的功能?
Is it possible to use template metaprogramming to do compile time selection of functionality that class will perform?
我经常这样写类:
Logger::Logger(bool log_time_, bool log_percentage, bool log_size):log_time(log_time_)... //made up example
Logger::Log()
{
string log_line;
if (log_time)
log_line += (get_time());
if (log_percentage)
log_line += (get_percentage());
//...
}
我想知道是否有一种方法可以将我的类使用模板魔法转换为代码,执行"if (something)"部分在编译时。
编辑:bool变量的值在编译时已知。
引言
本文将提供两种解决方案,一种使用c++ 03,另一种使用c++ 11。
这很难。你需要写很多代码),如果你想要一个真正的编译时间如果是保证没有任何运行时开销(没有函数跳转,等等)。
这是可能的,尽管如果你想添加另一个选项(在c++ 03中),代码维护起来会很繁琐。我建议你看看下面的解决方案。
c++ 03中的解
你的编译器应该足够聪明,可以优化掉对LogHelper<+NONE>
的任何调用,尽管如果你只是在寻找更可读的代码,而不是一个极好的性能增益,这种语法是相当甜蜜的。
enum LoggerType {
NONE =0,
DATE = (1<<0),
TIME = (1<<1),
PERCENT = (1<<2)
};
template<int> void LogHelper (std::string&);
template<> inline void LogHelper<+NONE> (std::string&) {}
template<> inline void LogHelper<+DATE> (std::string& s) {s += "1970-01-01 ";}
template<> inline void LogHelper<+TIME> (std::string& s) {s += "12:01:01 ";}
template<> inline void LogHelper<+PERCENT> (std::string& s) {s += "42% ";}
template<int LOG_FLAG = NONE>
struct Logger {
static void log (std::string const& description) {
std::string s1;
LogHelper<DATE & LOG_FLAG> (s1);
LogHelper<TIME & LOG_FLAG> (s1);
LogHelper<PERCENT & LOG_FLAG> (s1);
std::cerr.width (25);
std::cerr << s1 << " >> " << description << std::endl;
}
};
…
int
main (int argc, char * argv[]) {
Logger<DATE|TIME|PERCENT> foo_log;
Logger<TIME> time_log;
Logger<> no_log;
time_log.log ("log objects initialized!");
foo_log .log ("using foo_log");
no_log .log ("about to terminate application");
}
12:01:01 >> log objects initialized!
1970-01-01 12:01:01 42% >> using foo_log
>> about to terminate application
使用可变模板的解决方案(c++ 11)
enum LoggerType {
NONE, PERCENT, DATE, TIME
};
template<LoggerType T = NONE, LoggerType ... Next>
std::string LogHelper () {
return LogHelper<T> () + "; " + LogHelper<Next...> ();
}
template<> std::string LogHelper<NONE> () {return ""; }
template<> std::string LogHelper<DATE> () {return "1970-01-01";}
template<> std::string LogHelper<TIME> () {return "00:01:42";}
template<> std::string LogHelper<PERCENT> () {return "42%";}
template<LoggerType ... Types>
struct Logger {
static void log (std::string const& description) {
std::cerr.width (25);
std::cerr << LogHelper<Types...> ();
std::cerr << " >> " << description;
std::cerr << std::endl;
}
};
…
int
main (int argc, char * argv[]) {
Logger<DATE,TIME,PERCENT> foo_log;
Logger<TIME> time_log;
Logger<> no_log;
time_log.log ("log objects initialized!");
foo_log .log ("using foo_log");
no_log .log ("about to terminate application");
}
00:01:42 >> log objects initialized!
1970-01-01; 00:01:42; 42% >> using foo_log
>> about to terminate application
是的,这是可能的,尽管有些编译器不喜欢您这样做。然而,你最终会得到一组不同的类,因为你必须提供布尔值作为模板说明符(可能不是正确的术语)。
我认为你最好使用虚拟日志方法代替?然后创建几个类,每个类定义自己的Log方法。除非你有其他理由,否则我建议在这种情况下使用虚函数而不是模板。
当然。像这样:
template <bool Opt1, bool Opt2> void foo()
{
Action1<Opt1>();
Action2<Opt2>();
}
template <bool> void Action1();
template <bool> void Action2();
template <> void Action1<true>() { /* ... */ }
template <> void Action1<false>() { /* ... */ }
template <> void Action2<true>() { /* ... */ }
template <> void Action2<false>() { /* ... */ }
像foo<true, false>();
那样调用
为什么要在不需要的地方使用模板呢?任何自重的c++编译器都会基于常量表达式进行常量折叠:无论如何,它必须在编译时计算出这些表达式的值。也就是说,任何基于常量表达式的条件都不会在运行时出现。这种方法仅有的两个缺点是:
- 你依赖于编译器在相当基本的水平上相当体面 从代码中引用的
- 符号从未执行过,我仍然被引用
你可以这样做
struct DummyEnhancer
{
void operator()(string& s) const{
}
};
struct TimerEnhancer
{
void operator()(string& s) const{
s += "time";
}
};
struct PercenterEnhancer
{
void operator()(string& s) const{
s += "percent";
}
};
template <typename Timer , typename Percenter>
struct Logger
{
void Log()
{
string log_line;
Timer t;
t( log_line );
Percenter p;
p( log_line );
}
};
int main()
{
Logger<DummyEnhancer,DummyEnhancer> foo;
foo.Log();
Logger< TimerEnhancer , PercenterEnhancer > bar;
bar.Log();
return 0;
}
foo.Log()
将是一个无操作和bar.log()
将做定时器和百分比的东西你想
是对于编译时间常数,您可以使用template
编程:
template<bool log_time, bool log_perchentage, bool log_size>
struct Logger
{
static void log()
{ // log everything
string log_line;
log_line+=(get_time());
log_line+=(get_perchentage());
log_line+=(get_size());
}
};
template<>
struct Logger<false, false, false>
{
static void log()
{ // nothing to log
}
};
您还可以专门化中间版本,如Logger<true, false, false>
和Logger<false, true, true>
等。避免多个专门化的另一种方法是将time / percentage / size
分离为不同的struct
,并分别记录它们。
- 在执行其他功能的同时播放动画(LED矩阵和Arduino/ESP8266)
- 如何使用默认参数等选择模板专业化
- 选择在CMAKE中构建库或可执行文件
- 执行默认值:在C 中的开关案例选择语句中
- 您如何选择在opencv-dnn上执行操作的设备
- 在什么条件下,数据库在从 cpp 执行选择查询时不会关闭游标
- C 编译器如何在延期和异步执行std :: async之间进行选择
- 如何将音频与功率谱同步并选择帧长度 N(执行 fft)
- 选择零件的策略要执行C
- 使用用户定义的函数对循环/对称值执行Sqlite(C API)和查询(选择)
- 如果我已经完成了当前功能,我该如何选择继续执行另一个功能
- 为什么 Google 测试会自动执行未选择的测试用例
- 当选择一个选项时,如何执行另一个c++程序
- c++链接器如何选择将类的哪个定义链接到可执行文件中
- 选择并执行Qt中QListWidget上显示的MIDI文件
- 是否有可能使用模板元编程在编译时选择类将要执行的功能?
- 如何在链表上执行选择排序
- 游戏有时不会执行随机选择的策略
- 执行while循环,如果用户选择yes行为不正确,则重新启动程序
- 如何在分叉和执行后使用带有选择的管道