簡體   English   中英

C中的條件結構類型

[英]Conditional struct type in C

我只是想解析一些可以以兩種類型之一出現的數據。 第一個是

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

有人有什么想法?

這取決於您的解決方案必須具備的一般性。 正如其他答案所確定的那樣,這兩個示例結構非常相似,因此可以相對容易地進行管理(盡管決定如何確定字符串的結尾會帶來一些問題)。

如果你需要一個更通用的系統,你可能需要查看某種“結構描述符”字符串,它傳遞給轉換器,或者可能是“結構描述符數組”。

例如,字符串可能是:

"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中一個不太知名的宏,即<stddef.h>定義的offsetof宏。 您將創建一個描述符類型,例如:

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()函數看起來更像打印函數。


也可以看看

嗯,兩個結構之間的唯一區別是name成員中的字符數。 如果你的結構把它作為char* (在內存中),那么我認為你想要的就行了。 讀取id您可以將malloc為適當的大小並將其讀入,然后讀取結構的其余部分。

你當然可以

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);
  /* ... */
}

但它有點尷尬和重復。

你可以在數組結構中使用一個union,所以它的大小將在賦值時決定。 但是我不確切知道它如何與你的打包屬性一起使用。

每個成員訪問都需要知道結構布局。 而且因為你不知道你提前使用了哪種結構,所以你必須以某種形式復制代碼才能處理這兩種布局。 但是,如果你有一個已知數量的結構,你可以隱藏宏背后的細節:

#define MEMBER(_ptr, _type, _name) ((_type)?((A*)_ptr)->_name:((B*)_ptr)->_name)

printf("%i\n", MEMBER(a, type, value1));

我發現一些答案過於復雜。

從我的觀點來看,解決這個問題的最簡單方法是在數據結構中使用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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM