现有的C API看起来像这样:
//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?
方法当函数作为对象范围内的方法存在时,链接在语言中是有意义的。鉴于API处于当前状态,我将继续使用它:
int main(void) {
Widget w;
SetPosition(SetTitle(SetHeight(SetWidth(&w,400),600),"title"),0,0);
}
我可以在C中使用任何技术来获得与其他语言相同的流动性吗?
答案 0 :(得分: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);
这是否比单独的语句更具可读性是一个品味和本地编码惯例的问题。我个人觉得这很麻烦,也很难读。在代码生成方面有一个小优势:对象指针不需要从本地变量重新加载以进行下一次调用。这种微不足道的优化很难证明链接语法是正确的。
一些程序员尝试以这种方式使其更加可口:
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);
没有简单的方法可以将这个链重新组织成更具可读性的链接。
请注意,一些最古老的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);
链接这些也是可能的,但没有真正的收获。
最后,应该注意方法链不允许显式错误传播。在OOP语言中,链接是惯用的,可以抛出异常,以或多或少可口的方式表示错误。在C中,处理错误的惯用方法是返回错误状态,这与返回指向对象的指针相冲突。
因此,除非保证方法成功,否则建议不要使用方法链并执行迭代测试:
Widget *w = getWidget();
if (SetWidth(w, 640)
|| SetHeight(w, 480)
|| SetTitle(w, "Sample widget")
|| SetPosition(w, 0, 0)) {
/* there was an error, handle it gracefully */
}
答案 1 :(得分:0)
据我所知,没有很好的方法在c中实现OOP样式函数链接。您可以使用函数指针实现类似行为的链接。无论如何,这使C代码更加复杂。
这里是一个例子:
#define SCAN_STR(buf) str(&SCANER_NAME, buf)
#define SCAN_STR_L(buf, len) strL(&SCANER_NAME, buf,len)
#define SCAN_NUM(number) num(&SCANER_NAME, number)
typedef struct StrScanner
{
const char* curPos;
bool isError;
uint32_t parsedCnt;
StrScanner* (*chr)(StrScanner* scan, int*);
StrScanner* (*str)(StrScanner* scan, char* buf);
StrScanner* (*strL)(StrScanner* scan, char* buf, uint32_t len);
StrScanner* (*num)(StrScanner* scan, int*);
} StrScanner;
使用这种链接
char myBuf[30];
char my2Buf[30];
int in;
#define SCANER_NAME scan
if(SCANER_NAME.SCAN_STR(myBuf)->SCAN_STR_L(my2Buf, 5)->SCAN_NUM(&in)->isError)
printf("error\n");
#undef SCANER_NAME
通常,您只需使用c函数样式编写类似
char myBuf[30];
char my2Buf[30];
int in;
bool res = str(&scan, myBuf);
res = res && strL(&scan, myBuf2, 5);
res = res && num(&scan, num);
if(!res)
printf("error\n");
如果可以确保一次仅激活一个扫描仪,则可以使用全局存储扫描仪对象引用。现在,您不必将扫描对象传递给每个链函数。