Translation Units Considered Harmful?
cor3ntin.github.io
cor3ntin.github.io
#include <iostream>
int main () {
std::cout << "Hello world\n";
}
>Which on my system expands to about 33000 lines of code.Really great rant! Much underrated.
> So maybe we need to ask fundamental questions about the compilation model and examine some beliefs we hold (For example “Compilers and build system need to be kept separated”. Do they? To what extent?).
I think you just put your finger on why Google is a monorepo - Blaze/Bazel kinda suck horribly (they feel super dated and could have been made much friendlier if they'd been designed from the ground up knowing what the pain points are, and esp if they weren't basically an ad-hoc set of new languages/syntaxes), but they're also awesome. The huge benefit is that you can make tools that work across everything and track monstrous dependency graphs.
But also languages like rust and swift and before them OCaml and Haskell show that the current prevalent C/C++ ABI is not powerful enough. And in principle, there is not much of a difference between the reason for header-only libraries in C++ and rust not having a composable ABI. It is always about the lack of information one can pass through the ABI.
Interestingly, one does not even need to exchange the definition of a rust function or that of a C++ template for monomorphisation. One could easily define an extensible library that will create the new specialisation for any given input. One would only need to "freeze" the compiler that was used to create the library and ship it with that library. Ok, easier said than done, of course.
With ELF I can take a debugger and attach it to a rust binary as well as a C++ or go binary.
Also, we want to be able to interface different languages in an efficient manner. Now with a universal executable file format that becomes possible, but requires us to have a way to combine different object files.
From here on, I think it becomes obvious that we should have compilation units. A language that does not allow the definition of such a standardized object is a bad citizen, IMO. Rust, for instance does allow it, but is severely hampered by the limitations of the C-ABI.
Additionally, I don't see how removing translation units precludes multi-language integration.
Can complex binary interfaces be useful though?
People still write software in C. Not because it's a particularly good language but because there is a simple binary interface: symbols and calling conventions. Libraries built from this code can be used from virtually every other language. People actually rewrite stuff in C just to obtain this property.
Complex binary interfaces restrict software reusability. If it's too complex, it will be extremely difficult to build and maintain the links between programs.
Modern languages approach this problem by getting rid of the links. All code and interfaces are visible to the compiler, the resulting binary image is a perfectly optimized version of the whole program and nobody cares about the machinery the compiler outputs. Nothing else can interface with these programs though... I don't see people calling Rust functions from Python without using the C ABI.
If a template function is guaranteed to be exception-safe, and it’s called with a type whose move constructor isn’t noexcept, then the function needs to fallback to the copy constructor to maintain exception-safety.
This happens a lot with the standard library.
The whole section is gold
The removal of such things seems to be driven by dogma as much as good reasons, of which there definitely are.
As an example of what I mean, in Java and C++, class properties are typically accessed by getters and setters, since you might want to change the internal structure of a class without changing the interface. In Python and C#, you can get the same benefit by changing a member variable to a property. As a result, the boilerplate of getters/setters isn't needed.
It is the only advanced tool available in C and C++, where people hate it. But for some reason Java developers are great fans (even though reflection is available), and their generators will always accept XML. It's still the first time I see a C# developer asking for it.
Read the paragraph following point 3. in the following link, and laugh or cry (your choice): https://man7.org/linux/man-pages/man3/pthread_cleanup_push.3...
Thinking in terms of strings of text instead of the abstract syntax tree is literally the roman numerals of computer science.
Price US$7.9 million in 1977 (equivalent to $33.3 million in 2019) Weight 5.5 tons (Cray-1A) Memory 8.39 Megabytes
https://en.wikipedia.org/wiki/Cray-1#Cray-1M in the 1970s.
Dig up the spec of the PDP and be, well, something'd. Not sure what. Glad you were born now, I guess.
Seriously, the fact that textual macros are terrible isn't even a controversial point. Maybe I'm too young to be a Real Programmer(tm), but this 'everything is text' idea never made sense to me.
As a Programmer(tm), you spend more time debugging code (or specifically, reading it) than writing code. Everything as text makes this less painful than sussing it out of ASM behavior. It's a natural conclusion, given enough time.
There are cases where a simple textual macro is fine, and that's what the designers probably envisioned. But it's a slippery slope: because the macro system is there, it will be used, and because it will be used, it will shape how the language looks around itself.
If your position is "They shouldn't have used something with that complicated a syntax", that might be a defensible point, but now we're not talking about the preprocessor.
constexpr + inline would have covered 99% of the macro use cases. Alternatively, a limited system of AST rewriting wouldn't have been significantly harder to write than cpp and would have achieved many of the same goals.
As a totally random, non-factual, non-argumentative guess that could be 100% wrong, I believe the idea of the preprocessor was influenced by the general 'unix philosophy' principle that everything should be treated as text.
C was designed as a language for small sytems. It's used for ridiculously large and inapprpriate thinga now. The #include is terrible but it was ok back then - things were small enough to hold in your head. No longer.
Compilers were simpler back when C was designed. IIRC PDPs often had just a few K of memory. A megabyte would be huge in the 80's. A textual preprocessor can be done in a couple of K I'd guess, or less. Also a preprocessor is useful for many other things unrelated to the C language.
I understand your frustration but things started that way for a good reason, please don't criticise unless you really understand that.
Feel free to criticise the inertia that means we have much the same stuff nearly half a century later (but also understand why).
POSIX.1 permits pthread_cleanup_push() and pthread_cleanup_pop() to
be implemented as macros that expand to text containing '{' and '}',
respectively.
I'm sorry, what? Why would you permit that? On Linux, the pthread_cleanup_push() and pthread_cleanup_pop()
functions *are* implemented as macros that expand to text containing
'{' and '}', respectively. This means that variables declared within
the scope of paired calls to these functions will be visible within
only that scope.
Yeah, and people call me a joker. But I wouldn't manage to invent this even if I tried to.You could have lispish ast transforms (rust, julia, elixir) You could have comptime (zig, elixir)
both of these are far nicer than preprocessor.
That is blatantly and obviously false:
#define BEGIN {
#define END }
int main(void) BEGIN int x = 0; return x; ENDOne of the use cases of preprocessors is to accommodate several versions of the language. Obviously, if the AST changes between versions, then no AST rewriter can help.
We have had to bake our own ad-hoc simple preprocessor for OCaml to accommodate various versions of its stdlib API, for instance.
Thanks for pointing me at AST transformers.
There are other things too. But thanks for the link, will read up.
Be careful what you wish for.
If it's repetitive boilerplate, you could probably use T4 to generate it for you.
Are you able to share your use-case for one in C#?
That being said, Enums in C# are crap compared to Java.
> You can also create reasonably 'generic' extension methods that will work on 'any' Enum
Can you post a link or something to this, I didn't know it was possible and it will prove useful (even if only as an exercise).
> Enums in C# are crap compared to Java.
yessss, interesting isn't it. Some features of C# seem to be held back so as to make the next release look more attractive, do you think?
https://dotnetfiddle.net/xwYCrt
You can also use the Enum static functions in order to get metadata on any enum. If you use attributes such as [Display("Pretty Name")] you can use that to retrieve the value from an enum.
Now the [Display("Pretty Name")] is what I want! It apparently uses reflection, but for my app that's a very acceptable cost. I can see a few hundred LOC disappearing - thanks so much!