Witiko Witiko - 12 days ago 6
C++ Question

How does the compiler know which catch block to take?

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.

Answer

This is of course completely compiler dependent.

The constraints for all the compilers are:

  • the type of the exception is known when you throw (either at compile time, or at runtime in the case you'd throw a polymorphic object).
  • the applicable catch blocks (their can be several) and their types depend on the execution path.

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