4

There is an existing C API that looks like this:

//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;
}

The first parameter is always a pointer to the instance, and the functions that transform the instance always return it as a pointer.

I assume this was done to support some kind of Method Chaining?

Method Chaining makes sense in languages when the functions exist as methods inside the scope of the object. Given the API in its current state, I'm left using it like this:

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

Are there any techniques I can use in C to get the same fluidity as in other languages?

Trevor Hickey
  • 36,288
  • 32
  • 162
  • 271
  • 1
    Not really. Fluid interfaces generally only exist in OO languages. – Barmar Jan 31 '17 at 21:46
  • 1
    It's not much use in C, for two reasons: One, no exceptions. The return value is usually used to indicate success or failure. Two, manual memory management and no RAII. – EOF Jan 31 '17 at 21:47
  • 1
    C doesn't have methods. It has functions. Your question doesn't make sense. – user207421 Jan 31 '17 at 21:56
  • @Barmar [C can be OOP](https://github.com/aaronryank/c-prohackr112/blob/master/c-oop.c). Kind of. – MD XF Jan 31 '17 at 22:15
  • 1
    @Barmar: Fluent, not fluid. Unless you want to pump your interface through a pipe. – Kerrek SB Jan 31 '17 at 22:25

2 Answers2

3

There is no syntax trick in C to achieve method chaining as may be used in some other languages. In C, you would write separate function calls, passing the object pointer to each function:

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

The same can be done with method calls in C++ and other OOP languages:

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

With the above APIs, and assuming each method returns the this object, the method chaining syntax looks like this:

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

Whether this is more readable than the separate statements is a matter of taste and local coding conventions. I personally find it cumbersome and harder to read. There is a small advantage in terms of code generation: the object pointer does not need to be reloaded from a local variable for the next call. This minuscule optimisation hardly justifies the chaining syntax.

Some programmers try and make it more palatable this way:

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

Again, a matter of taste and coding conventions... But the C equivalent definitely looks awkward:

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

And there is no easy way to reorganise this chain into some more readable.

Note that some of the most ancien C library functions can be chained too:

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

Can be reorganised into:

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

But a safer and much preferred approach is this:

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

For more information, you might want to read this:

Marco Pivetta (Ocramius): Fluent Interfaces are Evil

Note also that if the C object has function pointer members for these calls, all of the above syntaxes could be used, but the object pointer must still be passed as an argument. The function pointers are usually grouped in a structure to which a pointer is stored in the object, mimicking the implementation of C++ virtual methods, making the syntax slightly heavier:

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);

Chaining these is possible too, but for no real gain.

Finally, it should be noted that method chaining does not allow for explicit error propagation. In OOP languages where chaining is idiomatic, exceptions can be thrown to signal errors in a more or less palatable way. In C the idiomatic way to handle errors is to return an error status, which conflicts with returning a pointer to the object.

As a consequence, unless the methods are guaranteed to succeed, it is advisable to not use method chaining and perform iterative tests instead:

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 */
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 3
    I think he knows what a fluent interface looks like in other languages. The point of the question was how to get something similar in C. This all seems to be just a long-winded way of saying you can't. – Barmar Jan 31 '17 at 22:28
  • @Barmar: *fluid* is the chosen term in OOP languages, but it has a generic meaning outside of this scope. I was hinting that the method chaining syntax does not IMHO carry much fluidity. It is only my opinion, so I might be off topic. – chqrlie Jan 31 '17 at 22:30
  • @Barmar: The OP probably knows, but the casual reader, a C programmer, might not. – chqrlie Jan 31 '17 at 22:31
  • Fluid or fluent? I made that typo above in a comment, are you saying it's an actual thing? The tag wiki and the link in the question show what fluent coding looks like in languages that support it. – Barmar Jan 31 '17 at 22:32
  • @Barmar: no, I was extrapolating... fluent is the term, I guess I am not as fluent in English as I should. – chqrlie Jan 31 '17 at 22:37
1

As far as I know there is no nice way to implement OOP style function chaining in c. You can implement chaining like behaviour using function pointers. Anyway this makes C code more complex.

Here is an example:

#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;

Use chaining like this

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

Normally you would simply write in c function style something like

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");

If you can ensure that there is only one scanner active at a time you can use a global to store scanner object reference. Now you don't have to pass scan object to every chain function.

Jonny Schubert
  • 1,393
  • 2
  • 18
  • 39