2017-01-31 35 views
3

有一個現有的C API,看起來像這樣:如何使C中的方法鏈接流暢?

//data 
typedef struct {int properties;} Widget; 

//interface 
Widget* SetWidth(Widget *const w, int width){ 
    // ... 
    return w; 
} 
Widget* SetHeight(Widget *const w, int height){ 
    // ... 
    return w; 
} 
Widget* SetTitle(Widget *const w, char* title){ 
    // ... 
    return w; 
} 
Widget* SetPosition(Widget *const w, int x, int y){ 
    // ... 
    return w; 
} 

第一個參數始終是一個指針實例和改造實例的功能總是返回它作爲一個指針。

我認爲這樣做是爲了支持某種Method Chaining

方法在函數作爲對象範圍內的方法存在時,鏈接對語言有意義。

int main(void) { 
    Widget w; 
    SetPosition(SetTitle(SetHeight(SetWidth(&w,400),600),"title"),0,0); 
} 

有沒有我可以在C使用得到同樣的流動性在其他語言中的任何技術:鑑於其當前狀態的API,我使用它像這樣離開?

+1

不是。流體接口通常只存在於OO語言中。 – Barmar

+1

這在C中沒有多大用處,原因有二:一,沒有例外。返回值通常用於指示成功或失敗。二,手動內存管理,沒有RAII。 – EOF

+0

C沒有方法。它有功能。你的問題沒有意義。 – EJP

回答

3

C語言中沒有任何語法技巧來實現方法鏈接,這可能與其他一些語言中使用的方法相同。在C中,你會寫分開的函數調用,傳遞對象指針到每個功能:

Widget *w = getWidget(); 
widgetSetWidth(w, 640); 
widgetSetHeight(w, 480); 
widgetSetTitle(w, "Sample widget"); 
widgetSetPosition(w, 0, 0); 

同樣可以完成與方法在C++調用和其它OOP語言:

Widget *w = getWidget(); 
w->SetWidth(640); 
w->SetHeight(480); 
w->SetTitle("Sample widget"); 
w->SetPosition(0, 0); 

利用上述API和假設每個方法返回this對象,鏈接語法該方法是這樣的:

getWidget()->SetWidth(640)->SetHeight(480)->SetTitle("Sample widget")->SetPosition(0, 0); 

這是否是比分隔符更易讀e聲明是品味和本地編碼慣例的問題。我個人覺得它很麻煩,很難閱讀。代碼生成方面有一個小優點:對象指針不需要從下一次調用的本地變量中重新加載。這種微小的優化很難證明鏈式語法的正確性。

一些程序員試圖使它更可口這樣:

getWidget() 
-> SetWidth(640) 
-> SetHeight(480) 
-> SetTitle("Sample widget") 
-> SetPosition(0, 0); 

再次,口味和編碼規範的問題......但C相當於肯定看起來彆扭:

Widget *w = widgetSetPosition(widgetSetTitle(widgetSetHeight(widgetSetWidth(getWidget(), 640), 480), "Sample widget"), 0, 0); 

而且沒有簡單的方法將這個鏈條重新組織成更具可讀性的鏈條。

需要注意的是一些最ANCIEN C庫函數可得鏈接:

const char *hello = "Hello"; 
const char *world = "World"; 
char buf[200]; 
strcpy(buf, hello); 
strcat(buf, " "); 
strcat(buf, world); 
strcat(buf, "\n"); 

可改組爲:

strcat(strcat(strcat(strcpy(buf, hello), " "), world), "\n"); 

但更安全,多首選的方法是這樣的:

snprintf(buf, sizeof buf, "%s %s\n", hello, world); 

欲瞭解更多信息,你可能要閱讀:

Marco Pivetta (Ocramius): Fluent Interfaces are Evil

還要注意的是,如果C的對象具有用於這些調用函數指針構件,上述所有的語法可以使用,但對象指針必須仍然作爲參數傳遞。函數指針通常在到其上的指針被存儲在對象中的結構組合,模仿的C++虛擬方法的實現,使得語法稍重:

Widget *w = getWidget(); 
w->m->SetWidth(w, 640); 
w->m->SetHeight(w, 480); 
w->m->SetTitle(w, "Sample widget"); 
w->m->SetPosition(w, 0, 0); 

鏈接這些也是可能的,但對於沒有真正的獲得。

+2

我想他知道流利的界面在其他語言中的樣子。問題的關鍵是如何在C中獲得類似的東西。這一切似乎只是一種說不出來的冗長的方式。 – Barmar

+0

@Barmar:* fluid *是OOP語言中的選定術語,但它具有此範圍之外的通用含義。我暗示說,方法鏈接語法不會帶來很多流動性。這只是我的看法,所以我可能會脫離主題。 – chqrlie

+0

@Barmar:OP可能知道,但一個C程序員可能不會。 – chqrlie