Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 382
Witiko Avatar asked Nov 24 '16 22:11

Witiko


People also ask

How compiler is knowing that which type of exception is occurring?

Compiler does not know what type of exception it just take that instance and then find out the its type using reflection then print the stack's trace on console.

Does order of catch block matters?

The order of catch blocks does matter It's because if we handle the most general exceptions first, the more specific exceptions will be omitted, which is not good, as Java encourages handling exceptions as much specific as possible.

What order is catch blocks?

If a catch block for objects of a base class precedes a catch block for objects of a class derived from that base class, the compiler issues a warning and continues to compile the program despite the unreachable code in the derived class handler. A catch block of the form catch(...)

What is the purpose of catch blocks?

Try/catch blocks allow a program to handle an exception gracefully in the way the programmer wants them to. For example, try/catch blocks will let a program print an error message (rather than simply crash) if it can't find an input file. Try blocks are the first part of try/catch blocks.


2 Answers

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
like image 65
Christophe Avatar answered Oct 07 '22 00:10

Christophe


It's a combination of RTTI (Run-time type information) and the implementation specific encoded type data that is generated for comparing types when trying to assess which catch block gets what.

But simply comparing two types at face value might not yield the proper result (such as in your case with base and derived classes). In case of Windows & SEH, a special additional extended type information structure (etype_info or something like that) exists that contains all the classes in the hierarchy which need to be traversed to determine a potential match (as a base class can be a limiting view into the derived class). If after traversing a match is found, catch block is invoked.

Addendum

Exception handling requires special runtime support provided by the OS and therefore the way this happens is implementation defined (such as Structured Exception Handling in Windows) as long as the end result satisfies the standard.

Upstairs is the Windows-flavored gist of it.

like image 28
Benjamin Sisko Avatar answered Oct 07 '22 00:10

Benjamin Sisko