web-dev-qa-db-ja.com

アサートするメッセージを追加する

こんにちは!

ステートメントをアサートするためにカスタムメッセージを追加する方法を探しています。この質問を見つけました assertにカスタムメッセージを追加しますか? しかし、メッセージはそこで静的です。私はこのようなことをしたい:

assert((0 < x) && (x < 10), std::string("x was ") + myToString(x));

アサーションが失敗すると、通常の出力に加えて、たとえば「x was 100」が必要になります。

39
tauran

あなたはここで不運です。最善の方法は、独自のassertマクロを定義することです。

基本的には次のようになります。

#ifndef NDEBUG
#   define ASSERT(condition, message) \
    do { \
        if (! (condition)) { \
            std::cerr << "Assertion `" #condition "` failed in " << __FILE__ \
                      << " line " << __LINE__ << ": " << message << std::endl; \
            std::terminate(); \
        } \
    } while (false)
#else
#   define ASSERT(condition, message) do { } while (false)
#endif

これは、デバッグなしマクロASSERTが定義されていない場合にのみ、NDEBUGマクロを定義します。

次に、次のように使用します。

ASSERT((0 < x) && (x < 10), "x was " << x);

"x was "xを明示的に文字列化する必要がないため、これは使用よりも少し簡単です。これはマクロによって暗黙的に行われます。

69
Konrad Rudolph

独自のルーチンを作成せずにメッセージを含めるには、いくつかの古いトリックがあります。

最初はこれです:

bool testbool = false;
assert(("this is the time", testbool));

もあります:

bool testbool = false;
assert(testbool && "This is a message");

内側の括弧式の結果は 'testbool'の値であるため、最初のものが機能します。文字列の値はゼロ以外になるため、2番目の方法は機能します。

16
Cameron

より良い代替方法は、デバッガーが失敗したときにアサートで停止するように指示することです。その後、x値だけでなく、呼び出しスタックを含むその他の情報を調べることができます。おそらく、これはあなたが本当に探しているものです。ここにサンプル実装が記載されています C++でプログラミングする場合、一部のメソッドがクラスにまだ実装されていないことを共同プログラマに示す方法

9
Alsk
#define ASSERT_WITH_MESSAGE(condition, message) do { \
if (!(condition)) { printf((message)); } \
assert ((condition)); } while(false)
6
AlcubierreDrive

完全を期すために、C++でマクロ実装をアサートするドロップイン2ファイルを公開しました。

_#include <pempek_assert.h>

int main()
{
  float min = 0.0f;
  float max = 1.0f;
  float v = 2.0f;
  PEMPEK_ASSERT(v > min && v < max,
                "invalid value: %f, must be between %f and %f", v, min, max);

  return 0;
}
_

次のプロンプトが表示されます。

_Assertion 'v > min && v < max' failed (DEBUG)
  in file e.cpp, line 8
  function: int main()
  with message: invalid value: 2.000000, must be between 0.000000 and 1.000000

Press (I)gnore / Ignore (F)orever / Ignore (A)ll / (D)ebug / A(b)ort:
_

どこ

  • (I)無視:現在のアサーションを無視します
  • (F)oreverを無視:アサーションが起動されたファイルと行を記憶し、プログラムの残りの実行では無視します
  • 無視(A)ll:残りのすべてのアサーション(すべてのファイルと行)を無視します
  • (D)ebug:接続されている場合はデバッガーに侵入し、それ以外の場合はabort()(Windowsでは、システムはユーザーにデバッガーを接続するように要求します)
  • A(b)ort:abort()をすぐに呼び出します

あなたはそれについてもっと知ることができます:

お役に立てば幸いです。

2
Gregory Pakosz

はい、これは可能です。

better_assert((0 < x) && (x < 10), std::string("x was ") + myToString(x));のような式を有効にするには、対応するマクロを次の形式で持つ必要があります。

_#define better_assert(EXPRESSION, ... ) ((EXPRESSION) ? \
(void)0 : print_assertion(std::cerr, \
"Assertion failure: ", #EXPRESSION, " in File: ", __FILE__, \ 
" in Line: ", __LINE__ __VA_OPT__(,) __VA_ARGS__))
_

ここで、_print_assertion_はアサーションを実行するプロキシ関数です。 EXPRESSIONが評価されると、false、すべてのデバッグ情報、___VA_ARGS___が_std::cerr_にダンプされます。この関数は任意の数の引数を受け取るため、可変個引数テンプレート関数を実装する必要があります。

_template< typename... Args >
void print_assertion(std::ostream& out, Args&&... args)
{
    out.precision( 20 );
    if constexpr( debug_mode )
    {
        (out << ... << args) << std::endl;
        abort();
    }
}
_

前の実装では、式_(out << ... << args) << std::endl;_はC++ 17のfold式を使用します( https://en.cppreference.com/w/cpp/language/fold );定数式_debug_mode_は、渡されるコンパイルオプションに関連しており、次のように定義できます。

_#ifdef NDEBUG
    constexpr std::uint_least64_t debug_mode = 0;
#else
    constexpr std::uint_least64_t debug_mode = 1;
#endif
_

また、式if constexpr( debug_mode )は、C++からインポートされたif( https://en.cppreference.com/w/cpp/language/if )がconstexprを使用することにも言及する価値があります。 17。

すべてをまとめるために、次のものがあります。

_#ifdef NDEBUG
    constexpr std::uint_least64_t debug_mode = 0;
#else
    constexpr std::uint_least64_t debug_mode = 1;
#endif

template< typename... Args >
void print_assertion(std::ostream& out, Args&&... args)
{
    out.precision( 20 );
    if constexpr( debug_mode )
    {
        (out << ... << args) << std::endl;
        abort();
    }
}
#ifdef better_assert
#undef better_assert
#endif
#define better_assert(EXPRESSION, ... ) ((EXPRESSION) ? (void)0 : print_assertion(std::cerr, "Assertion failure: ",  #EXPRESSION, " in File: ", __FILE__, " in Line: ",  __LINE__ __VA_OPT__(,) __VA_ARGS__))
_

その使用法を示す典型的なテストケースは次のとおりです。

_double const a = 3.14159265358979;
double const b = 2.0 * std::asin( 1.0 );
better_assert( a==b, " a is supposed to be equal to b, but now a = ", a, " and b = ", b );
_

これにより、次のようなエラーメッセージが生成されます。

_Assertion failure: a==b in File: test.cc in Line: 9 a is supposed to be equal to b, but now a = 3.1415926535897900074 and b = 3.141592653589793116
[1]    8414 abort (core dumped)  ./test
_

完全なソースコードは、このリポジトリで入手できます。 https://github.com/fengwang/better_assert

0
Feng Wang

Kondrad Rudolphの答えを拡張する:

#include <iostream>

#ifdef NDEBUG
#define assert(condition, message) 0
#else
#define assert(condition, message)\
   (!(condition)) ?\
      (std::cerr << "Assertion failed: (" << #condition << "), "\
      << "function " << __FUNCTION__\
      << ", file " << __FILE__\
      << ", line " << __LINE__ << "."\
      << std::endl << message << std::endl, abort(), 0) : 1
#endif

void foo() {
   int sum = 0;
   assert((sum = 1 + 1) == 3, "got sum of " << sum << ", but expected 3");
}

int main () {
   foo();
}

出力は...

Assertion failed: ((sum = 1 + 1) == 3), function foo, file foo.cpp, line 13.
got sum of 2, but expected 3
zsh: abort      ./a.out

これは、std :: assertマクロが追加のユーザー定義メッセージを使用してシステムに出力するものに似ています

0
solstice333