views:

160

answers:

3

I've noticed that reflection is one feature that developers from other languages find very lacking in c++. For certain applications I can really see why! It is so much easier to write things like an IDE's auto-complete if you had reflection. And certainly serialization APIs would be a world easier if we had it.

On the other side, one of the main tenets of c++ is don't pay for what you don't use. Which makes complete sense. That's something I love about c++.

But it occurred to me there could be a compromise. Why don't compilers add extensions to the std::type_info structure? There would be no runtime overhead. The binary could end up being larger, but this could be a simple compiler switch to enable/disable and to be honest, if you are really concerned about the space savings, you'll likely disable exceptions and RTTI anyway.

Some people cite issues with templates, but the compiler happily generates std::type_info structures for template types already.

I can imagine a g++ switch like -fenable-typeinfo-reflection which could become very popular (and mainstream libs like boost/Qt/etc could easily have a check to generate code which uses it if there, in which case the end user would benefit with no more cost than flipping a switch). I don't find this unreasonable since large portable libraries like this already depend on compiler extensions.

So why isn't this more common? I imagine that I'm missing something, what are the technical issues with this?

EDIT: Just a few metrics re-the bloat argument:

I've looked at a fairly large Qt project (about 45,000 LoC) and measures the size of the metaObjects. I feel this is a reasonable metric because the Qt moc system is a fairly exhaustive reflection system (types, functions, enumerations, members and a few Qt specific concepts like "properties"). There were 67 metaobjects total, so not a trivial amount but nothing crazy, which added up to 5479 bytes. However almost all of them were 32-bytes or less (the largest being 1427 bytes). Considering that modern compilers produce binaries of upwards of 4K for even the simplest program, this these numbers aren't outrageous). Though I'd love to see something like this applied to the STL to see how it fairs.

+1  A: 

Usually the use of reflection is indicative of poor software design; proper use of interfaces and polymorphism is sufficient to do pretty much whatever you would do with reflection. If one were to add extra information into std::type_info, it would indeed lead to program bloat. The issue with templates is not that you cannot generate std::type_info from them, but rather that you can get an explosion of types, and so every instantiation of the template results in yet another std::type_info object that you would need. Your suggestion of using a compiler switch doesn't really help or make sense... first of all, the standard would never specify a compiler switch, because that would be implementation-specific, but assuming it were to do so... what would happen if you wanted to use reflection with a class that came from a library for which reflection was disabled? If most libraries disabled reflection -- which they probably would -- then it would severely limit the utility of that feature, and if most libraries didn't disable it, then you would be paying for it without using it.

Michael Aaron Safyan
I know the standard wouldn't every mandate switches, I'm talking about an extension. And using typeinfo across library boundaries is already considered undefined behavior (IIRC). As for the poor design issue, while this is true for the general case, there are **plenty** of examples where it is considered useful in a good design. See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1751.html for a paper describing various valid benefits from it. Clearly reflection isn't needed for everyday uses, but when you need it, it is significantly simpler than some of the solutions c++ offers.
Evan Teran
Also, `type_info` is already generated for each template instantiation, so the amount of bloat may not be as bad as people predict (though I agree that's a valid concern). For example, Qt uses `moc` to generate all of the reflection information it needs for *every* subclass of `QObject`, this is arguably "bloated" but it's not bad enough to deter the thousands of users of Qt...
Evan Teran
@Michael: There are exceptions where some type of reflection is unavoidable, e.g. when you need to provide interfaces for dynamic languages.
Georg Fritzsche
A: 

Perhaps the multitude of types in C/C++ makes flexible reflective programming difficult. An hypothetical database or serializing interface capable of handling any primitive types would need special cases for [unsigned] int, short, [long] long, char, float, [long] double, arrays thereof, functions relating to, etc. It would amount to an interface to the name mangler.

Not that I think that's a good reason. I wrote a reflective SQL interface in Boost MPL several years ago, and it was troublesome. It would have been much nicer and easier with a "native" facility.

I think most people achieve reflection with additional preprocessing, such as Apple's Mach Interface Generator and similar tools at GNU and Microsoft.

More surprising to me than the lack of support in C++ is that in the up-and-coming languages that drive competition, such as D and (in spirit anyway) Go.

Potatoswatter
you make a decent point, but primitive types are fairly short to deal with. They aren't objects with properties or anything like that, so each would like be only a few (maybe 1) line to deal with. Just look at `numeric_limits<T>`, there is a specialization for every basic integer type and that's not **that** bad to deal with implementation wise.
Evan Teran
Correct. Users could shoot themselves in the foot, but C++ isn't about always ensuring safety.
Potatoswatter
+1  A: 

Instead of having a certain runtime reflection approach forced on me, i'd rather have compile time reflection capabilities that i could use to generate the information i need.

That way i wouldn't have to pay for information i don't actually use and could use meta-programming to generate interfacing code as needed without additional pre-build steps or some elaborate declaritive EDSL.

Caspin mentioned one possible approach here. To be honest, i am surprised that the subject seems to have never been pushed as a proposal for inclusion in C++.

Georg Fritzsche
Compile time reflection requires compile time iterators. The interface for data structures would have to be standardized before the structures could. Boost MPL would be the prototype. Maybe this will happen as language features and error messages get more polished.
Potatoswatter