之前寫了一篇《C++ 的 forward declaration》,大概介紹了一下 C++ 的 forward declaration 的使用方法。
而之所以會去寫那篇文章,其實主要是 Heresy 自己在搭配第三方 C 函式庫、想要使用 forward declaration 的時候碰到地雷了…
匿名結構
首先,一般定義一個結構,現在大概都會寫成下面的樣子:
struct TypeA { int iVal; };
但是在有的 C 函式庫裡面,會看到這種額外加了 typedef 的寫法:
typedef struct TypeA { int iVal; } TypeA;
他基本上應該就是定義了一個名為 TypeA 的結構(第一行的),同時又定義了他的別名也是 TypeA(第四行的)…
老實說,個人不太能理解為什麼要這樣搞?查了一下,這似乎是早期的 C 語言的寫法的設計需求?(參考:《Why are structure names different from their typedef names?》)
如果這樣倒還好,但是很多時候,又會碰到下面這種寫法:
typedef struct { int iVal; } TypeA;
這種寫法,基本上就是定義了一個沒有名稱的匿名結構(anonymous structure、參考),然後之後都是透過定義的別名 TypeA 來做操作了…
這種寫法雖然讓以 C++ 為主要開發語言的 Heresy 覺得很奇怪,但是實際上在 C 語言的函式庫裡面,算是相當常見的;像是 libTiff 裡面就有用到這種語法(參考)。
匿名結構 forward declaration 的問題
而如果遇到這種匿名結構的話,那在要使用 forward declaration 的時候,就會出現問題了…
像如果是下面的程式:
// defined in old_lib.h typedef struct { int iVal; } TypeA; // forward declaration in new_lib.h struct TypeA;
在 Visual Studio 就會出現下面的錯誤:
error C2371: 'TypeA': 重複定義; 基本類型不相同
而如果是 g++ 的話,錯誤訊息則會如下:
test.cpp:16:8: error: using typedef-name 'TypeA' after 'struct' 16 | struct TypeA; | ^~~~~
這邊的問題,基本上就是這邊已經先定義了一個名為 TypeA 的別名(typedef-name)後,又是著去宣告存在一個同名的結構 TypeA,由於兩者的型別是不同的,所以就變成語法錯誤了。
也就是說,當遇到這種只有別名的匿名結構的時候,基本上是沒辦法直接透過 forward declaration 來使用的。
這樣的問題其實在現在來說應該也算滿容易碰到的,所以 stack overflow 上也有相關討論了,像在《Forward declarations of unnamed struct》這篇,就有相關的套論;而這邊提供的建議,是「另外訂一個繼承自 TypeA 的結構來給外部操作」。
這邊大致上就是:
// defined in new_lib.cpp struct TypeB : TypeA {}; // forward declaration in new_lib.h struct TypeB;
基本上,這邊就是在 new_lib.cpp 裡面,定義一個結構 TypeB 去繼承 TypeA,然後在 new_lib.h 裡面,透過 forward declaration 來宣告 TypeB 的存在。
之後呢,基本上就是把所有針對 TypeA 的操作都藏在 new_lib.cpp 裡面,而在 new_lib.h 裡面,則是用 TypeB 的指標來取代本來的 TypeA!
如此一來,在 new_lib.h 的時候,就完全不會碰到 TypeA 了。
這部分大概就這樣了,基本上,就是紀錄一下自己碰到的問題了。