Suppose I have the following two files, main.cpp
:
#include <iostream>
class A {};
void foo();
int main(void)
{
try {
foo();
}
catch(const A& e) {
std::cout << "Caught an A." << std::endl;
}
return 0;
}
and foo.cpp
:
class A {};
class B : public A {};
void foo()
{
B b;
throw b;
}
Now, when I compile each of these files separately, link the resulting object files, and run the resulting executable, I get the expected result:
$ clang++ --std=c++14 -c main.cpp
$ clang++ --std=c++14 -c foo.cpp
$ clang++ --std=c++14 main.o foo.o
$ ./a.out
Caught an A.
And that boggles my mind! Class A
has no virtual methods. Therefore, it is not polymorphic and its instances should carry no type information at runtime. The main.o
object file is unaware of what is being thrown, since the actual throwing takes place inside foo()
, whose body is defined in a separate compilation unit. The foo.o
object file has more information, but is equally unaware of any catch statements and the expected types of caught exceptions.
In short: I do not see how the two source files compiled separately and then linked can produce the above input without having some runtime type information at disposal. Neither file compiled separately should have enough information to take the right catch block.
This is of course completely compiler dependent.
The constraints for all the compilers are:
This implies that the type must be recognized at runtime, even if the exception object is non-plymorphic.
An easy way to achieve this is to pass a pointer to a typeinfo
object together with the thrown object itself. This is the approach used by GCC: see online code, here an extract of throwing in foo()
for convenience:
call __cxa_allocate_exception
mov edx, 0
mov esi, OFFSET FLAT:typeinfo for B ; <== !!
mov rdi, rax
call __cxa_throw