【问题标题】:Comparing Types Of Names In C比较 C 中的名称类型
【发布时间】:2014-08-19 01:02:53
【问题描述】:

我正在尝试用 C(唉,不是 C++)编写一个宏,以捕获某些错误,特别是如果我传递了错误类型的名称。

例如,用

typedef int APLNELM;
typedef int APLRANK;
#define IsScalar(a) ((a) == 0)

APLNELM AplNelm = 0;
APLRANK AplRank = 0;

调用IsScalar (AplRank) 是正确的,因为Scalar 是一个等级概念,但IsScalar (AplNelm) 是错误的,因为Scalar 不是# 元素概念。

某个聪明的人能否找到一种方法来编写IsScalar 宏,以便它检查传递给它的名称的类型以确保它是APLRANK 类型?如果可以提供解决方案,请随意以任何等效方式重写原始示例。

【问题讨论】:

  • Typedef 声明不生成类型。 APLNELMAPLRANK 是完全相同的类型,并且都与int 完全相同。
  • IsScalar 必须是宏吗?您可以创建结构来表示您的不同类型,并使 IsScalar 成为接受正确数据类型的实际函数。
  • 您期待运行时测试还是编译错误?
  • 我认为@Ranic 是一个非常合适的解决方案。如果您仍然关心类型大小,您可以从结构中删除额外的填充,并使其与 sizeof(int) 完全匹配。
  • 可能重复:stackoverflow.com/questions/376452/…。请看一看;那里有很多很好的答案!

标签: c macros


【解决方案1】:

一种可能性是将整数包装在一个单字段结构中,以强制执行强类型。为避免最终的生产代码次优,使用不同的宏定义编译两次;一次使用结构来检测错误,一次不使用结构以获得最佳代码。

#ifdef STRONG_TYPING

#define TYPE(basetype, field)      struct { basetype field; }
#define INITIALIZER(value)         {(value)}
#define AS_BASETYPE(field, value)  ((value).field)

#else

#define TYPE(basetype, field)      basetype
#define INITIALIZER(value)         (value)
#define AS_BASETYPE(field, value)  (value)

#endif

typedef TYPE(int, alpnelm) APLNELM;
typedef TYPE(int, alprank) APLRANK;

#define IsScalar(a) (AS_BASETYPE(aplrank, a) == 0)

定义STRONG_TYPING 后,IsScalar(SomeAplNelm) 将给出编译器错误。没有STRONG_TYPING,结构的开销将完全消失。当然,所有模块必须在链接之前使用相同的定义进行编译,否则您的可执行文件可能会崩溃。

在您的程序代码中,在使用宏时,您必须遵守一些纪律。声明示例:

APLNELM MyAplNelm1;
APLNELM MyAplNelm2 = INITIALIZER(0);

作业:

AS_BASETYPE(aplnelm, MyAplNelm1) = 0;
AS_BASETYPE(aplnelm, MyAplNelm2) = AS_BASETYPE(aplnelm, MyAplNelm1);

仍然允许在不同的“强”类型之间交换值;只要您为每个单独的值指定正确的类型(结构中字段的名称)。

AS_BASETYPE(aplnelm, MyAplNelm2) = AS_BASETYPE(aplrank, MyAplRank);

请注意,您始终需要AS_BASETYPE 才能访问“强”类型之一的变量。这将使代码更加冗长(请随意为宏选择更短的名称),但这并没有错。这只是您要添加的元数据的概念;它实际上应该提高可维护性。

