ラッパーを使用して、引数を検証できます。
#define restrictedFunction(x) do { \
static_assert((x) == INPUT || (x) == OUTPUT); \
assert(!strcmp(#x, "INPUT") || !strcmp(#x, "OUTPUT")); \
restrictedFunction(x); \
} while(0)
ノート:
restrictedFunction()
これは、を返すことを前提としvoid.
ています。実際に使用する値を返す場合は、gccの複合ステートメントhttp://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.htmlのようなものが必要になります。または、C ++では機能しないように見えるので、私が忘れ続けている使用法( Cコードの「:-!!」とは何ですか?をBUILD_BUG_ON_ZERO
参照)を使用できます。
- 「
do ... while(0)
セミコロンを飲み込む」ことです。ここではあまり関係ありません。
static_assert()
コンパイル時のアサートです。利用可能な多くのバリエーションがあります。自分の手元がない場合は、 https://stackoverflow.com/a/9059896/318716へのリンクを次に示します。
assert()
標準のランタイムアサーションです。
- gcc 4.1.2を使用すると、私のバージョンでは、2つが;に置き換えられたときに
static_assert(),
、ランタイムをassert()
コンパイル時のアサーションに置き換えることができます。以下の例を参照してください。私はこれを他のコンパイラでテストしていません。!strcmp()
==
x
最初の4つの参照はコンパイル時にのみ使用されるため、マクロ展開で1回だけ使用されます。
実際に関数を定義するときは、次のように、マクロ展開を無効にするために括弧を追加する必要があります。
void (restrictedFunction)(int x){ ... }
また、コードに特殊なケース(コードにはない)がありrestrictedFunction()
、引数を指定して呼び出す必要がある場合は、次のfoo,
ように記述する必要があります。
(restrictedFunction)(foo);
これは、標準ライブラリ関数のラッパーを配置する完全な例ですexit()
。
#include <stdlib.h>
#define CONCAT_TOKENS(a, b) a ## b
#define EXPAND_THEN_CONCAT(a,b) CONCAT_TOKENS(a, b)
#define ASSERT(e) enum{EXPAND_THEN_CONCAT(ASSERT_line_,__LINE__) = 1/!!(e)}
#define ASSERTM(e,m) enum{EXPAND_THEN_CONCAT(m##_ASSERT_line_,__LINE__)=1/!!(e)}
#define exit(x) do { \
ASSERTM((x) == EXIT_SUCCESS || (x) == EXIT_FAILURE, value); \
ASSERTM(#x == "EXIT_SUCCESS" || #x == "EXIT_FAILURE", symbol); \
exit(x); \
} while(0)
int main(void) {
exit(EXIT_SUCCESS); // good
exit(EXIT_FAILURE); // good
exit(0); // bad
exit(3); // doubly bad
}
コンパイルしようとすると、次のようになります。
gcc foo.c -o foo
foo.c: In function 'main':
foo.c:17: error: enumerator value for 'symbol_ASSERT_line_17' is not an integer constant
foo.c:18: warning: division by zero
foo.c:18: error: enumerator value for 'value_ASSERT_line_18' is not an integer constant
foo.c:18: error: enumerator value for 'symbol_ASSERT_line_18' is not an integer constant