我只想解析一些可能出现在两种类型之一的数据。第一个是
struct typeA {
int id,
char name[16],
int value1,
int value2,
int value3,
int value4
} __attribute__ ((packed));
第二种可能性是数据具有双重名称长度的形式
struct typeB {
int id,
char name[32],
int value1,
int value2,
int value3,
int value4
} __attribute__ ((packed));
到目前为止一切顺利。现在我有两个解析这两个的函数
int parse_typeA(struct typeA *x){ /* do some stuff */ }
int parse_typeB(struct typeB *x){ /* do some stuff */ }
如果您有更多类型,这显然是不切实际的。如何使用单个函数和其他参数(如
)实现两种类型的解析int parse_any_type(void *x, int type){
/*
* WHAT TO DO HERE ??
*
* The following doesn't work
*
* if(type == 1)
* struct typeA *a = (struct typeA *)x;
* else
* struct typeB *a = (struct typeB *)x;
*/
printf("%i\n", a->id);
printf("%s\n", a->name);
printf("%i\n", a->value1);
printf("%i\n", a->value2);
printf("%i\n", a->value3);
printf("%i\n", a->value4);
}
有人有任何想法吗?
答案 0 :(得分:2)
这取决于您的解决方案必须具备的一般性。正如其他答案所确定的那样,两个示例结构非常相似,因此可以相对容易地进行管理(尽管决定如何确定字符串的结尾会出现一些问题)。
如果你需要一个更通用的系统,你可能需要查看某种“结构描述符”字符串,它传递给转换器,或者可能是“结构描述符数组”。
例如,字符串可能是:
"i s16 i i i i" // typeA
"i s32 i i i i" // typeB
"u32 i64 z d d" // struct { uint32_t a; int64_t b; size_t c; double d; double e; };
int parse_any_type(void *output, const char *desc);
然后你必须处理一些对齐和填充问题,但是(只要你得到描述符字符串正确)你可以编写一个例程来处理那个批次(打包或解包)。
使用'descriptors',您可能正在处理C中不太知名的宏之一,即offsetof
中定义的<stddef.h>
宏。您将创建描述符类型,例如:
enum Type { CHAR, UCHAR, SCHAR, STR, USTR, SSTR, SHORT, USHORT, INT, UINT, LONG, ULONG, ... };
struct descriptor
{
enum Type m_type; // Code for the variable type
size_t m_size; // Size of type
size_t m_offset; // Offset of variable in structure
};
struct descriptor d_TypeA[] =
{
{ INT, sizeof(int), offsetof(TypeA, id) },
{ STR, 16, offsetof(TypeA, name) },
{ INT, sizeof(int), offsetof(TypeA, value1) },
{ INT, sizeof(int), offsetof(TypeA, value2) },
{ INT, sizeof(int), offsetof(TypeA, value3) },
{ INT, sizeof(int), offsetof(TypeA, value4) },
};
然后,您可以将相应的类型描述符数组(以及该数组的大小)传递给函数,以及指向数据存储位置的指针。
您可以使用指向正确转换器的函数指针类型,而不是使用枚举。
int parse_structure(void *output, const struct descriptor *desc, size_t n_desc);
另一种选择是你只需用一个适当的函数来处理每个类型,该函数调用其他更简单的函数来处理结构的每个部分。
int parse_TypeA(TypeA *output)
{
if (parse_int(&output->id) == 0 &&
parse_str(output->name, 16) == 0 &&
parse_int(&output->value1) == 0 &&
parse_int(&output->value2) == 0 &&
parse_int(&output->value3) == 0 &&
parse_int(&output->value4) == 0)
return 0;
...diagnose error...
return -1;
}
您的示例没有清楚地标识数据的来源,而不是存储的位置。这可能无关紧要,但会影响解决方案。没有参数,期望从标准输入读取数据可能是合理的。或者,您可能有一个包含要解析的数据的字符串,也可能包含长度;这些都是函数的参数。
您的示例未说明错误处理;调用代码将如何知道转换是否成功。
如果操作正确,可以将相同的描述机制用于解析和打印机制 - 您的parse_any_type()
函数看起来更像打印函数。
答案 1 :(得分:0)
嗯,两个结构的唯一区别是name
成员中的字符数。如果您的结构将此作为char*
(在内存中)保留,那么我认为您想要的就可以了。阅读id
后,您可以malloc
找到合适的大小并将其读入,然后读取结构的其余部分。
答案 2 :(得分:0)
你当然可以做到
int parse_any_type(void *x, int type){
int id;
char *name;
int value1;
int value2;
int value3;
int value4;
if(type == 1) {
id = ((struct typeA*)x)->id;
name = ((struct typeA*)x)->name;
/* ... */
} else {
id = ((struct typeB*)x)->id;
/* ... */
}
printf("%i\n", id);
printf("%s\n", name);
printf("%i\n", value1);
/* ... */
}
但它有点尴尬和重复。
答案 3 :(得分:0)
你可以在数组结构中使用一个union,所以它的大小将在赋值时决定。但是我不确切知道它如何与你的打包属性一起使用。
答案 4 :(得分:0)
每个成员访问都需要知道结构布局。而且因为你不知道你提前使用了哪种结构,所以你必须以某种形式复制代码才能处理这两种布局。但是,如果你有一个已知数量的结构,你可以隐藏宏背后的细节:
#define MEMBER(_ptr, _type, _name) ((_type)?((A*)_ptr)->_name:((B*)_ptr)->_name)
printf("%i\n", MEMBER(a, type, value1));
答案 5 :(得分:0)
我发现一些答案过于复杂。
从我的观点来看,解决此问题的最简单方法是在数据结构中使用 union 。 对于您在示例中提供的那个,它应该类似于:
struct typeU
{
int id;
int name_len;
union
{
char _16[16];
char _32[32];
} name;
int value1;
int value2;
int value3;
int value4;
} __attribute__ ((packed));
打印功能类似于:
void typeU_print(struct typeU *t)
{
printf("%i\n", t->id);
switch (t->name_len)
{
case 16:
printf("%s\n", t->name._16);
break;
case 32:
printf("%s\n", t->name._32);
break;
}
printf("%i\n", t->value1);
printf("%i\n", t->value2);
printf("%i\n", t->value3);
printf("%i\n", t->value4);
}