2014-09-24 49 views
3

多態結構在C中很常見,但通常涉及顯式的強制轉換,它允許意外拋出不兼容的結構。編譯時檢查C中的多態類型?

struct ID { 
    char name[32]; 
}; 

struct IntID { 
    struct ID id_base; 
    int value; 
} 
struct FloatID { 
    struct ID id_base; 
    float value; 
} 

void id_name_set(ID *id, const char *name) 
{ 
    strlcpy(id->name, name, sizeof(id->name)); 
} 

/* macro that happens to use 'id_name_set', this is a bit contrived */ 
#define ID_NAME_SET_AND_VALUE(id, name, val) \ 
    do { \ 
     id_name_set((ID *)id, name); \ 
     id->value = val; \ 
    } while(0) 

void func(void) 
{ 
    struct { int value; } not_an_id; 

    /* this can crash because NotID doesn't have an ID as its first member */ 
    ID_NAME_SET_AND_VALUE(not_an_id, "name", 10); 
} 

這裏的問題是,我們不能鍵入檢查在宏id參數針對單一類型的,因爲它可能是一個ID或與ID作爲其第一個成員的任何結構。

我見過的很多代碼只是簡單地將結果轉換到整個地方的結構,但似乎有可能有一個更可靠的方法。

有沒有辦法在編譯時檢查?


注意,對於這個問題的目的,我們可以假設所有的結構,因爲他們繼承的結構使用相同的成員名稱。


注意,我希望能夠使用這樣的事情...

# define CHECK_TYPE_POLYMORPHIC(val, member, struct_name) \ 
    (void)(_Generic((*(val)), \ 
     /* base-struct */ struct_name: 0, \ 
     /* sub-struct */ default: (_Generic(((val)->member), struct_name: 0)))) 

/* --- snip --- */ 
/* check that `var` is an `ID`, or `var->id_base` is */ 
CHECK_TYPE_POLYMORPHIC(var, id_base, ID); 

...但失敗了ID類型的default情況 - 因爲他們沒有id會員。

到目前爲止,我發現要做到這一點的唯一方法是對所有結構體的完整列表進行類型檢查,這在某些情況下不是理想的(可能很多 - 或者是本地定義的,因此宏不知道,參見:Compile time check against multiple types in C?)。

+1

我不禁覺得這是領導下的歧途。如果你想要C++,可以使用C++。 – 2014-09-24 05:26:33

+1

@Jonathan Leffler。我知道,也許我試圖做的事情不是真的被C支持,但我並不真的有興趣轉向另一種語言,如果不可能對類型檢查進行類型檢查,我可以通過檢查struct的列表已經有了,但是如果有可能爲現有的C代碼庫添加一些額外的類型檢查,那麼爲什麼不呢? – ideasman42 2014-09-24 06:30:01

+0

您是否閱讀過GObject或gtk + 2.0/gtk + 3.0的任何文檔。他們做到這一點,不要重新發明車輪。只是他們的代碼它的版權是gnu小gpl。 – rhubarbdog 2014-09-26 04:14:13

回答

1

你不應該使用強制轉換。演員假設你知道你在做什麼,最壞的情況是導致未定義的行爲。您必須依賴於您感興趣的類型都具有相同名稱的struct ID字段這一事實。

然後,在你展示,你實際上有一個do-while一種官能大的情況下,您可以輕鬆地將一個輔助變量:

#define ID_NAME_SET_AND_VALUE(id, name, val) \ 
    do {          \ 
     ID* _id = &((id)->id_base));   \ 
     id_name_set(_id, (name));   \ 
     _id->value = (val);     \ 
    } while(0) 

如果一切順利,這是一個NOP,如果不這是違反約束並中止編譯。

在一個方面,你不能把一個變量,你可以使用複合文字,像

(ID*){ &((id)->id_base)) } 
1

C11(最新的C標準)爲編譯時間最接近的事多態性其 類型通用表達式使用_Generic關鍵字,但我不確定它是否符合您的需求。

GCC編譯器還爲您提供__builtin_type_compatible_p,您可以使用它編譯一些宏。

你也可以自定義GCC一些MELT擴展