我有一个C库,用于存储包含许多字段的记录。从文本文件中读取模式,包括记录中每个字段的类型。
为了简化问题,想象一下
typedef enum my_type_enum
{
INT32, //32-bit integer
MYSTRUCT, //some struct I have, details irrelevant
...
} my_type_enum;
typedef struct my_var
{
my_type_enum typetag;
unsigned char* data;
} my_var;
my_var myrecord[numfields];
模式文件说明myrecord的每个字段是否应该包含int32_t或mystruct。我的库读取模式文件,myrecord中的每个my_var都设置标记并为数据分配适当的空间。
my_var是不透明的,客户端程序基本上用于简单数据
void set(my_var* record, size_t field, void * src)
{
memcpy(record[field].data, src, datatypes[record[field].typetag].size);
}
int32_t x = 5;
set(myrecord, 0, &x);
将值存储在记录中,并使用类似的get()来取消记录。
标记的my_var类型允许在my_var内部进行类型检查,但是如果模式说记录包含三个INT32,那么当你尝试设置时,当然没有什么可以检查src指向int32_t而不是mystruct ()数据到my_var。
显然,在将int32_t *或mystruct *转换为void *之前,需要在包装set()中进行检查。我看过使用typeof()技巧进行编译时检查。我觉得我想要的可能是不可能的,但你永远不会知道所有的技巧......
有什么办法比在客户端程序编译时提供读取模式的工具更好,并生成一个set_CHECKED()包装宏,如果有人试图将int32_t复制到my_var标记为保存mystruct,则会给编译器错误? GCC扩展很好。
答案 0 :(得分:0)
实际上,'set'和'get'似乎是正确的放置位置 你的支票。如果访问具有错误类型的字段是致命的 错误,修复很简单:
void set(my_var* record, size_t field, void * src)
{
if (record[field].typetag != (my_var)src->typetag) {
fprintf(stderr, "Type mismatch!\n");
exit(1);
}
memcpy(record[field].data, src, datatypes[record[field].typetag].size);
}
如果类型不匹配不是致命错误,则需要进行设置和 获取返回和错误代码并在呼叫站点正确处理。
请注意,在C中,类型信息在代码发布时消失了 编译。 'typetag'字段是您必须知道的唯一方式 一个字段的类型。
当然,如果您不需要能够更改架构 重新编译库,你可以尝试从它生成C代码。 这将允许您在编译时使用编译器的类型检查。
(另外:不是很重要,但my_var.data应该是一个空*,而不是一个char *。 这不会改变代码的正确性,但void *告诉了 类型未知的读者(和调试器)。)