2
votes

In my code I throw my custom file_error exception, which is derived from std::runtime_error. In a different module I catch exceptions for that operation and want to handle my file_error like this:

try
{
  base::create_directory(INVALID_NAME, 0700);
  ...
}
catch (const base::file_error &exc)
{
   ...
}
catch(std::runtime_error &exc)
{
    ...
}
catch(std::exception &exc)
{
  ...
}

file_error is declared as:

namespace base {
class file_error : public std::runtime_error
{
  int sys_error_code;
  public:
    file_error(const std::string &text, int err);

    error_code code();
    int sys_code();
};
}

However, the catch branch for file_error is never triggered. Either I end up in the runtime_error branch or, if I remove that, in the exception branch.

However, this works fine in Linux + Win (gcc, VS) while it does not work on Mac (clang). Any idea what could be wrong here?

Update:

Here's the lldb output when I reach the runtime_error branch:

(lldb) p exc
(const base::file_error) $0 = {
  std::runtime_error = {
    __imp_ = (__imp_ = 0x0000000000000000)
  }
  sys_error_code = 13923331
}

That clearly indicates the exception is indeed of type base::file_error. It's just not catched in its associated catch block.

Update 2:

Declaring an own error based on file_error in the same file as the test code above like this:

class test_error : base::file_error {

public:
  test_error(const std::string &s) : base::file_error(s, 0) {};
};

allows me to catch it in a test_error block and in a catch-all block, but not in base::file_error, std::runtime_error or std::exception blocks. Weird.

Update 3:

After a lot of experimenting I now think this is a type mismatch issue, similar to the ODR-violation but a different kind. The types in the dylib and in the test app are not considered the same and hence the exception is not catched, unless I throw that base::file_error exeption directly in the test code.

2
That might suggest that you have an ODR violation: multiple definitions of base::runtime_error are used by different parts of your application. - Maxim Egorushkin
Nope, I checked all code. Just a single definition exists. - Mike Lischke
Try linking using gold with --detect-odr-violations command line option. - Maxim Egorushkin
Why would it show as const in the debugger whereas your exception handlers catch exceptions by reference to non-const? - Maxim Egorushkin
That's just because I tried with various combinations and don't want to constantly update the question text. Originally the catch block is for (const base::file_error &exc). - Mike Lischke

2 Answers

4
votes
class test_error : base::file_error 

allows me to catch it in a test_error block and in a catch-all block, but not in base::file_error, std::runtime_error or std::exception blocks.

Your exception classes need to derive publicly from base exception classes. Otherwise you won't be able to catch them via the base class:

class test_error : public base::file_error 
1
votes

The solution for this problem is to avoid using the compiler option -fvisibility=hidden (or make it default) in the test application. Read more about this here: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#Code-Gen-Options. In particular this part:

Note that -fvisibility does affect C++ vague linkage entities. This means that, for instance, an exception class that is be thrown between DSOs must be explicitly marked with default visibility so that the ‘type_info’ nodes are unified between the DSOs.

An overview of these techniques, their benefits and how to use them is at http://gcc.gnu.org/wiki/Visibility.