1

タイプ指定子に依存しない標準C(C89またはC99準拠)マクロを作成するにはどうすればよいですか?

具体的には、符号付き型と符号なし型の2つの数値、8ビット、16ビット、32ビット、64ビットを比較するマクロを作成したいと思います。

私が探しているのは、次のようなものです。

           int c,  a , b;
           uint16_t p, q, r;

                .
                .
                .

           c = min(a, b);

                .
                .
                .

           r = min(p, q);
                .
                .
                .

つまり、同じマクロ'min'を使用して、2つの整数(符号付きまたは符号なしの任意の幅)を比較できます。

動作する基本的なマクロは次のように記述できることを私は知っています。

          #define min(x, y)   (x) < (y) ? (x) : (y)

しかし、これには、式'x'と'y'を2回評価するという副作用が含まれますが、これは望ましくありません。

堅牢性を確保するために、式は1回だけ評価する必要があります。

今まで、私のアプローチは次のように見えます:

          #define min(type, x, y)  ( {  \ 
                  type __tmp1 = (x);                      \
                  type __tmp2 = (y);                      \
                  __tmp1 < __tmp2 ? __tmp1 : __tmp2; } )

          int a, b, c;
          uint16_t p, q, r;

                   .
                   .
                   .

          c = min(int, a, b);

Cコンパイラはエラーをスローします: "expecting; found("、および他の多くの。

このマクロを書き直して、使用法が次の形式になるようにするにはどうすればよいですか。

          <result> = min( <type>, <parm1>, <parm2> );

'min'マクロは、その使用法が次のように記述できるようです。

           min( <result>, <type>, <parm1>, <parm2> );  

しかし、最初の形があればいいのですが。

2012-09-04のアップデート#1:

皆さん、ありがとうございます。あなたの回答/コメントで提供されたすべての情報は、私に可能性のより広い視点を与えてくれました。ええと、私はgccを使用していません。また、私のコンパイラもC11仕様をサポートしていないので、利用できると思われるオプションは次のとおりです。

  1. 次の形式のマクロを使用します。

           // min( <result>, <type>, <parm1>, <parm2> ); 
    
           #define min(result, type, x, y)  {                   \ 
                  type __tmp1 = (x);                      \
                  type __tmp2 = (y);                      \
                  result = __tmp1 < __tmp2 ? __tmp1 : __tmp2; } 
    
            int p, q, r;
                .
                .
                .
    
            min(r, int, p, q);
    
  2. 他のアプローチは:

          inline char min_char(char x, char y)       { return x < y ? x : y; }
          inline short min_short(short x, short y)    { return x < y ? x : y; }
          inline int min_int(int x, int y)          { return x < y ? x : y; }
          inline long min_long(long x, long y)      { return x < y ? x : y; }
    
          inline unsigned char min_uchar(unsigned char x, unsigned char y)    { return x < y ? x : y; }  
          inline unsigned short min_ushort(unsigned short x, unsigned short y) { return x < y ? x : y; }   
          inline unsigned int min_uint(unsigned int x, unsigned int y)       { return x < y ? x : y; }  
          inline unsigned long min_ulong(unsigned long x, unsigned long y)   { return x < y ? x : y; }  
    
    
          #define   min(type, x, y)    min_##type(x, y)
    
    
          int p, q, r;
               .
               .
               .
    
          r = min(int, p, q);
    

2番目のアプローチの副作用はありますか?それは、特に「short」および「char」タイプ(符号付きまたは符号なし)に関して、場合によっては予期しない結果をもたらす可能性があります。

改善や提案は大歓迎です。

編集:

ユーザーは以下の関数をどのように指定しますか?

            'min_uchar'

彼/彼女は書かなければなりません:

             r = min(uchar, p, q);

ユーザーが簡単に書くことができるように、関数'uchar'を書く他の方法はありますか?

             r = min(unsigned char, p, q);

この場合、関数に「min_unsigned char」という名前を付けることはできません。これは、Cでは許可されていないためです。他の方法や、ユーザーがマクロ「min」で「unsignedchar」ではなく「uchar」として型を指定する必要がありますか。 '。

2012-09-10の更新:

