[英]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.