10

I'm just beginning to learn template metaprogramming tricks that allow you to query about a type.

For example, SFINAE allows us to check if a type has a particular typedef or function at compile time by using overloading and return type sizeof comparisons.

Q: So, why doesn't (correct me if there are better ways in C++11/14) the language provide better mechanisms/interfaces to make these queries regarding a type?

Edit: I'd like to clarify that this isn't a rant (I'm sorry if it sounds that way). Since I'm only beginning to appreciate the power of templates, I want to ensure my thought process isn't messed up.

For each template class/function for which I want to make these queries, I'd have to create a SFINAE specific version. Wouldn't a single interface that asks the compiler "does T have a member called A" or "does T have a function <return type> (arg list)" be a cleaner way? Surely the compiler has all the knowledge associated with user defined types - it just needs to expose this to the programmer for compile time queries.

Q : Is relying on this general functionality a bad way of designing/thinking about templates? Please state your reasons (with examples that accomplish the task w/o querying, if possible) if that's the case.

Raja
  • 2,846
  • 5
  • 19
  • 28
  • I don't see how this question is anything more than a veiled rant. It can only solicit opinions. Voting to close. – John Dibling Jan 13 '14 at 21:56
  • Dear [Professor](http://www.stroustrup.com/) Stroustrup.... – Elliott Frisch Jan 13 '14 at 21:57
  • @JohnDibling - I beg to differ. I'm trying to find out if i'm "thinking wrong" about template programming in general. – Raja Jan 13 '14 at 21:58
  • 4
    Two current study groups may be of interest to you: SG7 Reflection and SG8 Concepts. – Joseph Mansfield Jan 13 '14 at 21:59
  • @Raja: Fair enough. If you can restructure the question to seem less like a rant, I'll be happy to either recind my vote to close, or vote to reopen if it goes that far. – John Dibling Jan 13 '14 at 21:59
  • I was tempted to vote to close, but voting to close is an assertion that there do not exist technical reasons why the language does not provide better mechanisms. (If there were technical reasons, this question could be answered objectively by stating those reasons.) So, voting to close is effectively a statement that C++ is deficient in this regard. – Eric Postpischil Jan 13 '14 at 21:59
  • Speed vs Data tradeoff – zero298 Jan 13 '14 at 22:01
  • If you could quantify "better," that would go a long ways toward making this question seem less ranty in my eyes. – John Dibling Jan 13 '14 at 22:02
  • Alright, @Jerry Coffin's edits are good enough for me to vote to reopen. I'd still like to know what "better" means to you, Raja. – John Dibling Jan 13 '14 at 22:04
  • @JohnDibling I've made some edits. I hope it sounds better than the first version. – Raja Jan 13 '14 at 22:08
  • You can consider LLVM if that's your thing. I don't think reflection should be a core part of the language, because of how easily it can be abused. –  Jan 13 '14 at 22:08
  • @Raja: It does, and your question has been re-opened. – John Dibling Jan 13 '14 at 22:11
  • @zero298: How is this "speed vs. data"? Isn't everything happening at compile-time whether it's easy or hard? – user541686 Jan 13 '14 at 22:14
  • @Raja: I think the real answer is that they didn't realize the power of templates when they actually designed them, so they didn't expect them to be used this way. (!) – user541686 Jan 13 '14 at 22:15
  • Yes, but given that SFINAE is quite an old technique now, shouldn't there be better/cleaner ways to accomplish this? – Raja Jan 13 '14 at 22:16

2 Answers2

8

Template metaprogramming, SFINAE tricks & co. weren't actually designed to do this stuff. Template metaprogramming in C++ was moslty discovered.

Templates started just as means to write type-generic code, more capabilities were added to cover corner cases (SFINAE was born to avoid compiler errors that could happen when unrelated templates were pulled in the same program), then some day someone discovered that the C++ templates provided a Turing-complete metalanguage that allowed to perform queries about types, carry out computations at compile time and the like.

So, template metaprogramming in C++ is ugly to understand, ugly to write, ugly to debug, tragic to compile because it's mostly an abuse of stuff that was intended for other usages. Templates just happened to be so powerful, but none actually designed them for this.

C++11 provides some library support to this usage (and some core language support as well), but that doesn't change the essence of the situation.

Also, if you want my opinion, template metaprogramming is currently heavily abused; with it you can build monstrosities like Boost.Spirit, but you probably shouldn't.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • Yes, that explains very well, what I have _felt_ the c++11 language standard was evolving from ... – πάντα ῥεῖ Jan 13 '14 at 22:18
  • i'm curious about other compiled languages. do they have language constructs to interact with the compiler? – Raja Jan 13 '14 at 22:26
  • 1
    Mostly, no. Additional point of interest is that types in the C++ sense are also *abused* in templates for a purpose that they were not designed for, either. What you really want is Haskell or similar type classes which denote a particular interface of the object. The difference is that type classes make no assumption about any hierarchies or similar, they are purely a compile time contract similar to Java interfaces. – user268396 Jan 13 '14 at 22:32
  • 1
    @Raja [Boo](https://github.com/bamboo/boo/wiki) was designed to have [extensible compiler architecture](https://github.com/bamboo/boo/wiki/Boo-Compiler). – Bryan Chen Jan 13 '14 at 22:35
  • Not to necro an old post, but holy smokes is this ever an opinion masquerading as an objective answer, reader beware! – ldog Nov 02 '22 at 05:27
  • @ldog the opinion part is under "if you want my opinion"; the first two paragraphs are objectively how history went to the best of my knowledge; IIRC the first metaprogram calculating π at compile time was posted on the C++ newsgroup almost as a party trick, and to the surprise of Stroustroup himself; SFINAE was not born to perform type queries, that usage was discovered and finally blessed in `enable_if`, which is still a kludge. The 3rd paragraph IMO is still true, albeit with some free snark included (but again: C++ templates error messages are legendary, it's no invention of mine). – Matteo Italia Nov 02 '22 at 07:20
2

SFINAE feels like a hack because it is. What you want to do is reflection.

Now at first it sounds easy but the basic problem lies in the simple question: how do you know what type an arbitrary object is?

In languages with a VM/runtime which knows every object ever created this is solved by asking the runtime/VM directly. In C++ templates happen to offer you an escape route because at some point a template must be instantiated. At that point of instantiation, the compiler must know the type. SFINAE essentially aims to capture this knowledge at compile time and repurpose it to encode instructions which, when evaluated at run time will produce the correct result to determine the type of instantiated template.

SFINAE also happens to require virtually no additional machinery on top of "ordinary" C++ templates that compilers did not already have to implement in the first place.

In order to do proper reflection at compile time without tricks like SFINAE, for a statically linked program with no external dependencies you merely have to solve the halting problem. Which is something sensible people, compiler authors and even language committees very much would like to avoid.

In order to do it in the general case which includes you writing library code that will be linked against in the future as a DSO by some other code... well you have to do time travel into the future just to check that future code. So yeah, that is not going to be possible.

So you need proper run time information about types and methods in order to do what you really want to do. But SFINAE allows you a shortcut because you can use the fact that you pass the type(s) at instantiation to extract that information using the type variable in the template and the compile time sizeof evaluation to export the compile time type variable as constants in the generated code as a side effect of what template instantiation does. As said, the reason it feels like a hack is because it is one.

user268396
  • 11,576
  • 2
  • 31
  • 26
  • "In order to do proper reflection at compile time without tricks like SFINAE, for a statically linked program with no external dependencies you merely have to solve the halting problem." That's only a problem for polymorphic types, isn't it? For non-polymorphic types, compile-time reflection is straight-forward, isn't it? – Raja Jan 13 '14 at 22:53
  • Runtime reflection is not at all required for the OP's query: he asked about _types_, not _objects_. A type definition can't change after compilation. While run-time reflection is handy when you need it, there are plenty of use cases for reflection that don't need to see any deeper than a declared type. – Sean Middleditch Jan 14 '14 at 02:31
  • @SeanMiddleditch would it have been clearer if I'd written 'thing' as in: "how do you know what type an arbitrary **thing** is?" Really at no point do I state that runtime reflection is required for what the OP wants to do. What the OP really wants to do, though is simply not possible in C++ (C++ compilers don't really expose their parse tree to the code under compilation itself) and SFINAE attempts to make up for the missing interfaces in a way. – user268396 Jan 14 '14 at 17:10
  • 1
    I was more responding to the halting problem bits. The compiler has no need to expose a parse tree; it just needs to expose a few more type traits and a couple intrinsics, already being worked on in a working group in the ISO C++ committee (currently I nearly proposal stage). Not possible _today_, you're right on that for sure. :) – Sean Middleditch Jan 14 '14 at 17:56