3 ms·
C++ templates and metaprogramming is fundamentally incompatible with the idea of your code being treated in modules. The current solution chosen by compilers i
by fooker 9mo ago
C++ templates and metaprogramming is fundamentally incompatible with the idea of your code being treated in modules.
The current solution chosen by compilers is to basically have a copy of your code for every dependency that wants to specialize something.
For template heavy code, this is a combinatorial explosion.
- pjmlp 9mo agoIt has worked perfectly fine while using VC++, minus the usual ICE that still come up.
- fooker 9mo agoIt works perfectly when it comes to `import std` and making things a bit easier. It does not work very well at all if your goal is to port your current large codebase to incrementally use modules to save on compile time and intermediate code size.
- pjmlp 9mo agoOffice has made a couple of talks about their modules migration, which is exactly that use case.
- WalterBright 9mo agoD has best-in-class templates and metaprogramming, and modules. It works fine.
- amluto 9mo agoI think that SFINAE and, to a lesser extent, concepts is fundamentally a bit odd when multiple translation units are involved, but otherwise I don’t see the problem. It’s regrettable that the question of whether a type meets the requirements to call some overload or to branch in a particular if constexpr expression, etc, can depend on what else is in scope.
- direwolf20 9mo agoThis is one of those wicked language design problems that comes up again and again across languages, and they solve it in different ways. In Haskell, you can't ever check that a type doesn't implement a type class. In Golang, a type can only implement an interface if the implementation is defined in the same module as the type. In C++, in typical C++ style, it's the wild west and the compiler doesn't put guard rails on, and does what you would expect it to do if you think about how the compiler works, which probably isn't what you want. I don't know what Rust does.
- pornel 9mo agoRust's generics are entirely type-based, not syntax-based. They must declare all the traits (concepts) they need. The type system has restrictions that prevent violating ODR. It's very reliable, but some use-cases that would be basic in C++ (numeric code) can be tedious to define. Generic code is stored in libraries as MIR, which is half way between AST and LLVM IR. It's still monomorphic and slow to optimize, but at least doesn't pay reparsing cost.
- direwolf20 9mo agoHow does it handle an implementation of a trait being in scope in one compilation unit and out of scope in another? That's the wicked problem.
- amluto 9mo agoIt’s impossible (?) due to the “coherence” rule. A type A can implement a trait B in two places: the crate where A is defined or the crate where B is defined. So if you can see A and B, you know definitely whether A implements B. The actual rule is more complex due to generics: https://github.com/rust-lang/rfcs/blob/master/text/2451-re-rebalancing-coherence.md https://github.com/rust-lang/rfcs/blob/master/text/2451-re-r... and that document doesn’t actually seem to think that this particular property is critical.
- fooker 9mo ago
- direwolf20 9mo agoThe compiler is supposed to put the template IR into the compiled module file, isn't it?
- fooker 9mo agoExactly, that's no better than #including transitive dependencies to compile large translation units.