短路流

时间:2016-05-20 09:06:44

标签: c++ c++11 conditional iostream

该应用程序有一个日志记录系统,允许在运行时启用或禁用其模块的日志记录功能。日志命令接受输入流(作为" sprintf"的安全替代品;除了调试系统导致崩溃之外,几乎没有任何烦人的情况。

问题是,如果我执行以下内容:

 logger.Trace << "Requests pending:" << buffer.findRequests();

findRequests()计算复杂度很高,即使禁用模块的跟踪日志级别,也会在跟踪{{1}内部被拒绝之前执行搜索(在组合流时)方法。

显而易见的替代方案是将代码丢弃:

operator<<

它不漂亮而且不舒服。我可以使用 if(logger.Trace.Enabled()) logger.Trace << ... 替换它,或者使用if短路替换它,稍微更好(可以用作RValue,后面的Stream哲学在禁用的流上返回bool false):< / p>

&&

两者都不是特别漂亮或安全。一位同事建议关闭:

  #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 <<数百次?

是否有更简洁,安全和舒适的方法来阻止评估流?

2 个答案:

答案 0 :(得分:8)

确实可以使用宏,但是你需要类似函数的宏,它将输出作为参数,你需要确保它是一个单独的语句。

第二部分,确保宏体是单个语句,很容易,通常使用#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()检查。如果false返回#define TRACE(...) \ do \ { \ if (logger.Trace.Enabled()) \ { \ logger.Trace << __VA_ARGS__; \ } \ } while (false) ,则除了该检查之外不会发生任何事情。

如果你有一个能够使用C ++ 11或更高版本的编译器,它应该支持variadic macros,它可以帮助你克服宏参数中逗号的限制。

使用可变参数宏,宏看起来像这样:

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Registering ContextLoaderListener -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Registering DispatcherServlet -->
<servlet>
    <servlet-name>myapp</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>myapp</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

答案 1 :(得分:0)

我解决了这个问题。 Finnaly,我用这个接口和相关的宏创建了一个类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)的解决方案更好,因为我的解决方案后跟;是两条指令,无法在循环或条件中使用无需在{}之间书写。 现在我可以改进我的代码了。 : - )