作者tinlans ( )
看板C_and_CPP
標題Re: [問題] 關於typedef
時間Mon Oct 4 21:17:40 2010
先不說把 * 藏起來的問題。
如果你在寫 C++ 的話,
這類 typedef 其實是一個非常麻煩的東西。
因為...
---------------------------------------------------
// node.hxx
typedef struct node {
int value;
struct node *prev;
struct node *next;
} Node;
void NodeAPI1(Node *);
void NodeAPI2(Node *, Node *);
...
---------------------------------------------------
// node_client.hxx
struct Node;
void foo(Node *);
...
---------------------------------------------------
// node_client.cxx
#include "node.hxx"
#include "node_client.hxx"
void foo(Node *n)
{
// 這邊會存取到 n->value 等內部欄位
}
...
---------------------------------------------------
編譯後得到錯誤訊息:
In file included from node_client.cxx:4:
node_client.hxx:3: error: using typedef-name 'Node' after 'struct'
node.hxx:7: error: 'Node' has a previous declaration here
調換 node_client.cxx 兩行 #include 順序的話:
In file included from node_client.cxx:4:
node.hxx:7: error: conflicting declaration 'typedef struct node Node'
node_client.hxx:3: error: 'struct Node' has a previous declaration as
'struct Node'
一種解決方案是拔掉 node_client.hxx 的前置宣告,
然後讓它直接去 #include "node.hxx"。
但這樣做的話就會增加編譯期相依性,
任何寫到 #include "node_client.hxx" 的檔案,
只要 node.hxx 修改就會被重新編譯。
這也是為什麼 node_client.hxx 一開始會打算放前置宣告的原因。
所以 C++ 其實不是很喜歡這種 typedef 的用法,
typedef 在 C++ 一般比較常被用在 template 相關的 code 上。
一開始的寫法其實用純 C compiler 可以編譯通過,
不過 GCC 4.5 開始能用 gcc -Wc++-compat 得到警告訊息:
In file included from node_client.c:4:0:
node_client.hxx:3:8: warning: using 'Node' as both a typedef and a tag is
invalid in C++
node.hxx:7:3: note: originally defined here
--
Ling-hua Tseng (uranus@tinlans.org)
Department of Computer Science, National Tsing-Hua University
Interesting: C++, Compiler, PL/PD, OS, VM, Large-scale software design
Researching: Software pipelining for VLIW architectures
Homepage:
http://www.tinlans.org
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 118.160.107.243
※ 編輯: tinlans 來自: 118.160.107.243 (10/04 21:18)
推 manlike:好像搞得很複雜,這樣程式碼很難維護,可讀性也不高... 10/04 21:30
→ manlike:該include就include吧 犧牲一些compile time還好吧 除非 10/04 21:31
→ manlike:加了那個前置宣告可以讓你compile time從一天縮短到幾小時 10/04 21:33
→ manlike:而且你的Makefile也會變得很複雜 還是專注在寫程式好 XD 10/04 21:34
推 loveme00835:其實編譯時間時嚜的還是其次, 重點是「增加相依性」的 10/04 21:39
→ manlike:要不然乾脆直接把他做成 share library 只要 link XD 10/04 21:41
→ loveme00835:問題, 造成維護上的麻煩 10/04 21:41
→ manlike:本來就是相依的 程式都連在一起 你怎樣做都相依 = ="" 10/04 21:43
→ manlike:重點就在於編譯時間 是不是每次改動一點就要全部重編 = = 10/04 21:44
→ manlike:還是只要重編改過的 然後link進去就好 10/04 21:45
→ manlike:這樣寫維護性才不高 下一個看code的人 很難看懂 10/04 21:47
→ manlike:要追一下才會知道那個前置宣告到底定義在哪?? node.hxx 10/04 21:48
→ yoco315:不是吧,重點是根本不該那樣 typedef 吧,就全都可以避免 10/04 21:52
→ loveme00835:相依性有分強弱, 編譯的時間還可以用防火牆來擋, 為了 10/04 21:52
→ loveme00835:方便追亂引入, 等到ADL你就知道 10/04 21:53
推 purpose:C編譯器太鬆了,這樣寫也能通過,在Windows可以用cppcheck 10/04 21:53
→ purpose:對前制處理後(cl.exe /E)的檔案做靜態程式碼分析抓出問題 10/04 21:54
→ purpose:會回報Struct 'Node' hides typedef with same name 10/04 21:54
→ manlike:loveme00835 ... Orz 10/04 21:55
→ purpose: 置 10/04 21:55
→ yoco315:love 大大 Orz 10/04 21:56
推 loveme00835:@_@ 怎麼了嗎? 囧rz 10/04 22:06
→ tinlans:會出現前置宣告,除了降低編譯期相依性,也是告訴使用該 10/04 22:07
→ tinlans:header 的人,不需要知道 Node 細節。只要操作 Node * 10/04 22:07
→ tinlans:這種不透明的型別,和使用 foo() 這種 API 就可以了。 10/04 22:08
→ tinlans:如果 node_client.hxx 是很基礎的 utility header,用到它 10/04 22:09
→ tinlans:的有幾十個 .cxx。要是直接 #include "node.hxx", 10/04 22:10
→ tinlans:那就是大規模的重編譯。 10/04 22:10
→ tinlans:↑ 如果動到 node.hxx 的話。 10/04 22:11
推 loveme00835:所以才會有Pimpl idom這種手法出現, 不過也是少用為妙 10/04 22:33
→ loveme00835: i 10/04 22:58
推 purpose:tinlans大最後的推文說明完,比較懂這種設計的考量原因了 10/04 23:02
推 nowar100:今天花點時間看懂了,推一下 10/05 08:58
→ xatier:不過ADT好像常常用這種手法= =a 搞的維護者很難看懂程式碼 10/05 12:09