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 イディオムを介してブール値に評価する必要があります。たとえば、IOstream はそれを行い、ストリームの状態がまだ OK かどうかを知らせます。

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;は、変換演算子を使用するだけです。

explicit本当に最近のバージョンの GCC または Clang を使用している場合は、既に変換演算子にアクセスできる可能性があるため、 safe-bool イディオムは必要ないことに注意してください。

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 に答える