來聊聊 API 命名時,後綴字的處理。

版號後綴字

Windows SDK API 裡有個奇怪的命名慣例 (naming convention),學過 Windows Programming 應該都耳熟能詳:API 的加強版,通常是在原來的名字後面,加上個 Ex 後綴字尾以命名之。例如,CreateWindow() 的加強版,就叫做 CreateWindowEx()

之所以說這種命名慣例會「奇怪」是因為,如果還要對 Ex 版增強的時候,該怎麼辦?難道還要來一個 CreateWindowExEx() 嗎?那豈不沒完沒了?

針對這種問題,常見的解法有兩種。第一種比較簡單,就是棄 Ex 改數字。例如 sqlite3_open()sqlite3_open_v2()xmlrpc_client_init()xmlrpc_client_init2()SQLite 的命名法比較囉唆,可是應該是因為還要支援 UTF-16 版的緣故;xmlrpc-c 的方法比較常見,個人認為也比較簡潔方便。

型別後綴字

除了利用後綴字來表明版號之外,有時候,函式名稱後綴字也用來表明,其所處理的資料型別。舉例來說,Microsoft 的 generic-text-mapping 技術,利用 AW 後綴,來區分輸出入使用 charwchar_t 型別。

前述提到 sqlite3_open() 有 UTF-16 版本,命名為 sqlite3_open16(),也是類似的手法。

我個人覺得,這種後綴字還算可以接受,可是如果是在用 C++,那就很搞笑了。事實上,若有機會用 C++ 把這類 C 的 API 包裝起來的話,我都會利用 overloading 機制,把型別後綴字幹掉,圖個清爽。

行為後綴字

另一種常見的 API 後綴字,代表著函式的行為不太一樣。例如很多 non-thread-safe 的 API,其 reentrant 版,也就是可安全地重複使用的版本[1],會在名稱後面加上 _r 的後綴字。例如 strtok() 的 reentrant 版叫 strtok_r()。通常加上 _r 的 API,其參數都會不太一樣,否則直接解掉問題即可,沒必要再另外發明一個 API。

Microsoft 在 Visual C++ 裡,對這類 non-thread-safe 的 API,解法不太一樣。使用 multithread 版 run-time library 時,若要從 non-thread-safe 變成 thread-safe 時,參數可以一樣不變,則同上述直接改掉實作,解掉問題,API 不變。可是,當碰到需要修改 prototype 更動參數時,Microsoft 用的是骯髒的手段,如利用 TLS 儲存需要增加的參數,使得 API 還是不變[2]。原本因呼叫了 non-thread-safe API 的程式,立地成佛變成 thread-safe。

這其實會產生一些問題,妨礙 porting 我認為問題還小,讓 programmer 自以為安全,才是真正的隱憂。我認為,Microsoft 應該要做的,其實是當使用 multithread 版 run-time library 時,像 security enhanced crt 那樣,在 compile 時產生 deprecation warning,建議使用 reentrant 版 API 才對。

講到 VC 的 security enhanced crt 就好笑,Microsoft 會在有安全疑慮的 API 後面,加上個 _s 後綴字。但問題是,M$ 根本沒有在管,參數是否相同,只要有安全疑慮,即使參數相同,也會發明一個對應的 _s 版 API。若目的是為了讓 programmer 可以認知道其實這個 API 並不安全,那就很搞笑了,參數相同時,讓 API 實作能夠安全,不正是 vendor 的責任嗎?更別提,實際上 _s 版 API,並沒有多安全的問題了。

隨便亂選後綴字

講了這麼多,其實我是想碎碎念,新同事你實作 thread-safe 版 API,在後面加上 _s 後綴字是想怎樣?畫虎不成反類犬,真是厲害。

還好我不再需要面對這種隨便亂選後綴字的 API 了。阿彌陀佛,善哉,善哉。


  1. Non-reentrant-safe 在 single thread 時也會發生。
  2. 使用 TLS,其實只有讓 API 變得 thread-safe,但還是 non-reentrant-safe。