Good Code Design from Linux
leandromoreira.com.br
leandromoreira.com.br
struct Codec
{
*int (*encode)(*int);
*int (*decode)(*int);
};
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: struct Codec {
uint8_t* (*encode)(uint8_t*);
uint8_t* (*decode)(uint8_t*);
};
And that doesn't exactly deal with ownership issues.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?
we often, as developers, see something as useful or not only from our point of view, which is fair since you're reading and you want to learn more, but we can't forget that there are many developers out there being formed/mentored that might benefit from this kind of post.
In any case I just don't think the author has made the case in a very clear way. Unifying all the codecs in a codec library through an interface is something whose novelty is lesser than that of everything-is-a-file. It takes more ingenuity to consider objects like pipes and sockets as files, and I hope the author didn't really lump them together. I just hoped the author would cover more things about this design pattern and explain his thesis more clearly in an article with such a grandiose title.
It's always hard to know whether you're going too deep or too shallow, what are the target people?! I'm afraid, in this case, I didn't put as many details as I planned because every time I read it, it started to become be too difficult for newcomers and my idea was to show to anyone this idea of "we should try to do good code design despite the language since it works for many giant projects such as Linux"
I appreciate the time you took to read it and for the issues pointed: clarity and cover more thins, I'll pay more attention to the future posts and I'm open to suggestions, thanks. Maybe I miss the balance and I should put more details/examples into it.
Or just use void*, then the caller doesn't have to cast when they want to pass arbitrary data.
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.
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.
Basically the way to achieve runtime polymorphism is to have function pointers in a struct.
But you don't necessarily put the pointers in the per-instance struct. That would take memory proportional to the number of instances. So you create something like file_operations, which is nothing but pointers and no instance data. Maybe the ext4 driver declares exactly one of them as static const. And so every ext4 file gets the same function pointers. Another filesystem is free to provide their own. This arrangement is pretty similar to C++ vtables.
Also note that some of the operations aren't unique to the filesystem. So ext4 points some function pointers at a generic implementation rather than a specific one.
So these are some of the patterns discussed. I think it is just not exceptionally clear writing. (And I saw one spot where they got the syntax for pointers incorrect...)
* one slot for a variadic function pointer
* another slot for a datum that fully describes the function signature for the function the first slot points to.
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.
There's just one "tiny" problem with this approach. :)
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.)