Doesn't seem like the author knows C. The C example of codecs is riddled with syntax errors. When he writes<p><pre><code> struct Codec
{
*int (*encode)(*int);
*int (*decode)(*int);
};
</code></pre>
he probably meant to swap the int and the asterisk to mean a pointer to int. But no one would use a pointer to int to mean bytes; we have pointer to char or pointer to unsigned char for that:<p><pre><code> struct Codec {
uint8_t* (*encode)(uint8_t*);
uint8_t* (*decode)(uint8_t*);
};
</code></pre>
And that doesn't exactly deal with ownership issues.<p>But besides those nitpicking, I struggle to see what his point is. His point is to extract common features of components and build interfaces for them? I mean isn't that something we've been doing for a long time?
Is the only takeaway that polymorphic design is good for extensibility? I mean, isn't that the whole point of polymorphism? But Linux's everything's a file design is great. Makes for a great user experience when you know all the knobs are under /sys on sysfs
This article is making a case for standardized interfaces and the techniques two popular open source projects use to implement it in C. I always find it nice to read articles taking an explanatory look at open source projects, regardless of if it presents anything "novel" in the process.<p>A non-obvious place where this pattern also exists is React components in JavaScript. Components define an interface via props, but the consumer of the component is the one who provides the event handlers. It's an inversion of control in the same way that passing around function pointers to complete an interface is.
I was hoping this was going to be an analysis of design patterns used within the Linux kernel codebase itself, but the "everything is a file" concept is undoubtedly worth talking about.<p>There could have been more discussion of the tradeoffs involved, though. The "file" abstraction doesn't seem to handle every kind of use case equally well.
Consider the following:<p>* one slot for a variadic function pointer<p>* another slot for a datum that fully describes the function signature for the function the first slot points to.<p>This allows the programmer to specify whatever types the interface allows in parameters of the function callback. Otherwise the programmer must use a catchall interface like "argc, argv" and then add steps inside the function body to fetch the args. At least in my experience programmers screw up way more often with the catchall interface than they do with typing the correct function parameters.<p>There's just one "tiny" problem with this approach. :)<p>Nevertheless I've seen this technique in old code that still runs on every modern architecture on which someone bothered to compile it. Gcc doesn't complain about the tiny problem unless you use emscripten to compile to web assembly. (And even there you can still get away with undefined behavior as long as the number of parameters agree.)
This is a very simple principle, yet I see it constantly violated in big projects with plenty of redources.
Even though it seems obvious, it takes a lot of effort and arguing to keep these kind of interfaces small and effective.