64

I read a thoughtful series of blog posts about the new <system_error> header in C++11. It says that the header defines an error_code class that represents a specific error value returned by an operation (such as a system call). It says that the header defines a system_error class, which is an exception class (inherits from runtime_exception) and is used to wrap error_codess.

What I want to know is how to actually convert a system error from errno into a system_error so I can throw it. For example, the POSIX open function reports errors by returning -1 and setting errno, so if I want to throw an exception how should I complete the code below?

void x()
{
    fd = open("foo", O_RDWR);
    if (fd == -1)
    {
        throw /* need some code here to make a std::system_error from errno */;
    }
}

I randomly tried:

errno = ENOENT;
throw std::system_error();

but the resulting exception returns no information when what() is called.

I know I could do throw errno; but I want to do it the right way, using the new <system_error> header.

There is a constructor for system_error that takes a single error_code as its argument, so if I can just convert errno to error_code then the rest should be obvious.

This seems like a really basic thing, so I don't know why I can't find a good tutorial on it.

I am using gcc 4.4.5 on an ARM processor, if that matters.

David Grayson
  • 84,103
  • 24
  • 152
  • 189

2 Answers2

66

You are on the right track, just pass the error code and a std::generic_category object to the std::system_error constructor and it should work.

Example:

#include <assert.h>
#include <errno.h>
#include <iostream>
#include <system_error>

int main()
{
    try
    {
        throw std::system_error(EFAULT, std::generic_category());
    }
    catch (std::system_error& error)
    {
        std::cout << "Error: " << error.code() << " - " << error.what() << '\n';
        assert(error.code() == std::errc::bad_address);
    }
}

Output from the above program on my system is

Error: generic:14 - Bad address
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Cool, that works! For some reason, on my system that exact same code just outputs "Error: system:14 - " but I'm probably not using the latest version of libstdc++ or something. – David Grayson Aug 29 '12 at 06:07
  • 3
    For anyone else with the same problem, I found that `error.what()` returns an empty string but `error.code().message()` returns a nice string. – David Grayson Aug 29 '12 at 06:29
  • 4
    Can't you just throw `std::system_error(EFAULT, std::system_category());` – Cubbi Aug 29 '12 at 13:52
  • @Cubbi You're right, didn't think of that. Updated answer to reflect that. – Some programmer dude Aug 29 '12 at 14:38
  • This is great, but what if you want to check the error code against one of the generic members of the `errc` enum? https://stackoverflow.com/questions/46370615/how-do-i-use-the-generic-error-codes-enum-with-system-category-error-codes-from – Omnifarious Sep 22 '17 at 23:26
  • 1
    To wrap a POSIX `errno` codes, use the [std::generic_category](http://en.cppreference.com/w/cpp/error/generic_category). [std::system_category](http://en.cppreference.com/w/cpp/error/system_category) is meant to be used for errors reported by the operating system. – IInspectable Oct 30 '17 at 13:05
  • 4
    @IInspectable On a POSIX system I would argue that those error codes *are* from the operating system. Sure some errors could come from utility functions or the C library functions, but they could also come from *system calls* like `write` and `socket`. – Some programmer dude Oct 30 '17 at 13:07
  • To my knowledge, all systems that implement POSIX also provide extensions. Not using the correct error category is very likely to produce bugs down the road, even if most of the time it doesn't matter. – IInspectable Oct 30 '17 at 14:32
  • 1
    Yet another C++ blunder... What a messy interface for such a common task! – gatopeich Jan 27 '21 at 12:56
13

To add to the excellent accepted answer, you can enrich the error message with some contextual information in the 3rd argument, e.g. the failing file name:

std::string file_name = "bad_file_name.txt";
fd = open(file_name, O_RDWR);
if (fd < 0) {
    throw std::system_error(errno, std::generic_category(), file_name);
}

Then when caught, e.what() will return, for example:

bad_file_name.txt: file not found
rustyx
  • 80,671
  • 25
  • 200
  • 267