Why does C++ not have reflection?

There are several problems with reflection in C++.

  • It’s a lot of work to add, and the C++ committee is fairly conservative, and don’t spend time on radical new features unless they’re sure it’ll pay off. (A suggestion for adding a module system similar to .NET assemblies has been made, and while I think there’s general consensus that it’d be nice to have, it’s not their top priority at the moment, and has been pushed back until well after C++0x. The motivation for this feature is to get rid of the #include system, but it would also enable at least some metadata).

  • You don’t pay for what you don’t
    use. That’s one of the must basic
    design philosophies underlying C++.
    Why should my code carry around
    metadata if I may never need it?
    Moreover, the addition of metadata
    may inhibit the compiler from
    optimizing. Why should I pay that
    cost in my code if I may never need
    that metadata?

  • Which leads us to another big point:
    C++ makes very few guarantees
    about the compiled code. The
    compiler is allowed to do pretty
    much anything it likes, as long as
    the resulting functionality is what
    is expected. For example, your
    classes aren’t required to actually
    be there. The compiler can optimize them away, inline
    everything they do, and it
    frequently does just that, because
    even simple template code tends to
    create quite a few template
    instantiations. The C++ standard
    library relies on this aggressive
    optimization. Functors are only
    performant if the overhead of
    instantiating and destructing the
    object can be optimized away.
    operator[] on a vector is only comparable to raw
    array indexing in performance
    because the entire operator can be
    inlined and thus removed entirely
    from the compiled code. C# and Java
    make a lot of guarantees about the
    output of the compiler. If I define
    a class in C#, then that class will
    exist
    in the resulting assembly.
    Even if I never use it. Even if all
    calls to its member functions could
    be inlined. The class has to be
    there, so that reflection can find
    it. Part of this is alleviated by C#
    compiling to bytecode, which means
    that the JIT compiler can remove
    class definitions and inline
    functions if it likes, even if the
    initial C# compiler can’t. In C++,
    you only have one compiler, and it
    has to output efficient code. If you
    were allowed to inspect the metadata
    of a C++ executable, you’d expect to
    see every class it defined, which
    means that the compiler would have
    to preserve all the defined classes,
    even if they’re not necessary.

  • And then there are templates.
    Templates in C++ are nothing like
    generics in other languages. Every
    template instantiation creates a
    new type. std::vector<int> is a completely separate class from
    std::vector<float>. That adds up to
    a lot of different types in a entire
    program. What should our reflection
    see? The template std::vector? But
    how can it, since that’s a
    source-code construct, which has no
    meaning at runtime? It’d have to see
    the separate classes
    std::vector<int> and
    std::vector<float>. And
    std::vector<int>::iterator and
    std::vector<float>::iterator, same
    for const_iterator and so on. And
    once you step into template
    metaprogramming, you quickly end up
    instantiating hundreds of templates,
    all of which get inlined and removed
    again by the compiler. They have no
    meaning, except as part of a
    compile-time metaprogram. Should all
    these hundreds of classes be visible
    to reflection? They’d have to,
    because otherwise our reflection
    would be useless, if it doesn’t even guarantee that the classes I defined will actually be there. And a side problem is that the template class doesn’t exist until it is instantiated. Imagine a program which uses std::vector<int>. Should our reflection system be able to see std::vector<int>::iterator? On one hand, you’d certainly expect so. It’s an important class, and it’s defined in terms of std::vector<int>, which does exist in the metadata. On the other hand, if the program never actually uses this iterator class template, its type will never have been instantiated, and so the compiler won’t have generated the class in the first place. And it’s too late to create it at runtime, since it requires access to the source code.

  • And finally, reflection isn’t quite
    as vital in C++ as it is in C#. The
    reason is again, template
    metaprogramming. It can’t solve
    everything, but for many cases where
    you’d otherwise resort to
    reflection, it’s possible to write a
    metaprogram which does the same
    thing at compile-time.
    boost::type_traits is a simple
    example. You want to know about type
    T? Check its type_traits. In C#,
    you’d have to fish around after its
    type using reflection. Reflection
    would still be useful for some
    things (the main use I can see,
    which metaprogramming can’t easily
    replace, is for autogenerated
    serialization code), but it would
    carry some significant costs for
    C++, and it’s just not necessary as often as it is in other languages.

Edit:
In response to comments:

cdleary:
Yes, debug symbols do something similar, in that they store metadata about the types used in the executable. But they also suffer from the problems I described. If you’ve ever tried debugging a release build, you’ll know what I mean. There are large logical gaps where you created a class in the source code, which has gotten inlined away in the final code. If you were to use reflection for anything useful, you’d need it to be more reliable and consistent. As it is, types would be vanishing and disappearing almost every time you compile. You change a tiny little detail, and the compiler decides to change which types get inlined and which ones don’t, as a response. How do you extract anything useful from that, when you’re not even guaranteed that the most relevant types will be represented in your metadata? The type you were looking for may have been there in the last build, but now it’s gone. And tomorrow, someone will check in a small innocent change to a small innocent function, which makes the type just big enough that it won’t get completely inlined, so it’ll be back again. That’s still useful for debug symbols, but not much more than that. I’d hate trying to generate serialization code for a class under those terms.

Evan Teran: Of course these issues could be resolved. But that falls back to my point #1. It’d take a lot of work, and the C++ committee has plenty of things they feel is more important. Is the benefit of getting some limited reflection (and it would be limited) in C++ really big enough to justify focusing on that at the expense of other features? Is there really a huge benefit in adding features the core language which can already (mostly) be done through libraries and preprocessors like QT’s? Perhaps, but the need is a lot less urgent than if such libraries didn’t exist.
For your specific suggestions though, I believe disallowing it on templates would make it completely useless. You’d be unable to use reflection on the standard library, for example. What kind of reflection wouldn’t let you see a std::vector? Templates are a huge part of C++. A feature that doesn’t work on templates is basically useless.

But you’re right, some form of reflection could be implemented. But it’d be a major change in the language. As it is now, types are exclusively a compile-time construct. They exist for the benefit of the compiler, and nothing else. Once the code has been compiled, there are no classes. If you stretch yourself, you could argue that functions still exist, but really, all there is is a bunch of jump assembler instructions, and a lot of stack push/pop’s. There’s not much to go on, when adding such metadata.

But like I said, there is a proposal for changes to the compilation model, adding self-contained modules, storing metadata for select types, allowing other modules to reference them without having to mess with #includes. That’s a good start, and to be honest, I’m surprised the standard committee didn’t just throw the proposal out for being too big a change. So perhaps in 5-10 years? 🙂

Leave a Comment