The mentality of most developers there was: How can I make the change described in the ticket by touching as little existing code as possible?
TU with opaque pointers can be used for such purposes, and they are, but still fail short of Modula-2, Mesa, CLU and UCSD Pascal, to cite contemporary examples.
How come? C makes it trivial to create self-contained modules.
It lacks almost everything that a programming language with modules offers on its type system, and compiler toolchain.
Proper public/private types, module initialization/shutdown, linking with type checking, namespaces.
One can fake modules with void pointers, static types on translation units, prefixes that hopefully no one else is using, and that is about it.
Modules either have types as part of their interface, or haven't. I C, the declarations shipped in the interface header represent the interface, and the types declared in it are public types.
C also supports symbols with internal linkage.
What exactly is missing?
> module initialization/shutdown,
What's wrong with initializing objects? Languages such as Rust also rely on factory methods to instantiate objects.
> linking with type checking,
The interface headers define the symbols, which must correspond to the symbols handled by the linker. What's missing?
> namespaces.
That definitely C doesn't support as a first class citizen, but this is nothing that symbol prefixes don't handle.
What's missing, then?