用 C 编写一个安全的标记联合 [英] Writing a safe tagged union in C
问题描述
假设您正在编写一个 C struct
来表示一餐中的一道菜.课程 struct
中的字段之一是类型:
Let's suppose you are writing a C struct
which represents a course in a meal. One of the fields in the course struct
is of type:
enum TP_course {STARTER, MAINCOURSE, DESSERT};
然后,根据课程类型,您有一个子类型:
Then, depending on the type of the course, you have a subtype:
enum TP_starter {SALAD, GRILLEDVEGETABLES, PASTA};
enum TP_maincourse {BEEF, LAMB, FISH};
enum TP_dessert {APPLEPIE, ICECREAM, MOUSSE};
鉴于一次只使用一个这样的枚举(取决于课程的类型),将它们聚合在一个union
中是有意义的:
Given that only one of such enums will be used at a time (depending on the type of the course), it makes sense to aggregate them in a union
:
union U_subtype {
enum TP_starter s;
enum TP_maincourse m;
enum TP_dessert d;
};
所以课程 struct
看起来像这样:
So the course struct
would look like this:
struct S_course {
enum TP_course type;
union U_subtype stype;
float price_in_USD;
int availability;
...and all the rest of data would follow...
};
好的,一切都清楚了,但是……我是否可以遵循任何编码策略来尝试强制安全访问上面标记为 stype
的联合?也许以某种方式使其不透明?
Ok, everything is clear, but... is there any coding strategy I could follow to try to enforce safe access to the stype
tagged union above? Perhaps making it opaque in some way?
例如,如果我为 enum
编写了一个 switch/case
块,而我忘记为一个值编写一个 case
,则编译器会触发警告,这对以后维护代码有很大帮助.但是如果我访问 stype.s
而不先检查是否 type==STARTER
,编译器就不能足够聪明来实现有风险的编码,并且根本不会发出警告.
For example, if I write a switch/case
block for an enum
and I forget to write a case
for a value, the compiler will trigger a warning, which is of great help for maintaining the code in the future. But if I access stype.s
without first checking if type==STARTER
, the compiler cannot be smart enough for realizing of the risky coding, and won't warn at all.
我能否以某种方式组织代码,以便无法访问 U_subtype
联合的成员,除非在一个非常有限的地方,我清楚地记录了必须如何访问这些成员?
Can I organize the code in some way so that it's not possible to access the members of the U_subtype
union except in a very limited place where I clearly document how the access to such members must be done?
推荐答案
您可以
- 隐藏整个结构并暴露作用于指针的访问器函数
.
/* header */
struct S_course; //forward declaration
enum TP_starter {SALAD, GRILLEDVEGETABLES, PASTA};
enum TP_maincourse {BEEF, LAMB, FISH};
enum TP_dessert {APPLEPIE, ICECREAM, MOUSSE};
void S_course__set_starter(struct S_course *this, enum TP_starter starter);
//accessor functions
void S_course__set_maincourse(struct S_course *this, enum TP_maincourse maincourse);
void S_course__set_dessert(struct S_course *this, enum TP_dessert dessert);
/* c file */
enum TP_course {STARTER, MAINCOURSE, DESSERT};
union U_subtype {
enum TP_starter s;
enum TP_maincourse m;
enum TP_dessert d;
};
struct S_course {
enum TP_course type;
union U_subtype stype;
float price_in_USD;
int availability;
/*...*/
};
void S_course__set_starter(struct S_course *this, enum TP_starter starter)
{
this->type = STARTER;
this->stype.s = starter;
}
使用尖叫的成员名称不要碰我,或者像
tagged_union
这样的名称,这应该清楚地表明它需要如何访问.
Use member names that scream don't touch me, or a name like
tagged_union
, which should make it obvious how it needs to be accessed.
或
切换到 C++ 并使用其访问控制功能(私有/受保护)仅隐藏某些成员,同时允许通过公共成员/好友函数进行访问
Switch to C++ and use its access control features (private/protected) to hide only some members while allowing access through public member/friend functions
这篇关于用 C 编写一个安全的标记联合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!