0

我有一个 C 库,用于存储包含多个字段的记录。Schema 是从文本文件中读入的,包括记录中每个字段的类型。

为了简化问题,假设我有

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,那么当您尝试 set() 数据时,当然没有什么可以检查 src 指向 int32_t 而不是 mystruct进入 my_var。

显然,在将 int32_t* 或 mystruct* 转换为 void* 之前,需要在包装 set() 的东西中进行检查。我见过使用 typeof() 诡计进行编译时检查。我觉得我想要的可能是不可能的,但你永远不会知道所有的技巧......

有什么比提供在客户端程序编译时读取模式并生成 set_CHECKED() 包装宏的工具更好的方法,如果有人试图将 int32_t 复制到标记为保存 mystruct 的 my_var 中,则会出现编译器错误?GCC 扩展很好。

4

1 回答 1

0

实际上,“设置”和“获取”似乎是放置支票的正确位置。如果访问类型错误的字段是致命错误,则修复很简单:

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 应该是 void*,而不是 char*。这不会改变代码的正确性,但 void* 会告诉读者(和调试器)类型未知。)

于 2012-06-21T19:48:31.597 回答