匿名結構的 forward declaration

之前寫了一篇《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 了。


這部分大概就這樣了,基本上,就是紀錄一下自己碰到的問題了。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。