Search code examples
c++functionpointerstry-catchdereference

What does the variable declaration `const int *__errno_location ()` inside this catch statement actually do?


While working on an exercise for my C++ class where we were working on Exception handling, I came across a puzzling issue. I initially had the following exception handler (Please don't roast the use of ints as exceptions, this was the task we were given):

#include <iostream>
//...

try {
    // Code that throws an int
}
catch(const int errno) {
    std::cout << errno << std::endl;
}

To the untrained eye, this would seem like a normal declaration of an integer exception variable. However, the unsafe code kept evading the catch block, despite throwing an integer itself. After some investigation, it turned out that a Macro in errno.h declared #define errno (*__errno_location ()) caused a Macro Expansion into the following code

try {
    // Code that throws an int
}
catch(const int (*__errno_location())) {
    std::cout << (*__errno_location()) << std::endl;
}

I was unsure what exactly is being declared in the catch Expression. I tried using C Gibberish to English, but unsurprisingly that didn't help much, telling me that it was declaring a function returning an int*. ChatGPT was also puzzled by this expression, calling it "unconventional formatting".

Playing around with it a little, I came up with the following code:

const int *blah() {
    return new int(10);
}

int main() {
    try {
        throw &blah;
    }
    catch(const int *foo()) {
        std::cout << *foo () << std::endl;
    }
}

Which outputs the number 10 to the console. Changing the values in the integer constructor also causes that integer to be output to the console as well. So my current hypothesis is that const int *foo() is declaring that a function pointer returning an int pointer be caught in this catch block, and in the print statement we simply call that function and dereference the pointer. I would like my hypothesis be verified or corrected by more experienced C++ programmers though.


Solution

  • errno is implementation defined. See cppreference:

    errno is a preprocessor macro used for error indication. It expands to a static(until C++11)thread-local(since C++11) modifiable lvalue of type int.

    From what you already found out, we can conclude that __errno_location() is a function (or some callable) returning a pointer to int which encodes the last error on the implementation you are using.

    As mentioned in a comment, the identifier errno is reserved by POSIX.

    Now, what happens in your code is more or less this:

    int* foo() { 
        static int x = 42;
        return &x;
    }
    
    int main() {
        try {
    
        } catch(const int (*foo()) ){
    
        }
    }
    

    Which also compiles (see here: https://godbolt.org/z/3v8nsPr74).

    The reason is that const int (*foo()) is actually declaring a parameter named foo of type const int(*)(), ie a pointer to a function with no parameter and returning a const int.

    And as you already discovered, the name is just coincidence. Your parameter is called __errno_location, and inside the catch block it shadows the actual function with the same name.

    The lesson to learn here is how evil macros are. They pollute the global namespace. That's why you had the surprise when using errno as the name of the parameter. And secondly, macros are evil, because an innocent-looking name can turn a const int* into something of a totally different type. Unfortunately, here you have no choice to not use the macro. You only have to be careful to avoid the name.