C中的条件结构类型

时间:2011-11-24 20:55:36

标签: c parameters struct conditional

我只想解析一些可能出现在两种类型之一的数据。第一个是

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);
}

有人有任何想法吗?

6 个答案:

答案 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);
}