8 ms·
Is the problem about C++ templates it's error messages or not being able to strictly type the arguments in type signature? I have a question to the compiler /
by chakkepolja 5y ago
Is the problem about C++ templates it's error messages or not being able to strictly type the arguments in type signature?
I have a question to the compiler / IDE people here. In case of C++ templates, is it possible to output one error like: eg "class UserData needs to implement operator< to be used in min() function" instead of 200 lines errors? I mean, complex cases it's not possible but at least for simple ones like using an iterator variable directly instead of dereferencing it?
- yakubin 5y agoThose two are connected. Without strict typing the compiler doesn't know really whether the error is in the definition of min() or in its use, so the compiler mangles the two together and lets the programmer figure it out. > In case of C++ templates, is it possible to output one error like: eg "class UserData needs to implement operator< to be used in min() function" instead of 200 lines errors? This is already handled by concepts, but concepts solve only half the problem. E.g. in my snippet, in the comment you replied to, if any of the structs didn't have the kId constant, the errors would be about that. Concepts correctly detect when someone passes to them things that don't implement required functionality (as long as this functionality is specified in the concept). The other half of the problem, which they do not solve, is checking the correctness of the definition of the template against the concept. E.g. in my snippet I could use the "value" field, even though the concept that I used didn't declare it anywhere. At that point, if I passed a struct, which doesn't have that field, the compiler wouldn't be able to tell where the error is: in the template, or in its use --- so it would emit one error for each template instantiation, just as it did for "kIs" (because it can't know that it's just one typo in one place).
- tialaramex 5y ago> Concepts correctly detect when someone passes to them things that don't implement required functionality Unfortunately C++ Concepts check syntax even though as you'd expect the intention of the feature is to reflect semantics there is deliberately no way to capture that. So e.g. Rust has traits PartialEq and Eq reflecting things that claim to be partial or full equivalence relations. There's no syntactic difference, a Rust type can't syntactically be impossible to check for equivalence with some values and not others but e.g. Rust's f32 implements PartialEq not Eq due to NaNs whereas u16 has both since integers all exhibit equivalence. But in C++ there's just std::equality_comparable and it matches both integers and floats even though NaNs aren't in fact comparable because the float type syntactically has the == operator. So this means in C++ concepts matching means your program compiles but it might blow up at runtime thanks to a difference in functionality rather than syntax. To C++ programmers this is positioned as yet further responsibility on their shoulders. They must check that the data they're using actually models the concept that was required, it's not anybody else's job and if they get it wrong the program blows up.
- aseipp 5y agoI mean, I feel this particular example is really more of standard library design wart that occurs with any kind of syntactic overloading mechanism, e.g. Haskell has the exact same problem as C++ does here even though the overloading mechanism is rather different (Eq is defined for Float and there is no notion of a PartialEq in the standard library, even though there really should be), and there are numerous more advanced examples of that (every few years someone writes a fun library that violates the Monad laws, for instance). The fundamental question is: does an instance of an overloading mechanism respect the "properties" you expect of it, algebraic ones or not? Your program will often behave weirdly if the answer is "no" but the extent of the weirdness permissible is a spectrum. Basically no languages actually allow you to validate those semantics; it's expected that the "validity" of those instances is proven or known a priori, and bad ones don't exist. IMO, the bigger problem with this particular example is just that the numeric and algebraic hierarchies in most languages are littered with various edge cases just like this one, and that makes it an easy and convenient nitpick to point out. But I think the parent comment is largely correct in that the goal of a concept (or trait, or typeclass) is roughly to reflect a property or action of some object or type or what have you, even if C++ wasn't as originally as forward thinking as other languages today are with invariant safety.
- tialaramex 5y agoThe practical difference between C++ Concepts and Rust Traits is that Rust Traits get to have semantics by declaration and you can't do that with C++ Concepts. A Rust type gets to choose whether to be Eq or just PartialEq. I can wrap floats up in a new type, MyFloat, provide an implementation of == to qualify it as PartialEq and then I can choose whether to just declare that it's Eq. The compiler cannot check I told the truth, but as implementer I can decide whether to promise that or not. In C++ you do not get a choice. C++ concepts refer to syntax and only that. I can write a concept named hates_balloons and have it depend on types implementing a zero argument method named "pop". But if your stack class with a pop method doesn't in fact hate balloons too bad, my concept says that since the syntax matches you have no choice, your stack hates_balloons and there is nothing you can do about it. I understand exactly why C++ did this. There is a lot of legacy C++ code out there. If you make every new C++ iterator say it implements the iterator concept and don't grandfather the existing ones, nobody uses the feature. But, even though they had a good reason to do this, it's still a considerable weakness.
- chakkepolja 5y agoI was asking about the no-concept case. I was thinking something like this: Compiler encounters operator<(T) on argument (type T) is called. In best case it sees it was a function parameter. In worst case it at least sees concrete type of the parameter. Let's say that type is UserData It will say that UserData type needs to implement UserData::operator<(UserData) to be able to use min() function. Of course I can see this getting complex as nesting etc.. increase. But I am wondering if clang (since it's selling points include error messages, though gcc is catching up), hasn't tried something like this, there must be a fundamental technical difficulty for generalizing such a technique.