1

我使用 C 位域将数据存储在内存中。对于存档使用,这些数据必须写入文件(然后与另一台机器的数据组合)。将位域直接保存到文件似乎是个坏主意,因为数据的排列是特定于实现的。

出于这个原因,我编写了一些方法来“序列化”这些位域,以统一格式保存它们:

/* uint16 is a unsigned int with size 16 */

typedef struct {
    uint16 a : 1;
    /* ... just examples ... */
    uint16 z : 13;
} data;

void save_data(FILE* fp, data d) {
    uint16 tmp;
    tmp = d.a;
    fwrite(&tmp,sizeof(uint16),1,fp);
    /* ... */
    tmp = d.z;
    fwrite(&tmp,sizeof(uint16),1,fp);
}

虽然这是完美的工作,但似乎不太好扩展,因为添加更多成员还data需要将数据添加到保存例程中。

有什么技巧可以在更改位域数据时自动将位域数据转换为统一格式,而无需调整例程/宏

4

3 回答 3

1

如果您愿意投资一点,您可以使用P99之类的工具进行“语句展开”:

// in header
#define MY_BITFIELDS a, z
#define WRITE_IT(X) fwrite(&(unsigned){ d.X }, sizeof(unsigned), 1, fp)    
#define WRITE_ALL(...) P99_SEP(WRITE_IT, __VA_ARGS__)


// in your function
WRITE_ALL(MY_BITFIELDS);

顺便说一句,int如果可以避免这种情况,切勿用于位域。一组位的语义由 更好地匹配unsigned

通过更多的宏编码,您甚至可以使用类似的东西

#define MY_BITFIELDS (a, 1), (z, 11)

产生struct声明写入部分。

于 2013-07-24T11:47:55.027 回答
1

这是一种方法。我不能推荐它,但它就在那里并且它有点工作,所以为什么不看看它。这个化身仍然依赖于平台,但您可以轻松切换到独立于平台的、可能是人类可读的格式。为简洁起见,省略了错误处理。

// uglymacro.h
#if defined(DEFINE_STRUCT)

#define BEGINSTRUCT(struct_tag)     typedef struct struct_tag {
#define ENDSTRUCT(struct_typedef)   } struct_typedef;
#define BITFIELD(name,type,bit)     type name : bit;
#define FIELD(name,type)            type name;
#define ARRAYFIELD(name,type,size)  type name[size];

#elif defined(DEFINE_SAVE)

#define BEGINSTRUCT(struct_tag)     void save_##struct_tag(FILE* fp, \
                                                           struct struct_tag* p_a) {
#define ENDSTRUCT(struct_typedef)   }
#define BITFIELD(name,type,bit)     { type tmp; tmp = p_a->name; \
                                      fwrite (&tmp, sizeof(type), 1, fp); }
#define FIELD(name,type)            { fwrite (&p_a->name, sizeof(p_a->name), 1, fp); }
#define ARRAYFIELD(name,type,size)  { fwrite (p_a->name, sizeof(p_a->name[0]), size, fp); }

#elif defined(DEFINE_READ)

#define BEGINSTRUCT(struct_tag)     void read_##struct_tag(FILE* fp, \
                                                           struct struct_tag* p_a) {
#define ENDSTRUCT(struct_typedef)   }
#define BITFIELD(name,type,bit)     { type tmp; fread (&tmp, sizeof(type), 1, fp); \
                                      p_a->name = tmp; }
#define FIELD(name,type)            { fread (&p_a->name, sizeof(p_a->name), 1, fp); }
#define ARRAYFIELD(name,type,size)  { fread (p_a->name, sizeof(p_a->name[0]), size, fp); }

#else
#error "Must define either DEFINE_STRUCT or DEFINE_SAVE or DEFINE_READ"
#endif

#undef DEFINE_STRUCT
#undef DEFINE_READ
#undef DEFINE_WRITE
#undef BEGINSTRUCT
#undef ENDSTRUCT
#undef FIELD
#undef BITFIELD
#undef ARRAYFIELD

您的结构定义如下所示:

// mystruct_def.h
BEGINSTRUCT(mystruct)
BITFIELD(a,int,1)
FIELD(b,int)
ARRAYFIELD(c,int,10)
ENDSTRUCT(mystruct)

你像这样使用它:

// in mystruct.h file
#define DEFINE_STRUCT
#include "uglymacro.h"
#include "mystruct_def.h"

// in mystruct.c file
#include "mystruct.h"
#define DEFINE_READ
#include "mystruct_def.h"
#define DEFINE_WRITE
#include "mystruct_def.h"

坦率地说,按照现代标准,这种方法是丑陋的。大约 20 年前我用过类似的东西,当时它很丑。

另一种选择是使用更人性化的代码生成工具而不是 C 预处理器。

于 2013-07-24T12:26:48.567 回答
-1

为什么不使用人类可读的文本格式?

typedef struct {
    int a : 1;
    /* ... just examples ... */
    int z : 13;
} data;

void save_data(FILE* fp, data d) {
    fprintf( fp, "a:%d\n", d.a );
    fprintf( fp, "b:%d\n", d.b );
    ...
    fprintf( fp, "z:%d\n", d.z );
}

这种技术的优势在于,使用任何不同语言的人都可以快速编写解析器,将您的数据加载到任何机器、任何架构上。

于 2013-07-24T11:47:44.373 回答