【问题标题】:Enforce strong type checking in C (type strictness for typedefs)在 C 中强制执行强类型检查(typedefs 的类型严格性)
【发布时间】:2010-09-27 10:45:13
【问题描述】:

有没有办法对相同类型的 typedef 强制执行显式强制转换?我必须处理 utf8,有时我对字符数和字节数的索引感到困惑。所以最好有一些 typedef:

typedef unsigned int char_idx_t;
typedef unsigned int byte_idx_t;

此外,您需要在它们之间进行显式转换:

char_idx_t a = 0;
byte_idx_t b;

b = a; // compile warning
b = (byte_idx_t) a; // ok

我知道 C 中不存在这样的功能,但也许你知道一个技巧或编译器扩展(首选 gcc)可以做到这一点。


编辑 总的来说,我仍然不太喜欢匈牙利符号。由于项目编码约定,我无法将它用于这个问题,但我现在在另一个类似的情况下使用它,其中类型也相同并且含义非常相似。我不得不承认:它有帮助。我永远不会去声明每个整数都以“i”开头,但就像 Joel 的重叠类型示例一样,它可以挽救生命。

【问题讨论】:

  • 还有一篇很好的文章(虽然我不同意禁止 goto :))来自Joel,名为Making Wrong Code Look Wrong。即使没有直接联系,我认为这与您的问题非常相关。
  • 那篇文章中最“有趣”和最重要的事情,恕我直言,是关于匈牙利符号历史的一点。非常有趣...

标签: c typedef strong-typing typechecking


【解决方案1】:

如果您正在编写 C++,您可以创建两个具有不同名称的相同定义的类,它们是无符号整数的包装器。我不知道在 C 中做你想做的事的技巧。

【讨论】:

  • C 与结构的工作方式相同。 Microsoft 使用它来区分 windows.h 中的句柄类型。
【解决方案2】:

你可以这样做:

typedef struct {
    unsigned int c_idx;
} char_idx;

typedef struct {
    unsigned int b_idx;
} byte_idx;

然后你会看到你什么时候使用每个:

char_idx a;
byte_idx b;

b.b_idx = a.c_idx;  

现在更清楚它们是不同的类型,但仍然可以编译。

【讨论】:

    【解决方案3】:

    对于“句柄”类型(不透明的指针),Microsoft 使用了声明结构然后 typedef'ing 指向结构的指针的技巧:

    #define DECLARE_HANDLE(name) struct name##__ { int unused; }; \
                                 typedef struct name##__ *name
    

    然后代替

    typedef void* FOOHANDLE;
    typedef void* BARHANDLE;
    

    他们这样做:

    DECLARE_HANDLE(FOOHANDLE);
    DECLARE_HANDLE(BARHANDLE);
    

    所以现在,这行得通:

    FOOHANDLE make_foo();
    BARHANDLE make_bar();
    void do_bar(BARHANDLE);
    
    FOOHANDLE foo = make_foo();  /* ok */
    BARHANDLE bar = foo;         /* won't work! */
    do_bar(foo);                 /* won't work! */   
    

    【讨论】:

      【解决方案4】:

      使用棉绒。请参阅Splint:Typesstrong type check

      强大的类型检查经常揭示 编程错误。夹板可以检查 更严格的原始 C 类型 比典型的编译器更灵活(4.1) 并提供布尔类型支持 (4.2)。此外,用户可以定义 提供的抽象类型 信息隐藏 (0)。

      【讨论】:

        【解决方案5】:

        在 C 中,编译器强制执行的用户定义类型之间的唯一区别结构之间的区别。任何涉及不同结构的 typedef 都可以使用。您的主要设计问题是不同的结构类型是否应该使用相同的成员名称?如果是这样,您可以使用宏和其他坏血病技巧来模拟一些多态代码。如果不是,你真的致力于两种不同的表现形式。例如,您是否希望能够

        #define INCREMENT(s, k) ((s).n += (k))
        

        并在byte_idxchar_idx 上使用INCREMENT?然后对字段进行相同的命名。

        【讨论】:

          【解决方案6】:

          您询问了扩展程序。 Jeff Foster 的CQual 非常好,我认为它可以完成您想要的工作。

          【讨论】:

          • 是否有类似的程序仍在运行(CQual 网页的最后一次更新是在 2004 年)
          【解决方案7】:

          您想要的称为“强类型定义”或“严格类型定义”。

          一些编程语言 [Rust, D, Haskell, Ada, ...] 在语言级别对此提供了一些支持,而 C[++] 则没有。有人提议将其包含在名为“opaque typedef”的语言中,但未被接受。

          虽然缺乏语言支持确实不是问题。只需将要别名的类型包装到一个新类中,该类恰好具有 1 个类型为 T 的数据成员。大部分重复可以通过模板和宏来分解。这种简单的技术与直接支持的编程语言一样方便。

          【讨论】:

            【解决方案8】:

            使用BOOST_STRONG_TYPEDEF中定义的强类型定义

            【讨论】:

            • Boost 是一个 C++ 库,因此无关紧要。
            【解决方案9】:

            使用 C++11,您可以使用枚举类,例如

            enum class char_idx_t : unsigned int {};
            enum class byte_idx_t : unsigned int {};
            

            编译器将强制在两种类型之间进行显式转换;它就像一个薄包装类。不幸的是,您不会有运算符重载,例如如果您想将两个 char_idx_t 加在一起,则必须将它们转换为 unsigned int。

            【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2022-01-26
            • 2010-09-28
            • 1970-01-01
            • 2019-07-11
            • 1970-01-01
            • 1970-01-01
            • 2023-03-08
            • 2021-12-09
            相关资源
            最近更新 更多