2014-11-08 31 views
5

我有一個C項目,可以移植到各種(PC和嵌入式)平臺。如何在通用標題中鍵入實現定義的結構?

應用程序代碼將使用各種調用,這些調用將具有特定於平臺的實現,但共享一個共同的(通用)API以幫助實現可移植性。我試圖以最合適的方式來聲明函數原型和結構。

下面是我想出迄今:

的main.c:

#include "generic.h" 

int main (int argc, char *argv[]) { 
    int ret; 
    gen_t *data; 

    ret = foo(data); 
    ... 
} 

generic.h:(與平臺無關的包括)

typedef struct impl_t gen_t; 

int foo (gen_t *data); 

impl.h:(平臺特定聲明)

#include "generic.h" 

typedef struct impl_t { 
    /* ... */ 
} gen_t; 

impl.c:(特定於平臺的實現)

int foo (gen_t *data) { 
    ... 
} 

體形:

gcc -c -fPIC -o platform.o impl.c 
gcc -o app main.c platform.o 

現在,這似乎工作......在它編譯好。但是,我通常不會標記我的結構,因爲它們從來不會在typedef的別名之外訪問。這是一個小挑戰,但我想知道是否有辦法通過匿名結構實現相同的效果?

我還要求後人,因爲我搜索了一會兒,最接近的答案,我發現是這樣的:(Link

在我而言,這不會是正確的做法,因爲應用程序特別是不應該直接包含實現頭文件 - 整個過程就是將程序與平臺分離。

我看到一對夫婦的其他低於理想的方式來解決這個問題,例如:

generic.h:

#ifdef PLATFORM_X 
#include "platform_x/impl.h" 
#endif 

/* or */ 

int foo (struct impl_t *data); 

這些都不似乎特別有吸引力,絕對不是我的風格。雖然我不想在上游游泳,但當我可能有更好的方法來實現我的想法時,我也不希望發生衝突。所以我認爲typedef解決方案是正確的,它只是我留下的結構標籤行李。

想法?

+0

你可以只讓'gen_t'指針類型(句柄)。然後客戶端將不得不使用你的api來初始化/使用/銷燬它。或者有一點變化,讓你公開的結構包含一個不透明的指向實際實現的指針。否則,你可以做的其他事情就不多了。必須創建客戶必須瞭解的對象或跟蹤方法。 – 2014-11-08 05:59:43

+0

存在這樣一個問題是被包括BEFORE impl.h的generic.h但是,generic.h被引用在impl.h I.E.定義的結構一個前瞻性參考。 (可能不會編譯)要好得多,以使generic.h包含impl.h(其中impl.h是特定於平臺的)然後,當generic.h嘗試使用它時,該結構已經被定義。 – user3629249 2014-11-08 09:41:11

+1

定義一個結構好得多:struct tagname {;;; };然後始終將其引用爲'struct tagname myname;'有這樣一些原因:1)命名結構是折舊格式2)調試器使用標籤名稱引用字段等3)在結構上使用typedef是(一般情況下)不好的編程練習 – user3629249 2014-11-08 09:46:00

回答

4

您目前的技術是正確的。試圖使用匿名(無標記)的struct會挫敗你正在嘗試做的事情 - 你不得不公開所有地方的struct定義的細節,這意味着你不再有不透明的數據類型。


commentuser3629249說:

頭文件夾雜物的順序意味着有一個前向參考由generic.h文件的結構;也就是說,在定義結構之前使用它。這不太可能編譯。

此問題對於問題中顯示的標題不正確;它是準確的樣本main()代碼(我沒有注意到,直到添加此響應)。

關鍵問題是顯示的接口函數採用或返回類型爲gen_t的指針,該指針依次映射到指針struct impl_t。只要客戶端代碼不需要爲結構分配空間,或者取消引用指向訪問結構成員的結構的指針,客戶端代碼就不需要知道結構的詳細信息。將結構類型聲明爲現有就足夠了。你可以使用其中任一申報struct impl_t存在:

struct impl_t; 

typedef struct impl_t gen_t; 

後者還介紹了別名gen_t的類型struct impl_t。也參見Which part of the C standard allows this code to compile?Does the C standard consider that there are one or two struct uperms entry types in this header?

在問題中的原始main()程序爲:

int main (int argc, char *argv[]) { 
    int ret; 
    gen_t data; 

    ret = foo(&data); 
    … 
} 

該代碼不能與gen_t被編譯成一個不透明的(非指針)型。它的工作確定有:

typedef struct impl_t *gen_t; 

它不會與編譯:

typedef struct impl_t gen_t; 

因爲編譯器必須知道的結構有多大分配正確的空間data,但是編譯器不知道大小根據不透明類型的定義。 (對於typedefing指針結構見Is it a good idea to typedef pointers?

因此,main()代碼應該更像:

#include "generic.h" 

int main(int argc, char **argv) 
{ 
    gen_t *data = bar(argc, argv); 
    int ret = foo(data); 
    ... 
} 

其中(在這個例子中)bar()被定義爲extern gen_t *bar(int argc, char **argv);,所以它返回一個指針不透明類型gen_t

意見分爲總是使用struct tagname還是使用typedef作爲名稱。 Linux內核是一個不使用typedef機制的大量代碼;所有結構都明確地爲struct tagname。另一方面,C++不需要明確的typedef;寫作:

struct impl_t; 
在C++程序

指該名​​稱現在是一個類型的名稱。由於不透明的結構類型需要一個標籤(或你最終使用void *的一切,這是壞的原因有很大的兵團,但主要的原因是,你失去了使用void *所有類型的安全;請記住,typedef引入了一個別名底層類型,而不是一個新的不同的類型),在C方式,我的代碼模擬C++:

typedef struct Generic Generic; 

我避免使用我的類型_t後綴,因爲POSIX保留_t爲實現使用*(見What does a type followed by _t represent?)。你可能很幸運,並逃脫它。其中,「實施」是指C編譯器和其支持的代碼,或C庫及其配套代碼 - 我在哪裏類型,如dec_tloc_t是由代碼庫定義的代碼庫(這是不是執行工作的一部分工作),並且這兩種類型都導致數十年來的痛苦,因爲代碼移植的一些系統定義了這些類型,系統的特權也是如此。我設法擺脫的其中一個名字;另一個我沒有。 'Twas痛苦!如果你必須使用_t(它是一種方便的方式來表明的東西是一類),我建議使用一個獨特的前綴太:pqr_typename_t對一些項目pqr,例如。

*請參閱POSIX標準中The Name Space中第二個表的底線。

+0

頭部的順序文件包含意味着通過generic.h文件IE對結構進行了前向引用在定義結構之前,它被使用。這是不太可能編譯 – user3629249 2014-11-08 09:42:42

+0

這是一個很好的答案,清除了我所關心的一切。謝謝!順便說一句,你對main()中的gen_t用法絕對正確。我把這個簡化成了最小的相關代碼,並將這部分翻譯成了翻譯。指針上使用的第一個函數調用是實現的一部分,它確實知道它的確切大小,並根據需要分配內存。最後,謝謝你對_t後綴的提示。我沒有意識到這一點 - 也許沒有經過正式訓練的缺點之一。 – SirNickity 2014-11-11 01:09:23