4

如果我有一个在发布模式下为空的日志记录类,并且有一个不执行任何操作的 ostream 运算符。它或多或少看起来像这样:

struct null_logger
{
    template<typename T> inline null_logger& operator<<(T) { return *this; }
};

我创建了一个简单的测试并将生成的程序集粘贴在下面:

const char* foo()
{
    return "hello";
}

int main()
{
    int i = 0;
    null_logger() << i << foo() << " this is a test";
    return 0;
}

老实说,我并不完全了解大会。根据@Als 的建议,我已经查找了call没有的语句。因此可以安全地假设,在发布模式下,对这个 ostream 运算符的任何调用都将被编译出来?

这是生成的程序集,使用g++ -O3 -S main.cpp

    .file   "main.cpp"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "hello"
    .text
    .p2align 4,,15
.globl _Z3foov
    .type   _Z3foov, @function
_Z3foov:
.LFB3:
    movl    $.LC0, %eax
    ret
.LFE3:
    .size   _Z3foov, .-_Z3foov
    .p2align 4,,15
.globl main
    .type   main, @function
main:
.LFB4:
    xorl    %eax, %eax
    ret
.LFE4:
    .size   main, .-main
    .section    .eh_frame,"a",@progbits
.Lframe1:
    .long   .LECIE1-.LSCIE1
.LSCIE1:
    .long   0x0
    .byte   0x1
.globl __gxx_personality_v0
    .string "zPR"
    .uleb128 0x1
    .sleb128 -8
    .byte   0x10
    .uleb128 0x6
    .byte   0x3
    .long   __gxx_personality_v0
    .byte   0x3
    .byte   0xc
    .uleb128 0x7
    .uleb128 0x8
    .byte   0x90
    .uleb128 0x1
    .align 8
.LECIE1:
.LSFDE1:
    .long   .LEFDE1-.LASFDE1
.LASFDE1:
    .long   .LASFDE1-.Lframe1
    .long   .LFB3
    .long   .LFE3-.LFB3
    .uleb128 0x0
    .align 8
.LEFDE1:
.LSFDE3:
    .long   .LEFDE3-.LASFDE3
.LASFDE3:
    .long   .LASFDE3-.Lframe1
    .long   .LFB4
    .long   .LFE4-.LFB4
    .uleb128 0x0
    .align 8
.LEFDE3:
    .ident  "GCC: (SUSE Linux) 4.3.4 [gcc-4_3-branch revision 152973]"
    .section    .comment.SUSE.OPTs,"MS",@progbits,1
    .string "Ospwg"
    .section    .note.GNU-stack,"",@progbits
4

1 回答 1

4

不是对您的问题的直接回答,但通常您会以另一种方式禁用日志记录:您根本不评估LOG_DBG通过短路之后的任何内容,这很简单:

#ifdef NDEBUG
#define LOG_CHECK false &&
#elseif
#define LOG_CHECK /*empty*/

#define LOG_DBG LOG_CHECK /*your_debug_logging_code*/

这样,您可以保证之后的所有内容LOG_DBG都是可以在发布模式下优化的死代码。

请注意,即使它们没有被编译出来,由于短路,它们也不会在运行时执行。

现在,要使其真正起作用,/*your_debug_logging_code*/需要评估为布尔值,例如通过 safe-bool 习惯用法。例如,IOstreams 会这样做,让您知道流的状态是否仍然正常。

class Logger{
  typedef void (Logger::*safe_bool)();
  void safe_bool_check(){}
public:
  // ...

  operator safe_bool() const{
    return check_state() ? &Logger::safe_bool_check : 0;
  }
};

当然,如果你只是想让布尔转换使短路工作,你可以简单地return 0;在转换运算符中。

请注意,如果您使用的是最新版本的 GCC 或 Clang,您可能已经可以访问explicit转换运算符,因此不需要 safe-bool idiom

explicit operator bool() const{ return check_state(); }

另一种方法可能是使用三元运算符进行短路。然而,两个“分支”需要是相同的类型,所以我们将在这里使用一个小技巧,一个可以从任何东西构造的简单类:

namespace log_detail{
struct log_check_helper{
  log_check_helper(){}
  template<class T>
  log_check_helper(T const&){}
};
}

#ifdef NDEBUG
#define LOG_CHECK true ? log_detail::log_check_helper() : 
#else
#define LOG_CHECK /*empty*/
#endif

#define LOG_DBG LOG_CHECK /*your_debug_logging_code*/

使用一个编译但不评估 Ideone的 IOstream 代码的工作示例。

于 2012-05-22T03:23:25.460 回答