This is an excellent pattern in C. The Dovecot mail server has many fine examples of the style as well e.g.<p><pre><code> struct dict dict_driver_ldap = {
.name = "ldap",
.v = {
.init = ldap_dict_init,
.deinit = ldap_dict_deinit,
.wait = ldap_dict_wait,
.lookup = ldap_dict_lookup,
.lookup_async = ldap_dict_lookup_async,
.switch_ioloop = ldap_dict_switch_ioloop,
}
};
</code></pre>
defines the virtual function table for the LDAP module, and any other subsystem that looks things up via the abstract dict interface can consequently be configured to use the ldap service without concrete knowledge of it.<p>(those interested in a deeper dive might start at <a href="https://github.com/dovecot/core/blob/main/src/lib-dict/dict-private.h" rel="nofollow">https://github.com/dovecot/core/blob/main/src/lib-dict/dict-...</a>)
I spend a ton of time in FFmpeg, and I’m still blown away by how it uses abstractions to stay modular—especially for a project that’s been around forever and still feels so relevant. Those filtergraphs pulling off polymorphism-like tricks in C? It’s such an elegant way to manage complex pipelines. e.g.<p>ffmpeg -i input.wav -filter_complex "
[0:a]asplit=2[a1][a2];
[a1]lowpass=f=500[a1_low];
[a2]highpass=f=500[a2_high];
[a1_low]volume=0.5[a1_low_vol];
[a2_high]volume=1.5[a2_high_vol];
[a1_low_vol][a2_high_vol]amix=inputs=2[a_mixed];
[a_mixed]aecho=0.8:0.9:1000:0.3[a_reverb]
" -map "[a_reverb]" output.wav<p>That said, keeping those interfaces clean and consistent as the codebase grows (and ages) takes some real dedication.<p>Also recently joined the mailing lists and it’s been awesome to get a step closer to the pulse of the project. I recommend if you want to casually get more exposure to the breadth of the project.<p><a href="https://ffmpeg.org/mailman/listinfo" rel="nofollow">https://ffmpeg.org/mailman/listinfo</a>
For the record, this design pattern is called a virtual method table, or vtable.<p>I'm surprised that this article never mentioned the term.<p>C++ programmers will know this pattern from the `virtual` keyword.
> The interface type in golang is much more powerful than Java’s similar construct because its definition is totally disconnected from the implementation and vice versa. We could even make each codec a ReadWriter and use it all around.<p>This paragraph completely derailed me — I’m not familiar with golang, but `interface` in Java is like `@protocol` in Objective-C — it defines an interface without an implementation for the class to implement, decoupling it entirely from the implementation. Seems to be exactly the same thing?
I'd say at 20kloc of C, <a href="https://www.lua.org/" rel="nofollow">https://www.lua.org/</a> gets you as far up the Object Oriented tower as you want.
No discussion about polymorphism in C is complete without mentioning this macro:<p><a href="https://stackoverflow.com/questions/15832301/understanding-container-of-macro-in-the-linux-kernel" rel="nofollow">https://stackoverflow.com/questions/15832301/understanding-c...</a>
> for instance, Linux handles network socket, special files (like /proc/cpuinfo) or even USB devices as files. This is a powerful idea that can make easy to write or use programs for linux since we can rely in a set of well known operations from this abstraction called file.<p>Benno Rice gave a fantastic talk a few years ago called "What UNIX Cost Us," which he starts off by showing how to write some USB device code in macOS, Windows, and Linux. It only takes a few minutes to demonstrate how pretending that everything is a file can be a pretty poor abstraction, and result in far more confusing code, which is why everyone ends up using libusb instead of sysfs.<p><a href="https://www.youtube.com/watch?v=9-IWMbJXoLM#t=134s" rel="nofollow">https://www.youtube.com/watch?v=9-IWMbJXoLM#t=134s</a>
What is with the incorrect function declarations? I see:<p><i>int (</i>func)().<p>Maybe you meant: int * (*func)(void)?<p>Don't mean to be pedantic. Just wanted to point it out so you can fix it.