【讨论】:

    【解决方案2】:

    如果你想定义两个不同的整数类型,直接的typedef 方法会失败,因为typedef 为相同类型创建同义词,从不创建新类型。

    有一种方法可以创建不同的整数类型,但即使在这种情况下,也无法通过它们的值“检测”它们。

    例如,观察这段代码:

     enum myint1_e {min1 = -32767, max1 = 32767};
     enum myint2_e {min2 = -32767, max2 = 32767};
     typedef enum myint1_e integer1_t;
     typedef enum myint2_e integer2_t;
    
     integer1_t x1 = 0;
     integer2_t x2 = 0;
    

    现在,enum myint1_tenum myint2_t 这两种类型是不同的整数类型。
    见 C11:6.7.2.3.(par.5):

    在不同范围内或使用不同标签的 [...] 枚举类型的两个声明声明了不同的类型。

    所以,他们的typedef-ed 版本也不同。
    因此,变量x1x2 具有不同的类型。
    整数值 0 可以分配给这两个变量。

    现在,如果您想检查变量的类型是否是您想要的类型,您可以尝试这样做:

     #define VERIFY_INT1TYPE(a) ((integer1_t*)(0) == (&a))
    

    但此方法仅提供警告消息,而不是您期望的“与值进行比较”。

    说明:尽管整数类型在某种程度上可以在赋值操作中互换,但另一方面,它们的“指针”版本总是不同的类型。因此,像x1 == x2 这样的句子完全没有问题,但是比较两种不同指针类型的值会引发警告信息

    备注: 表达式(integer1_t*)(0) 是转换为integer1_t* 类型的NULL 指针。

    示例:

      VERIFY_INT1TYPE(x2);  
    

    当我使用 GCC 编译时,此示例会引发警告消息。

    【讨论】:

    • 我喜欢这个解决方案,但我无法让它发挥作用。也就是说,我尝试了 void main (void) { enum myint1_e {min1 = -32767, max1 = 32767};枚举 myint2_e {min2 = -32767, max2 = 32767}; typedef enum myint1_e integer1_t; typedef enum myint2_e integer2_t;整数1_t x1 = 0; integer2_t x2 = 0; #define VERIFY_INT1TYPE(a) ((integer1_t*)(&(a))) VERIFY_INT1TYPE (x1); VERIFY_INT1TYPE (x2); } // 使用 cl -c -Wall foo.c 结束编译(使用 MSVC 11.0)没有错误的 main
    • 对格式混乱感到抱歉。我想我也需要这方面的帮助!
    • @BobSmith:我原来的定义不起作用。所以我把它改成了更容易理解的东西,而且它对我有用。请检查我编写的宏的新定义。
    • 我喜欢这个解决方案,但我无法让它发挥作用。也就是说,我用VERIFY_INT1TYPE (x1)(x2) 尝试了你的代码,但都没有产生错误(使用MSVC 11.0 和cl -c -Wall foo.c)。我还尝试将结果分配给integer1_t *y1;integer2_t *y2; 也编译没有错误。
    • @BobSmith:你试过修改版的宏吗?
    【解决方案3】:

    我放弃了以下代码(需要 GNU 扩展:typeofStatement Exprs):

    #include <stdio.h>
    
    typedef int APLNELM;
    typedef int APLRANK;
    
    #define IsScalar(a) \
        ({ \
            /* Override typedefs in block scope */ \
            typedef char APLNELM; \
            typedef int  APLRANK; \
            /* Create variable with typeof(a) type; \
             * then compare it by sizeof with APLNELM */ \
            typeof(a) b; sizeof b == sizeof(APLNELM); \
        })
    
    int main(void)
    {
        APLNELM a = 5;
        APLRANK b = 5;
    
        printf("IsScalar: %d\n", IsScalar(a) ? 1 : 0);
        printf("IsScalar: %d\n", IsScalar(b) ? 1 : 0);
    
        return 0;
    }
    

    问题是typeof(a) 实际上并没有被APLNELMAPLRANK 取代。 C 不是动态语言,我同意struct 概念更适合这种区分。

    【讨论】:

    • 如果你打算使用 GNU 扩展,typeof() 可能会有用。您可以比较类型,IIRC。
    • 恕我直言,您只能通过 sizeof 运算符比较它们。 typeof 更像是“用我的类型替换我”。无法获得实际类型的名称(显然是int)。
    【解决方案4】:

    这可行,但我强烈建议您不要使用它,因为它不是超级可维护的:

    typedef int APLNELM;
    typedef int APLRANK;
    
    typedef unsigned int TYPETRAITS;
    
    #define TRAIT_SCALAR 0x1
    #define TYPETRAITS_APLNELM TRAIT_SCALAR /*whatever else you want, up to 32 traits*/
    #define TYPETRAITS_APLRANK 0/*whatever else you want, up to 32 traits*/
    
    #define GET_TYPE_TRAITS(X) TYPETRAITS_##X
    
    #define IS_SCALAR(X) (X & TRAIT_SCALAR)
    
    #define IS_TYPE_SCALAR(X) IS_SCALAR(GET_TYPE_TRAITS(X))
    
    int main()
    {
       const int aplnelm_traints = GET_TYPE_TRAITS(APLNELM);
       const int aplrang_traints = GET_TYPE_TRAITS(APLRANK);
    
       const bool is_aplnelm_scalar = IS_TYPE_SCALAR(APLNELM);
       const bool is_aplrang_scalar = IS_TYPE_SCALAR(APLNELM);
    }
    

    【讨论】:

      【解决方案5】:

      如果只有这两种类型会被传递到 isScalar 宏中,那么您可以执行以下操作:

      #include <stdio.h>
      
      struct APLNELM {
          int nelm;
          char a[1];
      };
      
      struct APLRANK {
          int rank;
          char a[2];
      };
      
      #define isScalar(b) (sizeof b.a == 2)
      
      int main(void) {
          // your code goes here
      
          struct APLNELM temp1;
          struct APLRANK temp2;
      
          printf("%d\n", isScalar(temp1));
          printf("%d\n", isScalar(temp2));
      
          return 0;
      }
      

      这段代码的输出是

      0
      1
      

      【讨论】:

      • 或者,编译时错误:int only_in_APLRANK,然后是 #define isScalar(b) b.only_in_APLRANK,如果在 isScalar() 中使用了没有名为 only_in_APLRANK 的成员的其他结构,则会产生编译器错误
      猜你喜欢
      • 1970-01-01
      • 2023-03-26
      • 2012-09-14
      • 1970-01-01
      • 2015-02-10
      • 2020-01-06
      • 1970-01-01
      • 1970-01-01
      • 2019-09-28
      相关资源
      最近更新 更多