Here’s another post in the series, this time it’s C++ vs. the D programming language.
Let’s talk about templates.
If you’ve ever tried templates in C++ you surely as hell recall the pages and PAGES of compiler errors and seemingly random placement of typename keyword.
Trust me, there are EVEN WORSE problems with templates in C++… Consider the following:
1: struct Foo { 2: template<int N> 3: void bar() {} 4: 5: template<int N> 6: struct Bar {}; 7: }; 8: 9: template<typename T> 10: void f() { 11: T foo; 12: foo.template bar<0>(); 13: typename T::template Bar<0> b; 14: } 15: 16: int main() { 17: f<Foo>(); 18: } 19:
This little snippet is pretty simple, but… OH GOD! Lines 12 and 13. What the hell?
Line 12 shows the so-called object.template function<arg>() syntax and it is probably the least known C++ syntax out there.
The reason it exists is simple – in this particular corner case the compiler simply… can’t… parse… this code.
When parsing f() functions definition it can’t possibly know that T foo has a templated member bar and confuses the template opening bracket with the less-than operator, so we end up with an unresolved overloaded function type error. That’s harsh.
Line 13 contains a similar corner case where we need to use all kinds of messed up syntax just to declare a variable.
The typename tells the compiler that we want a type because you know, it won’t figure it out on its own. Next, we access with a :: the templated member type Bar instantiated with a 0. The template is there for the same reason it’s found on line 12.
Here’s the semantically equivalent code in the D programming language:
1: struct Foo { 2: void bar(int N)() {} 3: 4: struct Bar(int N) {} 5: } 6: 7: void f(T)() { 8: T foo; 9: foo.bar!0(); 10: T.Bar!0 b; 11: } 12: 13: void main() { 14: f!Foo(); 15: } 16:
The analogous lines (9 & 10) will always mean the same and don’t require disambiguation with any keywords.
C++’ template problem may not seem that serious, but imagine more complicated templates and, oh snap, debugging their code and all the weird stuff that happens when you forget a random template keyword somewhere.
And I didn’t even start ranting about…
// ...C++11. * template <size_t k, class T, class... Ts> typename enable_if<k != 0, typename tuple_element<k, tuple<T, Ts...>>::type&>::type // WAT get(tuple<T, Ts...>& t) { tuple<Ts...> & super = t; return get<k - 1>(super); }
To sum it up, D’s template syntax is simpler, corner cases don’t exist and declarations are cleaner. This is a yet another reason I like D so much.
* The snippet comes from an awesome presentation given by Andrei Alexandrescu on the Going Native 2012 conference a while ago. I didn’t get into any details because it’s a topic for another rant. Stay tuned.
hmm. Very interesting, how then to define template template parameters in D?
A nice example is the `std.functional.memoize’ function comming from D’s strandard library Phobos:
ReturnType!fun memoize(alias fun)(ParameterTypeTuple!fun args) { // Implementation... }This simplified version takes a function as its template parameter and has both templated arguments and templated return type.
The `ParameterTypeTuple!fun’ extracts a `TypeTuple’ of `fun’s parameter types and the `ReturnType!fun’ is rather selfexplanatory.
As you can see first parentheses in a function declaration contain template parameters that can be used both in the runtime parameters and the return type and there is no need for a `typename’ keyword.
Hopefully this will give you a general idea, because I really, really don’t want to decipher any C++11 code… ;)
got it. thx. interesting – is it possible to provide the same syntax in C++ without violating existing?
Pingback: Templated Monads? Monadic Templates? | I do robots