C宏与类型说明符无关

时间:2012-09-04 08:36:48

标签: c

如何编写独立于类型说明符的标准C(C89或C99兼容)宏?

具体来说,我想写一个宏来比较两个可以是有符号类型或无符号类型的数字,8位,16位,32位,64位。

我正在寻找的是这样的:

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

                .
                .
                .

           c = min(a, b);

                .
                .
                .

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

即,相同的宏'min'可用于比较两个整数 - 任何宽度,有符号或无符号。

我知道有效的基本宏可以这样写:

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

但是这涉及到两次评估表达式'x'和'y'的副作用,这是我不想要的。

为了提高稳健性,表达式只能被评估一次。

直到现在,我的方法似乎是这样的:

          #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);
    
  3. 第二种方法是否存在任何副作用,在某些情况下可能产生意外结果,特别是关于“短”和“字符”类型 - 签名或未签名。

    欢迎任何改进或建议。

    已编辑:

    用户如何指定以下功能:

                'min_uchar'
    

    他/她必须写:

                 r = min(uchar, p, q);
    

    还有其他方法来编写函数'uchar',以便用户只需编写:

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

    在这种情况下,我不能将该函数命名为'min_unsigned char',因为它在C中是不允许的。 有没有其他方法或用户必须在宏'min'中指定类型为'uchar'而不是'unsigned char'。

    2012-09-10更新:

    谢谢大家的帮助。由于我需要接受答案,我接受 Eitan T 的答案,但我感谢各位的回复。感谢。

4 个答案:

答案 0 :(得分:2)

如果您使用的是gcc statement expressions,那么您还可以使用gcc extension keyword typeof(或__typeof__):

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

C11使用_Generic关键字引入了类型通用表达式:

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的旧版本中有效的语法。

答案 1 :(得分:2)

当然你的宏可以独立于类型说明符,这是使宏如此强大的原因之一。你可以:

  1. 传递变量名称和类型,让​​宏处理类型。
  2. 传递变量名而不类型,并假设宏对该变量执行合法操作,无论其类型如何。
  3. 您的实际问题是,在第一种形式中,您希望宏返回一个表达式,但在其中您声明变量和什么不是。我相信因为这个,你把花括号放在宏中,现在它扩展为:({ bla bla }),这会在ISO C99中给你以下错误信息:

      

    ISO C forbids braced-groups within expressions

    不幸的是,在ISO C99中,你不能都有一个表达式并在其中声明临时变量。这就是函数的用途。

    所以要么使用你描述的第二种形式,那就是将“result”变量传递给宏:

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

    或使用内联函数。

    P.S:
    此外,在编写C宏时,thisthis值得阅读。

答案 2 :(得分:1)

Macrofunctions无法执行类型检查。如果你想对指定类型的操作数进行操作(如果参数类型不兼容,则强制编译器警告),你必须使用一个函数(如果考虑性能,可能会inline)。

您的方法很讨人喜欢,但带表达式的带括号的组是gcc扩展(不是标准的),因此标准编译器无法将您的宏函数作为具有特定值的表达式进行求值。

此外,您的\字符后面有一个空格,这会导致语法错误。

/* GCC-compatible */
#define min(t, x, y) ({ \
    t __tmp1 = (x); \
    t __tmp2 = (y); \
    __tmp1 < __tmp2 ? __tmp1 : __tmp2; \
})

答案 3 :(得分:0)

由于所有解决方案似乎都需要指定类型,因此您可以调用特定的宏来进行比较。您还可以概括为两个宏,并为每个签名/未签名使用最大类型:

#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)

请务必仅比较相同标牌的类型。