在 C 中强制执行强类型检查(typedef 的类型严格性)

发布于 2024-07-10 16:56:31 字数 560 浏览 15 评论 0原文

有没有办法对相同类型的 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 的重叠类型示例中那样,它可以挽救生命。

Is there a way to enforce explicit cast for typedefs of the same type? I've to deal with utf8 and sometimes I get confused with the indices for the character count and the byte count. So it be nice to have some typedefs:

typedef unsigned int char_idx_t;
typedef unsigned int byte_idx_t;

With the addition that you need an explicit cast between them:

char_idx_t a = 0;
byte_idx_t b;

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

I know that such a feature doesn't exist in C, but maybe you know a trick or a compiler extension (preferable gcc) that does that.


EDIT
I still don't really like the Hungarian notation in general. I couldn't use it for this problem because of project coding conventions, but I used it now in another similar case, where also the types are the same and the meanings are very similar. And I have to admit: it helps. I never would go and declare every integer with a starting "i", but as in Joel's example for overlapping types, it can be life saving.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

梦幻之岛 2024-07-17 16:56:31

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

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

然后而不是

typedef void* FOOHANDLE;
typedef void* BARHANDLE;

They do:

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! */   

For "handle" types (opaque pointers), Microsoft uses the trick of declaring structures and then typedef'ing a pointer to the structure:

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

Then instead of

typedef void* FOOHANDLE;
typedef void* BARHANDLE;

They do:

DECLARE_HANDLE(FOOHANDLE);
DECLARE_HANDLE(BARHANDLE);

So now, this works:

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! */   
疏忽 2024-07-17 16:56:31

您可以执行以下操作:

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;  

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

You could do something like:

typedef struct {
    unsigned int c_idx;
} char_idx;

typedef struct {
    unsigned int b_idx;
} byte_idx;

Then you would see when you are using each:

char_idx a;
byte_idx b;

b.b_idx = a.c_idx;  

Now it is more clear that they are different types but would still compile.

柠檬心 2024-07-17 16:56:31

你想要的是“强typedef”或“严格typedef”。

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

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

What you want is called "strong typedef" or "strict typedef".

Some programming languages [Rust, D, Haskell, Ada, ...] give some support for this at language level, C[++] does not. There was a proposal to include it into the language with the name "opaque typedef", but was not accepted.

The lack of language support is really not a problem though. Just wrap the type to be aliased into a new class having exactly 1 data member, of type T. Much of the repetition can be factored out by templates and macros. This simple technique is just as convenient as in the programming languages with direct support.

中二柚 2024-07-17 16:56:31

使用棉绒。 请参阅 Splint:Types强类型检查

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

Use a lint. See Splint:Types and strong type check.

Strong type checking often reveals
programming errors. Splint can check
primitive C types more strictly and
flexibly than typical compilers (4.1)
and provides support a Boolean type
(4.2). In addition, users can define
abstract types that provide
information hiding (0).

节枝 2024-07-17 16:56:31

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

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

byte_idxchar_idx 上使用 INCRMENT 吗? 然后以相同的方式命名这些字段。

In C, the only distinction between user-defined types that is enforced by the compiler is the distinction between structs. Any typedef involving distinct structs will work. Your major design question is should different struct types use the same member names? If so, you can simulate some polymorphic code using macros and other scurvy tricks. If not, you are really committed to two different representations. E.g., do you want to be able to

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

and use INCREMENT on both byte_idx and char_idx? Then name the fields identically.

提笔落墨 2024-07-17 16:56:31

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

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

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

With C++11 you can use an enum class, e.g.

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

The compiler will enforce an explicit cast between the two types; it is like a thin wrapper class. Unfortunately you won't have operator overloading, e.g. if you want to add two char_idx_t together you will have to cast them to unsigned int.

寂寞陪衬 2024-07-17 16:56:31

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

You asked about extensions. Jeff Foster's CQual is very nice, and I think it could do the job you want.

羁拥 2024-07-17 16:56:31

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

If you were writing C++, you could make two identically defined classes with different names that were wrappers around an unsigned int. I don't know of a trick to do what you want in C.

陌路黄昏 2024-07-17 16:56:31

使用 BOOST_STRONG_TYPEDEF 中定义的强 typedef

Use strong typedef as defined in BOOST_STRONG_TYPEDEF

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文