みんな助けてくれてありがとう。答えを受け入れる必要があるので、エイタンTの答えを受け入れていますが、皆さんからの返事に感謝します。ありがとう。

4

4 に答える 4

3

もちろん、マクロは型指定子から独立させることができます。これは、マクロを非常に強力にするものの1つです。次のいずれかを実行できます。

  1. 変数名と型を渡し、マクロに型を処理させます。
  2. タイプなしで変数名を渡し、タイプに関係なく、マクロがその変数に対して有効な操作を実行すると想定します。

実際の問題は、最初の形式ではマクロが式を返すことを期待しているのに、その中で変数などを宣言していることです。そのため、中括弧をマクロに入れると、次のように展開される({ bla bla })と思います。これにより、ISOC99で次のエラーメッセージが表示されます。

ISO C forbids braced-groups within expressions

残念ながら、ISO C99では、式を含めることも、その中に一時変数を宣言することもできません。これが機能の目的です。

したがって、説明した2番目の形式を使用します。つまり、「結果」変数をマクロに渡します。

#define min(type_, x_, y_, result_)                    \
    do {                                               \
        type_ __tmp1 = (x_);                           \
        type_ __tmp2 = (y_);                           \
        result_ = (__tmp1 < __tmp2 ? __tmp1 : __tmp2); \
    } while(0)

または、代わりにインライン関数を使用してください。

PS:
また、これこれは、Cマクロを書くときの良い習慣のために読む価値があります。

于 2012-09-04T09:25:41.587 に答える
2

gccステートメント式を使用している場合は、gcc拡張キーワードtypeof(または__typeof__)を使用することもできます。

      #define min(x,y)  ( {                                \ 
              typeof(x) __tmp1 = (x);                      \
              typeof(y) __tmp2 = (y);                      \
              __tmp1 < __tmp2 ? __tmp1 : __tmp2; } )

_GenericC11は、キーワードを使用して型ジェネリック式を導入します。

int min_int(int x, int y) { return x < y ? x : y; }
long min_long(long x, long y) { return x < y ? x : y; }
#define min(x,y) _Generic((x + y),                        \
                          int: min_int,                   \
                          long: min_long)(x, y)

マクロ呼び出しで型を記述しても構わないと思っている場合は、次を使用できます。

int min_int(int x, int y) { return x < y ? x : y; }
long min_long(long x, long y) { return x < y ? x : y; }
#define min(type,x,y) min_ ## type(x, y)

これは、Cのレガシーバージョンで有効な唯一の構文です。

于 2012-09-04T08:59:11.807 に答える
1

マクロ関数はタイプチェックを実行できません。指定されたタイプのオペランドを操作する場合(および引数に互換性のないタイプがある場合はコンパイラーに警告を強制する場合)、関数を使用する必要があります(inlineパフォーマンスについて考える場合)。

あなたのメソッドは楽しいですが、式を含むブレースグループはgcc拡張(標準ではありません)であるため、標準コンパイラはマクロ関数を特定の値を持つ式として評価できません。

また、文字の後にスペースが\あり、構文エラーが発生します。

/* GCC-compatible */
#define min(t, x, y) ({ \
    t __tmp1 = (x); \
    t __tmp2 = (y); \
    __tmp1 < __tmp2 ? __tmp1 : __tmp2; \
})
于 2012-09-04T08:41:53.193 に答える
0

すべてのソリューションでタイプを指定する必要があるように思われるため、特定のマクロを呼び出して比較を行うこともできます。また、2つのマクロだけに一般化して、符号付き/符号なしのそれぞれに最大の型を使用することもできます。

#define smin(x_, y_, result_)                     \
  do {                                            \
    int64_t __tmp1 = (x_);                        \
    int64_t __tmp2 = (y_);                        \
    result_ = __tmp1 < __tmp2 ? __tmp1 : __tmp2;  \
  } while(0)
#define umin(x_, y_, result_)                     \
  do {                                            \
    uint64_t __tmp1 = (x_);                       \
    uint64_t __tmp2 = (y_);                       \
    result_ = __tmp1 < __tmp2 ? __tmp1 : __tmp2;  \
  } while(0)

同じ看板の種類のみを比較するようにしてください。

于 2012-09-04T17:34:39.163 に答える