短路流
Short-circuiting a stream
该应用程序有一个日志系统,允许在运行时启用或禁用其模块的日志记录功能。日志命令接受输入流(作为 "sprintf" 的安全替代方案;几乎没有比调试系统导致崩溃更烦人的情况了。
问题是,如果我执行以下操作:
logger.Trace << "Requests pending:" << buffer.findRequests();
和findRequests()
具有很高的计算复杂度,即使禁用模块的Trace日志级别,也会在Trace operator<<
方法中被拒绝之前执行搜索(在组装流时) .
明显的替代方法是在代码中乱加:
if(logger.Trace.Enabled()) logger.Trace << ...
不漂亮,也不舒服。我可以使用 if
的宏或使用 &&
短路的宏来替换它,这样会更好一些(可以用作 RValue,遵循 Stream 哲学 returns bool false on disabled流):
#define TRACE if(logger.Trace.Enabled()) logger.Trace
#define TRACE dummyLogVar = logger.Trace.Enabled() && logger.Trace
都不是特别漂亮或安全。一位同事建议关闭:
logger.Trace([&](f){f << "Requests pending:" << buffer.findRequests();});
.Trace
仅在启用该级别时才会评估闭包。从逻辑上讲,这很好,但在语法上绝对可怕。乱七八糟的输入:logger.Trace([&](f){f <<
... ;});
数百次?
有没有更简洁、安全和舒适的方法来防止对流进行评估?
确实可以使用宏,但是你需要类似函数的宏,将输出作为参数,并且你需要确保它是单个语句。
第二部分,确保宏体是单个语句,很简单,通常使用 do { ... } while (false)
。
使用宏的参数也不难,只要参数中没有逗号即可。逗号限制包括在宏参数中使用带有自己参数的函数调用,预处理器非常愚蠢,在宏参数中使用 any 逗号作为宏参数的分隔符.
在不考虑逗号限制的最简单形式中,宏可能看起来像
#define TRACE(output) \
do \
{ \
if (logger.Trace.Enabled()) \
{ \
logger.Trace << output; \
} \
} while (false)
注意while (false)
后没有分号。
然后你就可以像这样使用它了
TRACE("Requests pending:" << buffer.findRequests());
do { ... } while (false)
部分很可能会被编译器优化掉,只剩下一个简单的 if
检查。如果 logger.Trace.Enabled()
returns false
那么除了检查之外什么都不应该发生。
如果您的编译器支持 C++11 或更高版本,它应该支持 variadic macros,这应该可以帮助您克服宏参数中逗号的限制。
使用可变宏,宏将如下所示:
#define TRACE(...) \
do \
{ \
if (logger.Trace.Enabled()) \
{ \
logger.Trace << __VA_ARGS__; \
} \
} while (false)
我解决了那个问题。最后,我用这个接口和相关的宏创建了一个 class Log
:
class Log
{
public:
static bool trace_is_active();
static bool debug_is_active();
static bool info_is_active();
static bool warning_is_active();
static bool error_is_active();
static void write_as_trace(const std::string& msg);
static void write_as_debug(const std::string& msg);
static void write_as_info(const std::string& msg);
static void write_as_warning(const std::string& msg);
static void write_as_error(const std::string& msg);
};
#define LOG_TRACE(X) {if(Log::trace_is_active()){std::ostringstream o__;o__<<X;Log::write_as_trace(o__.str());}}
#define LOG_DEBUG(X) {if(Log::debug_is_active()){std::ostringstream o__;o__<<X;Log::write_as_debug(o__.str());}}
#define LOG_INFO(X) {if(Log::info_is_active()){std::ostringstream o__;o__<<X;Log::write_as_info(o__.str());}}
#define LOG_WARNING(X) {if(Log::warning_is_active()){std::ostringstream o__;o__<<X;Log::write_as_warning(o__.str());}}
#define LOG_ERROR(X) {if(Log::error_is_active()){std::ostringstream o__;o__<<X;Log::write_as_error(o__.str());}}
那么用法就很简单明了:
//...
LOG_WARNING("The variable x = " << x << " is out of range");
//...
LOG_DEBUG("The inverse of the matrix is inv(m) = " << std::endl << inv(m) << std::endl);
编辑: 我注意到 do{...}while(false)
的解决方案更好,因为我的解决方案后跟 ;
是两条指令,不能用于循环或条件而不在 {
和 }
之间写入它。
现在我可以改进我的代码了。 :-)
该应用程序有一个日志系统,允许在运行时启用或禁用其模块的日志记录功能。日志命令接受输入流(作为 "sprintf" 的安全替代方案;几乎没有比调试系统导致崩溃更烦人的情况了。
问题是,如果我执行以下操作:
logger.Trace << "Requests pending:" << buffer.findRequests();
和findRequests()
具有很高的计算复杂度,即使禁用模块的Trace日志级别,也会在Trace operator<<
方法中被拒绝之前执行搜索(在组装流时) .
明显的替代方法是在代码中乱加:
if(logger.Trace.Enabled()) logger.Trace << ...
不漂亮,也不舒服。我可以使用 if
的宏或使用 &&
短路的宏来替换它,这样会更好一些(可以用作 RValue,遵循 Stream 哲学 returns bool false on disabled流):
#define TRACE if(logger.Trace.Enabled()) logger.Trace
#define TRACE dummyLogVar = logger.Trace.Enabled() && logger.Trace
都不是特别漂亮或安全。一位同事建议关闭:
logger.Trace([&](f){f << "Requests pending:" << buffer.findRequests();});
.Trace
仅在启用该级别时才会评估闭包。从逻辑上讲,这很好,但在语法上绝对可怕。乱七八糟的输入:logger.Trace([&](f){f <<
... ;});
数百次?
有没有更简洁、安全和舒适的方法来防止对流进行评估?
确实可以使用宏,但是你需要类似函数的宏,将输出作为参数,并且你需要确保它是单个语句。
第二部分,确保宏体是单个语句,很简单,通常使用 do { ... } while (false)
。
使用宏的参数也不难,只要参数中没有逗号即可。逗号限制包括在宏参数中使用带有自己参数的函数调用,预处理器非常愚蠢,在宏参数中使用 any 逗号作为宏参数的分隔符.
在不考虑逗号限制的最简单形式中,宏可能看起来像
#define TRACE(output) \
do \
{ \
if (logger.Trace.Enabled()) \
{ \
logger.Trace << output; \
} \
} while (false)
注意while (false)
后没有分号。
然后你就可以像这样使用它了
TRACE("Requests pending:" << buffer.findRequests());
do { ... } while (false)
部分很可能会被编译器优化掉,只剩下一个简单的 if
检查。如果 logger.Trace.Enabled()
returns false
那么除了检查之外什么都不应该发生。
如果您的编译器支持 C++11 或更高版本,它应该支持 variadic macros,这应该可以帮助您克服宏参数中逗号的限制。
使用可变宏,宏将如下所示:
#define TRACE(...) \
do \
{ \
if (logger.Trace.Enabled()) \
{ \
logger.Trace << __VA_ARGS__; \
} \
} while (false)
我解决了那个问题。最后,我用这个接口和相关的宏创建了一个 class Log
:
class Log
{
public:
static bool trace_is_active();
static bool debug_is_active();
static bool info_is_active();
static bool warning_is_active();
static bool error_is_active();
static void write_as_trace(const std::string& msg);
static void write_as_debug(const std::string& msg);
static void write_as_info(const std::string& msg);
static void write_as_warning(const std::string& msg);
static void write_as_error(const std::string& msg);
};
#define LOG_TRACE(X) {if(Log::trace_is_active()){std::ostringstream o__;o__<<X;Log::write_as_trace(o__.str());}}
#define LOG_DEBUG(X) {if(Log::debug_is_active()){std::ostringstream o__;o__<<X;Log::write_as_debug(o__.str());}}
#define LOG_INFO(X) {if(Log::info_is_active()){std::ostringstream o__;o__<<X;Log::write_as_info(o__.str());}}
#define LOG_WARNING(X) {if(Log::warning_is_active()){std::ostringstream o__;o__<<X;Log::write_as_warning(o__.str());}}
#define LOG_ERROR(X) {if(Log::error_is_active()){std::ostringstream o__;o__<<X;Log::write_as_error(o__.str());}}
那么用法就很简单明了:
//...
LOG_WARNING("The variable x = " << x << " is out of range");
//...
LOG_DEBUG("The inverse of the matrix is inv(m) = " << std::endl << inv(m) << std::endl);
编辑: 我注意到 do{...}while(false)
的解决方案更好,因为我的解决方案后跟 ;
是两条指令,不能用于循环或条件而不在 {
和 }
之间写入它。
现在我可以改进我的代码了。 :-)