Exploring Polymorphism in C: Lessons from Linux and FFmpeg's Code Design (2019)
20 comments
·March 6, 2025social_quotient
MuffinFlavored
how similar are the C abstractions in ffmpeg and qemu given they were started by the same person?
variadix
I haven’t worked with ffmpeg’s code, but I have worked with QEMU. QEMU has a lot of OOP (implemented in C obviously) that is supported by macros and GCC extensions. I definitely think it would have been better (and the code would be easier to work with) to use C++ rather than roll your own object model in C, but QEMU is quite old so it’s somewhat understandable. I say that as someone who mostly writes C and generally doesn’t like using C++.
shmerl
What's the reason for ffmpeg to use C, also historic?
sitkack
I'd say at 20kloc of C, https://www.lua.org/ gets you as far up the Object Oriented tower as you want.
cbarrick
For the record, this design pattern is called a virtual method table, or vtable.
I'm surprised that this article never mentioned the term.
C++ programmers will know this pattern from the `virtual` keyword.
KerrAvon
> 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.
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?
mananaysiempre
The difference between Go and Java is that in Go a type need not declare its adherence to an interface up front—any type that has methods of appropriate names and signatures is considered to implement the interface, even if its designers were not aware of the interface’s existence. (This involves a small bit of dynamism in the runtime; easily cached, though, as the set of methods of a given type and the set of all interfaces are both fixed by the time the program runs.) Whether that’s a good thing depends on your design sensibilities (ETA: nominal vs structural).
jeroenhd
For those wishing Java had a similar feature, there's Manifold: https://github.com/manifold-systems/manifold/tree/master/man...
Manifold is a very interesting project that adds a lot of useful features to Java (operator overloading, extension classes, and a whole bunch more). I don't know if it's smart to use it in production code because you basically go from writing Java to writing Manifold, but I still think it's a fun project to experiment with.
sitkack
> Manifold is a Java compiler plugin, its features include Metaprogramming, Properties, Extension Methods, Operator Overloading, Templates, a Preprocessor, and more.
Neat tool. It is like having a programmable compiler built into your language.
owlstuffing
>The difference between Go and Java is that in Go a type need not declare its adherence to an interface up front.
Go can't declare adherence up front, and in my view that’s a problem. Most of the time, explicitly stating your intent is best, for both humans reading the code and tools analyzing it. That said, structural typing has its moments, like when you need type-safe bridging without extra boilerplate.
JavierFlores09
The funny thing about Java is that while its design is to be entirely nominally typed, the way it is implemented in the JVM is compatible with structural typing, but there are artificial limitations set to follow the intended design (though of course, if one were to disable these limitations then modeled type safety goes out of the window as Java was simply not designed to be used that way). One community which takes advantage of this fact is the Minecraft modding space, as it is the basis[1] of how modding platforms like Fabric work.
1: https://github.com/SpongePowered/Mixin/wiki/Introduction-to-...
billfruit
So golang supports 'duck typing'?
kazinator
GNU C++ once had this feature; it was called Signatures. It was removed, though.
A signature declaration resembled an abstract base class. The target class did not have to inherit the signature: just have functions with matching names and types.
The user of the class could cast a pointer to an instance of the class to a pointer to a compatible signature. Code not knowing anything about the class could indirectly call all the functions through the signature pointer.
juwjfoobar
interfaces in Go are structural. Interfaces in Java are nominal and require immediate declaration of intent to implement at type definition.
null
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.
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
That said, keeping those interfaces clean and consistent as the codebase grows (and ages) takes some real dedication.
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.
https://ffmpeg.org/mailman/listinfo