6

Consider this example:

struct Nobody_Expects_The_Spanish_Inquisition{};

int main(){
    throw Nobody_Expects_The_Spanish_Inquisition();
}

Output shown on Ideone:

terminate called after throwing an instance of Nobody_Expects_The_Spanish_Inquisition'

Similar output for Windows:

Unhandled exception at 0x760fb727 in Test.exe: Microsoft C++ exception: Nobody_Expects_The_Spanish_Inquisition at memory location 0x001ffea3..

As can be seen, the final assembly seems to contain the name of the exception already, or there is another method to acquire the name.

Can this be seen as some kind of reflection? Or is it compiler/OS dependent if the name of the exception can actually be displayed?

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • For everything deriving from `std::exception`, this is easily explained, since it has a member `what()` which returns a descriptive string. Obviously your example works nevertheless (without that base class), which I think is a MSVC "special feature". There is `typeid` too, of course, which is probably the same mechanism that the compiler secretly uses here too. – Damon Jun 10 '11 at 22:03
  • @Damon: Ideone uses GCC. Also, the output displays the name of class, not the return of the `what()` function. – Xeo Jun 10 '11 at 22:03
  • @Xeo: gcc does that kind of thing? Cool... I've never noticed (though probably because I always catch all). Nice to see it works in such a descriptive way, though. – Damon Jun 10 '11 at 22:09
  • 3
    @Damon I don't think this has anything to to with what(), as the OP's question does not use an exception derived from std::exception. –  Jun 10 '11 at 22:09
  • 1
    Reflection is usually defined as the program's ability to introspect portions of itself, using programming language features. Unhandled runtime exceptions seem not to meet any of these requirements, so I don't see how you got from this example to "reflection" at all. Debug text strings embedded in object files don't count. – Ira Baxter Jun 10 '11 at 22:12
  • I am always a little surprised that nobody even mentions `typeid` / RTTI whenever "C++ and reflection" comes up. – Nemo Jun 10 '11 at 22:26
  • 1
    @Nemo I can't think why you bring them up in this context - they have nothing to do with exceptions. –  Jun 10 '11 at 22:31
  • @Neil: True. But I also followed the "Why does C++ not have reflection?" link below and nobody seems to have mentioned it there, either. (It's not "true" reflection, but it is the closest thing C++ offers, to my knowledge.) – Nemo Jun 10 '11 at 22:35
  • @Damon: You should catch `std::exception` first, and then `catch all` as a last resort. If you happen to catch a `std::exception`, then you can make use of `std::exception::what` member function to log a more meaningful error message. – Emile Cormier Jun 10 '11 at 22:39
  • @Nemo: My link "Why does C++ not have reflection" specifically but breifly mentions RTTI as something that might be considered as reflection. – Ira Baxter Jun 10 '11 at 22:48
  • @Nemo: Nonsense. Reflection has nothing to do with it. By your logic, C++ has reflection because types are named in compilation errors. Just because the information is carried through to the executable in this special case doesn't make it reflection: the program has no access to this information (in the case where it didn't already have that through RTTI). – Lightness Races in Orbit Jun 18 '11 at 23:58
  • No. See my SO answer: http://stackoverflow.com/questions/359237/why-does-c-not-have-reflection/1355664#1355664 – Ira Baxter Jun 10 '11 at 22:02
  • Not relevant, your answer says nothing on the topic (as long as I didn't overlook anything). I asks if the above can be considered reflection, not why C++ doesn't have reflection such as C#. Also, this should be a comment. – Xeo Jun 10 '11 at 22:05
  • This directly answers your question: "Does C++ already have some kind of reflection?" My answer: " No, the language designers didn't require it, and the compiler guys didn't put it in". How is that answer not relevant? – Ira Baxter Jun 19 '11 at 05:43

5 Answers5

7

It's compiler dependant. Obviously, it's easy for the compiler to spot every throw, and encode the type of every thrown object into the executable. But there is no requirement that they should do this.

And thinking about it, exceptions have to be copied into a weird implementation-dependent space when they are thrown. So it makes sense that the name of their type is accessible via this mechanism to the runtime of a specific compiler.

  • It's not a special mechanism, most likely. Most likely it's just the existing `typeid` system. – Omnifarious Jun 11 '11 at 00:55
  • 1
    @Omnif Most likely not. And if you are going to downvote, at least have the guts to be open about it. –  Jun 11 '11 at 00:59
  • @Neil Butterworth: Alright then. -1 because I think there are other answers (@Luc Danton's answer for example) that are much better than this one. – Omnifarious Jun 11 '11 at 01:07
  • @Omnif Better in the sense that it no way addresses the question? –  Jun 11 '11 at 01:10
  • 1
    @Neil Butterworth: In that it almost completely addresses the OP's question in a fashion that's clearer and more to the point. The only thing it's missing is pointing out that RTTI is a very primitive form of reflection, and that's a fairly minor thing. This answer completely avoids the question about reflection and only answers the question of how the terminate handler knows the information it's printing out, and then it does it in a way that's less clear. – Omnifarious Jun 11 '11 at 01:19
  • 2
    @Omni: -1 because there are better answers? Unjustified. A -1 should be given if an answer advices bad practices or is flat-out wrong, not because one thinks the answer isn't the best. Come on... – Xeo Jun 12 '11 at 08:33
  • @Xeo: Absolutely right. You can determine the most popular answers by comparing scores. You most certainly don't downvote every answer that's not your favourite. – Lightness Races in Orbit Jun 18 '11 at 23:56
3

No, it's not reflection in any meaningful capacity, just debug symbols.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 1
    Actually, with MinGW gcc if you compile without debug and strip the executable, you still get the symbolic name on a crash. I agree it's not reflection. –  Jun 10 '11 at 22:19
  • As @Neil says, even in release mode w/o debug symbols, the names still get displayed. – Xeo Jun 10 '11 at 22:33
  • 1
    No, I don't think I will. The symbolic name is only of worth to the developer, when debugging. That makes it debug symbols. Just because it didn't compile into a separate file to be inspected by the debugger in-process doesn't make it not debug symbols- and indeed, they're useless for any purpose except debugging. – Puppy Jun 10 '11 at 22:57
  • @Dead I compiled without the -g flag and stripped the executable. There were no debug symbols. In fact, you still get the same symbolic name when the program crashes when compiled with -O2 and stripped, which is about as near to a non-debugged executable as you can get. If you know how to remove the symbolic names for the exceptions, please say. –  Jun 10 '11 at 23:08
  • This violent agreement could stop if the answer were amended to refer to e.g. "debug information" rather than "debug symbols", which does have a very specific meaning. – Luc Danton Jun 10 '11 at 23:12
  • @Luc If you think this is violent, you ain't seen nothing yet! And me and DeadMG are friends? colleagues? Anyway, not particularly opposed. And your proposed solution is wrong. –  Jun 10 '11 at 23:15
  • @Neil 'violent agreement' is not of my invention and I accept that my suggestion is just that. – Luc Danton Jun 10 '11 at 23:18
2

When an exception escapes main, std::terminate is called, which in turn calls the installed terminate handler. If no terminate handler were set in the program, the default terminate handler is called. The only requirement on this default terminate handler is that it calls std::abort. This means an implementation is free to print a helpful message before calling std::abort, which is apparently the case here.

While reflection or debug symbols or RTTI are sufficient to print this error message, they are not necessary: the implementation can use any kind of black magic, no matter how deep.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
0

This program:

#include <typeinfo>
#include <iostream>

struct Nobody_Expects_The_Spanish_Inquisition {
};

namespace junk {
struct I_didnt_expect_a_kind_of_spanish_inquisition
   : public Nobody_Expects_The_Spanish_Inquisition
{
};
}

int main(int argc, const char *argv[])
{
   using ::std::type_info;
   using ::std::cout;

   Nobody_Expects_The_Spanish_Inquisition foo;
   junk::I_didnt_expect_a_kind_of_spanish_inquisition bar;
   const type_info &fooinfo = typeid(foo);
   const type_info &barinfo = typeid(bar);
   cout << "The type of foo is <" << fooinfo.name() << ">\n";
   cout << "The type of bar is <" << barinfo.name() << ">\n";
   return 0;
}

has this output:

$ ./foo 
The type of foo is <38Nobody_Expects_The_Spanish_Inquisition>
The type of bar is <N4junk44I_didnt_expect_a_kind_of_spanish_inquisitionE>

This is as good as it gets for introspection in C++. And this is just barely enough to accomplish what the default terminate handler is doing.

While, as others have pointed out, the default terminate handler is allowed to accomplish this goal anyway it darn well pleases, I'd be surprised if it didn't make use of the same mechanisms that are used to implement typeid to make this work.

Of course, the default terminate handler could work by accessing a special area the compiler creates whenever an exception is thrown that records whatever the compiler knows about the name of the type at the spot where it's thrown. As other's have pointed out, the default terminate handler is put there by the compiler and is not bound by any of the rules code written by a C++ programmer would have to follow.

I've seen people write their own terminate handlers that hand-walk the call stack and lookup debug symbols associated with each address to get some facsimile of a stack-trace. These are compiler and platform specific magic, and since the compiler knows exactly which compiler it is and what platform its being used on, it could have a default terminate handler that did the same thing on supported platform.

In summary, RTTI is not required to implement the feature you're noting. But RTTI is a very rudimentary form of reflection, and could be used to implement that feature.

Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • The point is, for at least GCC, the compiler does all this for you. –  Jun 10 '11 at 23:55
  • @Neil Butterworth: So it does. But it likely does this just by having the default "Oops, `main` terminated with an exception!" code use the existing `typeid` framework to figure out which exception it is. – Omnifarious Jun 11 '11 at 00:54
-1

The "reflection" is made using RTTI. Nothing fancy, just the name of the type.

Jörgen Sigvardsson
  • 4,839
  • 3
  • 28
  • 51
  • 1
    Disabled RTTI, still get the exception name, so no, RTTI is not used. – Xeo Jun 10 '11 at 23:02
  • @Xeo: Just because you 'disabled RTTI' doesn't mean that the `typeid` portion isn't set up for types that are thrown as exceptions anyway. It basically has to be in order for the code that prints out the name to work. – Omnifarious Jun 11 '11 at 01:01