6

引数が配列であることを ac マクロでコンパイル時に検証する方法はありますか?

たとえば、この 2 つのマクロでは次のようになります。

#define CLEAN_ARRAY(arr) \
    do { \
        bzero(arr, sizeof(arr)); \
    } while (0)

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

CTC(X)macroを使用して何かを試しましたarrが、配列でない場合に検証/警告する方法が見つかりませんでした。

4

7 に答える 7

12

未定義の動作を呼び出さない純粋な C のソリューションを次に示します。

#define IS_INDEXABLE(arg) (sizeof(arg[0]))
#define IS_ARRAY(arg) (IS_INDEXABLE(arg) && (((void *) &arg) == ((void *) arg)))

値が配列であることを確認する必要がある場合 (そうでない場合はコンパイル時エラーが発生します)、次のように列挙型ステートメント (または静的変数) の初期化子として単純に使用できます。

static int __ ## arg ## _is_array = IS_ARRAY(arg); // works for an array, fails for pointer.

VLAで何が起こるかは完全にはわかりませんが、少し遊んでみると、その答えがかなり早く見つかるはずです.


古い答え:

これは C (および GCC) とタグ付けされているため、ここで解決策を試みます。

#define IS_ARRAY(arg) __builtin_choose_expr(__builtin_types_compatible_p(typeof(arg[0]) [], typeof(arg)), 1, 0)

C11 の_Generic機能 &を使用した別のソリューションtypeof:

#define IS_ARRAY(arg) _Generic((arg),\
    typeof(arg[0]) *: 0,\
    typeof(arg[0]) [sizeof(arg) / sizeof(arg[0])]: 1\
)

基本的には、GCC の凝った機能を使用して、引数の型が引数の要素の型の配列と互換性があるかどうかを判断するだけです。0 または 1 が返されます。必要に応じて、0 をコンパイル時エラーを発生させるものに置き換えることができます。

于 2013-05-28T15:13:08.847 に答える
1

(C++の場合)VS2010の現在のクリアマクロは次のとおりです。

#define CLEAR(v)    do { __pragma(warning(suppress: 4127 4836)) typedef std::remove_reference< decltype(v)>::type T; static_assert( std::is_pod<T>::value || (__has_trivial_constructor(T) && __has_trivial_destructor(T)), "must not CLEAR a non-POD!" ); static_assert( !std::is_pointer<T>::value, "pointer passed to CLEAR!" );  memset(&(v), 0, sizeof(v)); } while(0)

type_traits ヘッダーの内容を使用してバリアントを作成できます。

説明付きのフォーマットされたバージョン:

#define CLEAR(v) \
do { \ 
    __pragma(warning(suppress: 4127 4836)) \
    typedef std::remove_reference< decltype(v)>::type T; \
    static_assert( \
        std::is_pod<T>::value \
        || (__has_trivial_constructor(T) && __has_trivial_destructor(T)), \
        "must not CLEAR a non-POD!" ); \
     static_assert( !std::is_pointer<T>::value, "pointer passed to CLEAR!" ); \
     memset(&(v), 0, sizeof(v)); \
  } while(0)

if/else を含むすべての場所で本物の関数のように使用できるようにするための外側の do-while。

左辺値で機能するように Remove_reference が必要です。decltype だけでは int* と int*& が異なり、is_pointer は後者に対して false を報告します。

is_pod チェックは一般に適しています。追加の条件により struct A1 : A; が許可されます。A が POD で、A1 が POD メンバーのみを追加する場合の作業。is_pod の目的では false ですが、クリアするのも同じ意味です。

is_pointer チェックは、ポインタの間接化が間違っている場合や混乱して構造体のアドレスを渡した場合に、予想されるミスタイプをガードします。= NULL を使用してポインターをクリアしてください。;-)

は、それ以外の__pragma場合に発行される L4 警告を抑制するためにあります。

于 2013-05-28T14:58:38.693 に答える
1

私のコメントによると:

sizeof((x)[0])型が「インデックス可能な」型でない場合はエラーが発生しますが、たまたまポインターである場合は、喜んでそれを取ります。また、operator[]のタイプの がある場合x

C でこれを行うのは非常に困難ですが、C++ ではいくつかのテンプレート タイプのソリューションを使用できる場合があります (実際にその方法を知っているのは、それを試みたことがないか、テンプレートで同様のことをしたことがないためです)。

于 2013-05-28T15:10:33.783 に答える
1

私が知る限り、 の引数ARRAY_SIZEが実際に配列であることを保証する方法をまだ提供している人は誰もいません。

<編集>ポインターを拒否する配列サイズのマクロが
見つかりました 元の回答は次のとおりです: </編集>

これに使用するマクロは次のとおりです。

#define ASSERT_EXPR(condition,return_value) \
(((char(*)[(condition)?1:-1])0)?(return_value):(return_value))

原則:
0配列へのポインターにキャストされます (サイズが 1 (条件 true) またはマイナス 1 (条件 false、エラーを生成))。このヌルポインタは、三項演算子の条件として使用されます。常に 3 番目のオペランドのみを評価することはわかっていますが (null ポインターは false を意味します)、2 番目のオペランドも同様です。return_valueこのようにして、結果の型は の型と同じになりreturn_valueます。

これを使用して (およびリチャード J. ロス III の回答IS_ARRAYから)、セーフマクロを次のように定義できます。ARRAY_SIZE

#define IS_ARRAY(arg) __builtin_choose_expr(__builtin_types_compatible_p(typeof(arg[0]) [], typeof(arg)), 1, 0)
#define ARRAY_SIZE(x) ASSERT_EXPR(IS_ARRAY(x), (sizeof(x)/sizeof((x)[0])) )

Richard J. Ross IIIの他の 2 つのバリアントではうまく動作しませんでしたIS_ARRAYが、それは私 (または gcc) のせいかもしれません...

于 2017-08-24T15:48:27.913 に答える
-3

Cでは、これはうまくいくはずです:

#define VALIDATE_ARRAY(arr) (void)(sizeof((arr)[0]))

int *a, b;
int main() {
        VALIDATE_ARRAY(a);
        VALIDATE_ARRAY(b);
        return 0;
}

bは配列ではないため、このプログラムはコンパイルに失敗します。これは がb[0]無効であるためです。ポインターと配列を区別しません-それはできないと思います。

この形式のマクロは、関数内でのみ使用できます。関数の外で使用したい場合は、変更する必要があります (たとえば、extern配列を宣言するなど)。

于 2013-05-28T14:56:37.060 に答える