Skip to content

Instantly share code, notes, and snippets.

@ykst
Created January 28, 2015 14:04
Show Gist options
  • Save ykst/4930d8fef81edfee8349 to your computer and use it in GitHub Desktop.
Save ykst/4930d8fef81edfee8349 to your computer and use it in GitHub Desktop.
いつものASSERTマクロ

いつものASSERTマクロ

gccな環境でいつも使ってるASSERTマクロ。printfが頼みの組み込みな環境で特に威力を発揮します。

#ifndef unlikely
# ifdef __builtin_expect
# define unlikely(x) __builtin_expect((x), 0)
# else
# define unlikely(x) (x)
# endif
#endif

#define ERROR(fmt, ...) do { \
  fprintf(stderr, "\x1b[1;31mERROR:%s:%s:%d: " fmt "\x1b[0m",  __BASE_FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
} while (0)

#define ASSERT(b,action) ({ int __b = (int)(b); if(unlikely(!(__b))){ ERROR("failed (%s)\n", #b); action; } __b;})

使い方

// ASSERT(<条件式>, <失敗時アクション>)
ASSERT(maybe_failed() == SUCCESS, goto error);

この例だと失敗したときに↓を吐いてgoto errorが実行されます。

ERROR :main.c:main:17: failed (maybe_failed() == SUCCESS)

actionのところにはもちろんブロック式も書けます。

ASSERT(maybe_failed() == SUCCESS, { do_something(); goto error; });

ポイント

メリット

  • 失敗したときに任意のアクションを記述出来る
  • 問題のあるソースの位置と評価式がプリントされて一目瞭然
  • 失敗するとコンソールが赤くなるので危機感を煽れる
  • __builtin_expectで最適化している(気分になれる)
  • コードの見た目が統一されて厳かになる
  • returnコード方式でエラーを返していくとバックトレースが安く手に入る

デメリット

  • 評価式や関数名がリテラルで埋め込まれるので、バイナリが余分に大きくなる →リリースビルドでは詳細をダンプしない等で対応
  • C0カバレッジで異常系がスルーされる傾向 →ある意味ではメリット

これ系では失敗した時の挙動を決め打ちしてしまうマクロを良く見かけるのですが、文をまるっと渡す事で柔軟性と規律を両立するこの書き方の方が個人的には好みです。

失敗する可能性のある処理の全てでこうした書き方を使うように習慣付けると、コードの防御力はかなり高まると思います。

(ちなみにそのままASSERTだと他のフレームワークと衝突したりリリースビルドで消されそうに見えたりと、名前については一考の余地有り。)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment