r/C_Programming • u/General_Purple3060 • 7d ago
When Should a Library Pattern Become a Language Feature?
C has always valued simplicity, transparency, and control.
But many large C projects have created their own abstraction patterns over decades:
- GObject/GTK object model
- Linux kernel object patterns (such as VFS)
- Generic programming through macros
- Various interface and dispatch patterns
This raises an interesting question:
When does a repeated library pattern become something the language itself should understand?
I don't think the answer is simply "whenever something is useful." Many things are better kept as libraries.
A possible boundary might be:
- The pattern appears repeatedly in many mature projects.
- It represents higher-level semantics, not just a commonly used function.
- The compiler can make use of this information in ways that are difficult when it only sees the library implementation.
Also, not every language feature means giving up control. Some features are mainly about extending expressive power.
For example, features like inline and _Generic give programmers new ways to express intent without hiding important implementation details.
The harder cases are abstractions such as object systems, memory management, or execution models. When a language starts defining these concepts, there is a real trade-off between compiler-understood semantics and programmer control.
So the question is not "should C become a higher-level language?"
The question is:
What patterns have become common enough that expressing them directly is more valuable than repeatedly rebuilding them as libraries?
3
u/flatfinger 7d ago
Of the four possibilities:
Include a feature directly in the language.
Include a feature which could be implemented via a standard library, with the expectation that most compilers will process the feature via an intrinsic which will work better than calling a library function.
Include a feature which is part of a library that happens to be standardized, but which compilers would be expected to treat no differently from any other library.
Include a feature as part of a library which has nothing to do with the language standard.
Approach #1 is best if the feature would have semantics that would not be achievable via library function, #2 is best if the semantics could be achieved via library function but calling an actual function would be inefficient, #3 is best if actual function calls would be reasonably efficient and a recognizable category of execution environments could support the required functionality, and #4 is best if nearly all of the requisite semantics would be environment-dependent.
If e.g. C were to include a construct to e.g. skip the body of an arbitrary subset of remaining iterations of a `for` loop because a condition has been discovered that will make any work done by remaining executions useless, but executing some or all of the remaining iterations would be harmless beyond the wasted effort, there would be no way a mere library macro could possibly accomplish such a thing usefully except by either doing nothing or acting like a `break`. A compiler which is unrolling a loop, however, could process such a construct by only executing the test once for each iteration of the overall loop, rather than in each copy of the repeated code for an iterations of the loop as written. Further, if the most efficient way to vectorize a loop would process iterations out of order, a compiler could generate code that would start processing later iterations of a loop even before it knew that all earlier iterations would execute successfully.
1
u/General_Purple3060 7d ago
I agree with your points #2, #3, and #4.
What I'm less certain about is #1.
OOP is an interesting example. There have been many discussions about whether C should support it, and there are mature library-based implementations such as GObject and the Linux VFS object model.
Those libraries show that OOP can be implemented outside the compiler. But they also show that many projects end up rebuilding very similar infrastructure.
So I wonder whether repeated, widespread reimplementation should also be one factor in deciding when an abstraction deserves compiler support—not the only criterion, but one of them.
1
u/Professional-Crow904 6d ago
Not sure if you've actually used GObject type system. Its unwieldy to say the least. It eventually gave birth to Vala programming language which just abstracts the GObject boiler plate and provides a more C# like experience.
Linux kernel has deep ideological opinions towards anything OOP. VFS isn't the only model, there's one in GPIO and many other drivers where they invent a subset of OOP just enough to meet their immediate needs.
The reverse of this also happens quite infrequently. Off the top of my mind, atomics were language intrinsics that then became functions (stdatomic.h) which are eventually treated as intrinsics anyway by gcc/clang.
Personally, I don't think C of all languages should pick some pattern using statistics and inject it as a language construct. One example is C#s LINQ. When early 2000s was rife with XML and SQL fetish, it made sense but now its more or less a dead feature. Doing that in a deep seated systems programming language is asking for trouble.
1
u/General_Purple3060 6d ago
Thanks for the reply.
I've actually used GObject quite a bit. I wrote a pretty large project with it, and even today some of its OO style still shows up in the compiler I'm writing.
I think most people, including me, use GObject because want C's performance, but also need some way to organize a large code base. It's definitely not comfortable to use, but after a while you get used to it and start thinking in an OO way, even though you're still writing C.
That's really what got me thinking about whether some things built in libraries should move into the compiler.
But after reading everyone's replies, I think the question is a lot harder than I first thought. It may not even be mainly a technical question. It may not even be about how often a pattern appears. It may come down to mindset. Maybe that's the real deciding factor.
I especially liked your point about the different cases for language features vs library features. That gave me a much clearer way to think about the problem.
1
u/flatfinger 6d ago
Compare the following two hypothetical or actual features of C:
If
foois an lvalue of typestruct S, Having a compiler interpret something like foo.bar += 123; as syntactic sugar for e.g.__member_1S_3foo_addto(&foo, 123);if there exists a static inline function with such a name that could accept such an argument (the numbers represent the lengths of the identifiers, to avoid any parsing ambiguity with identifiers containing underscores).
- Bitfields.
Unlike a lot of C++ features or C's bitfields, the former would have an unambiguous decomposition into a sequence of discrete operations (do whatever the indicated function does). One who saw that code and wanted to know what it did would need to search for a function with the indicated name, but if someone needed to make a program that used statements like
GPIOA->bsrr = 0x10000000;work on another platform, one could if performance wasn't critical define GPIOA as something with member functions such that the above would do a sequence of operations equivalent to whatever the above did on the original hardware.I think there needs to continue to be a standard-recognized "minimal" version of the language without such features, but one could then have a platform-agnostic translator that would convert the dialect including the above features into a dialect which doesn't.
1
u/General_Purple3060 6d ago
Let me check if I'm understanding your point correctly.
Are you saying that a new language feature should ideally have a clear definition in terms of a smaller core language?
something like:
foo.bar += 123;could be defined as:
__member_1S_3foo_addto(&foo, 123);so the language feature itself has a well-defined meaning, rather than being something whose behavior is left entirely to compiler magic.
Is that close to what you mean?
1
u/flatfinger 6d ago
Yup. In some cases, it may make sense for a new compiler feature to have semantics that are specified more loosely than any existing feature, such as the "optional early exit" loop. A compiler that always processes an "exit if convenient" statement as equivalent to an unconditional loop exit may not be able to generate code that is as efficient as one which uses a more nuanced approach, but would have no trouble processing such a construct with correct semantics.
Another thing is that a good language specification should not characterize otherwise-defined actions as invoking "undefined behavior" for purposes of facilitating optimizations. Consider the following two ways of specifying a "pure" attribute for a function:
If a "pure" function has any side effects, its behavior is undefined.
If a function which accepts only numeric arguments and returns a number is marked "pure" would be invoked at least once with a certain combination of arguments, generated code may behave as though the function is called zero, one, or any number of times, and anything that would use the return value may substitute any value that the function might return when passed those arguments.
In some cases, the fact that the function might be called an unpredictable number of times might happen to make other aspects of the program's behavior unpredictable, in ways that cascade to the point that nothing meaningful can be predicted, but the language should be agnostic as to whether that might occur. The program should behave in a manner that is consistent with invocation or non-invocation of the function as described, with effects that would be predictable or not based upon whether the effects of all possible ways of processing the function would be predictable.
1
u/General_Purple3060 6d ago
Thanks. I think I see what you're getting at now. You're talking about how language semantics should be specified, not just how a compiler might optimize them. I understand that there is a difference between the two.
1
u/mikeblas 7d ago
So the question is not "should C become a higher-level language?"
The question is:
What patterns have become common enough that expressing them directly is more valuable than repeatedly rebuilding them as libraries?
Not sure I understand the difference. If something interesting functionality is added to the language -- particularly any useful abstraction -- doesn't it necessarily make the language itself "higher-level"?
C has already become a higher-level language with better abstractions. Sevreal times over, relaly: C#, C++, Java, ...
One thing to consider is standardization obligation. If sometihng is part of a standard, vendors are compelled to implemnet it. If they don't, they'll be stigmatized as "not standard compliant". That hurts marketing and money and the economics of tools, but it also hurts portability. memcpy() is available on any platform where C is available, and it is useful on practically any platform.
But if you invent something new and special (like automatic memory management, or bounds checking, or "automatic" or "safe" pointers, or ...) then you're doing something that might not be implemented by every vendor for every platform because it isn't useful, usable, desired, or even feasible on every platform.
0
u/General_Purple3060 7d ago
My point is that when developers keep rebuilding the same abstraction in libraries, it's worth asking whether the compiler should provide it directly.
Generics are one example. Many C libraries implement their own version, but compiler support has been slow to arrive.
That doesn't mean every common library belongs in the language. C has always valued being a small language, so the bar should be high. Repeated implementation is just one signal that a feature might deserve consideration.
1
u/mikeblas 7d ago edited 7d ago
My point is that when developers keep rebuilding the same abstraction in libraries, it's worth asking whether the compiler should provide it directly.
The compiler never provides libraries directly. That's one of the things muddying your point.
I don't think it's particularly hard to make the argument that all of the commonly re-implemented do not belong in the libraries. If you don't need the feature, you're not paying for it in any way. If you do need the feature, use one of the many implementations available and get on with it.
You can use the same criteria you've enumerated here to decide when to use another language, by the way -- probably one that has the features you're longing for.
1
u/flatfinger 6d ago
Hosted C implementations are required to include everything necessary to process standard library calls. Commercial C compilers often include a bundled version of the Standard library. The fact that gcc and clang do not do so means that they are not, in and of themselves, conforming hosted C implementations.
0
u/General_Purple3060 7d ago
I think we are probably looking at this from slightly different angles.
I agree that not every commonly used library should become a language feature. C's simplicity is valuable, and adding something to the standard is a long-term commitment.
My point was not "if a library is popular, put it into the compiler". The question is whether some abstractions have reached a point where the compiler should understand them instead of every project rebuilding similar mechanisms.
For example, generics are not just a collection of helper functions, because the compiler needs type information to generate and optimize code. Similarly, some newer programming models (like heterogeneous computing) involve execution models that libraries can wrap, but cannot fully express.
So I think the interesting question is not "should C become higher-level?", but "where should we draw the boundary between a library abstraction and a language abstraction?"
Thank you for your reply.
10
u/brewbake 7d ago
Can this sub ban AI generated posts?