3

dbgassert標準に似たマクロを作成しようとしていますassert。何を行うかに加えて、任意の数の追加パラメーター (デバッグ情報を含む)を出力assertしたいと考えています。dbgassert

私がこれまでに持っているものを以下にリストします。これは、この SO answerから改作されています。しかし、可変個引数テンプレートまたはマクロのいずれかでコードに問題があります。少なくとも 1 つの追加の引数 (OK 行) を使用すると、dbgassert期待どおりに機能します。しかし、追加の引数を指定しないと、コンパイルが失敗します (問題の行)。

可変個引数テンプレート プログラミング (タプルを出力する方法など) の経験はありますが、可変個引数マクロは使用したことがありません。

この可変引数マクロの組み合わせを記述する適切な方法を説明してください。

ところで、誰か#EXがマクロの魔法について説明してくれませんか? それは式を示し、gcc4.8.1 で動作します。普遍的にサポートされていますか?

ありがとう、


コード:

//corrected reserved identifier issue and assumption issues per comments
#include <cassert>
#include <iostream>
using namespace std;

template <typename ...Args>
void realdbgassert(const char *msg, const char *file, int line, Args ... args) {
  cout << "Assertion failed! \nFile " << file << ", Line " << line << endl 
       << "  Expression: " << msg << endl;
  std::abort();
}

#define dbgassert(EX,...) \
  (void)((EX) || (realdbgassert (#EX, __FILE__, __LINE__, __VA_ARGS__),0))

int main() {
  dbgassert(1>2,"right","yes"); //OK
  dbgassert(1>2,"right"); //OK.
  //dbgassert(1>2); //Problem. compile error: expected primary-expression before ')' token
                  //#define dbgassert(EX,...) (void)((EX) || (realdbgassert (#EX, __FILE__, __LINE__, __VA_ARGS__)^,0))
}

コードの元のバージョン。

#include <cassert>
#include <sstream>
using namespace std;

#ifdef __cplusplus
extern "C" {
#endif
extern void __assert (const char *msg, const char *file, int line);
#ifdef __cplusplus
};
#endif

template <typename ...Args>
void _realdbgassert(const char *msg, const char *file, int line, Args ... args) {
    stringstream os;
    //... do something
    __assert(msg,file,line);
}
#define dbgassert(EX,...) (void)((EX) || (_realdbgassert (#EX, __FILE__, __LINE__, __VA_ARGS__),0))

int main() {
  dbgassert(1==0,"right"); //Problem line: undefined reference to `__assert'
} 
4

5 に答える 5

5

あなたの問題は__VA_ARGS__、問題の場合に空の値です。そのため、プリプロセッサが展開するrealdbgassert(#EX, __FILE__, __LINE__, __VA_ARGS__)と、結果は未完成のパラメータ リストになりrealdbgassert("1>2", "foo.c", 42, )ます。の空の展開が原因で、パラメータ リストが正しく終了していないことに注意してください__VA_ARGS__

これを修正するには、何らかのトリックを使用する必要があります。最善の解決策は、__VA_ARGS__最後の無条件引数が含まれるように状況を微調整し、関数呼び出しの最後にオプションのものと一緒に渡すことです。標準 C であるため、これが最適です。

私が知っている他の修正は、言語の gcc 拡張です。詳細については、このgcc ドキュメント ページを##参照して__VA_ARGS__ください。

#define dbgassert(EX,...) \
  (void)((EX) || (realdbgassert (#EX, __FILE__, __LINE__, ## __VA_ARGS__),0))

Ps:
#プリプロセッサの文字列化演算子です。マクロ パラメータの値を文字列リテラルに変換します。つまり、貼り付ける代わりに貼り1>2付け"1>2"ます。

于 2014-07-25T20:33:33.293 に答える
1

メモとして、@ cmaster と @ ds27680 の解決策に加えて、末尾のコンマの問題を解決する別の方法を見つけることができました。余分な__VA_ARGS__コンマがあるため__VA_ARGS__、または関数呼び出しにパックしstd::tuple、タプル/結果を実際の関数のパラメーターとして使用できます。__VA_ARGS__1 つの有効な値 (つまり、空のタプルまたは nullary 関数の戻り値) にパックされるため、空の は問題になりません。これはコードの方が長いと思いますが、##.

上記の 2 つのケースは、以下のコードのdbgassertおよびdbgassert1マクロにそれぞれ示されています。

#include <cassert>
#include <iostream>
#include <tuple>
using namespace std;

template <typename ...Args>
string print_tuple(tuple<Args...> tp) {
  return ""; //print the tuple...
}

template <typename ...Args>
void realdbgassert(tuple<Args...> info,const char *msg, const char *file, int line) {
  cout << "Assertion failed! \nFile " << file << ", Line " << line << endl 
       << "  Expression: " << msg << endl
       << "  Info: " << print_tuple(info) << endl;
  std::abort();
}

#define dbgassert(EX,...) \
  (void)((EX) || (realdbgassert (std::tie(__VA_ARGS__),#EX,__FILE__, __LINE__),0))

void realdbgassert1(string info,const char *msg, const char *file, int line) {
  cout << "Assertion failed! \nFile " << file << ", Line " << line << endl 
       << "  Expression: " << msg << endl
       << "  Info: " << info << endl;
  std::abort();
}

template <typename ...Args>
string print_info(Args ... args) {
  return "";  //print stuff
}

#define dbgassert1(EX,...) \
  (void)((EX) || (realdbgassert1 (print_info(__VA_ARGS__),#EX,__FILE__, __LINE__),0))


int main() {
  dbgassert(1>2,"right","yes"); //OK
  dbgassert(1>2,"right"); //OK
  dbgassert(1>2); //OK now
  dbgassert1(1>2); //OK too
}
于 2014-07-26T04:22:53.650 に答える
1

の前にトークン貼り付け演算子 (##) を配置します __VA_ARGS__。これにより、空の__VA_ARGS__場合の前のコンマが削除されます。__VA_ARGS__

マクロは次のようになります。

#define dbgassert(EX,...) \
  (void)((EX) || (realdbgassert (#EX, __FILE__, __LINE__, ##__VA_ARGS__),0))

トークンの貼り付けは、正しく言及されている他のポスターの 1 つとして GNU CPP 拡張であることに注意してください ( https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.htmlを参照)。

MS コンパイラ (VS2010 でテスト済み) はトークンの貼り付けを必要とせず、__VA_ARGS__空の場合は末尾のコンマを削除するだけです。 .aspx

于 2014-07-25T21:01:17.067 に答える
0

assertを呼び出すことによって実装されていると想定しています__assert。これは、特定の実装がどのように機能するかということかもしれませんが、一般的に依存することはできません。

代わりに、ドキュメントに従ってください: 条件をテストし、失敗した場合は診断情報を標準エラーに出力してから、 を呼び出しますstd::abort

于 2014-07-25T17:55:32.027 に答える