Как переносимо сравнивать исключения std::system_error со значениями std::errc?

Насколько я понял, один из лучших способов проверки условий system_error переносимым способом — это сравнить их значение code() со значениями в перечислении std::errc. Однако, когда я пытаюсь запустить следующий код, это не работает.

#include <cassert>
#include <cerrno>
#include <system_error>

int main() {
    try {
        throw std::system_error(ENOENT, std::system_category());
    } catch (std::system_error const & e) {
        assert(e.code() == std::errc::no_such_file_or_directory); // <- FAILS!?
    }
}

Я неправильно понимаю, как должны работать эти диагностические ошибки, или я делаю что-то не так? Как сравнивать std::system_error исключений с std::errc значениями?

РЕДАКТИРОВАТЬ: код, кажется, работает правильно с использованием clang++ и libc++, но не работает при сборке с libstdc++ независимо от того, какой компилятор GCC или Clang (и версию) я использую. Связаны с PR 60555? Любое портативное обходное решение?


person jotik    schedule 07.06.2017    source источник
comment
Вот правильный пиар. Тут особо нечего сказать...   -  person T.C.    schedule 07.06.2017


Ответы (2)


Вы не делаете ничего плохого. Как подтверждается в комментариях T.C. и в недавних похожих вопросы, это действительно вызвано PR #60555. К счастью, к 8 августа 2018 года эта ошибка уже была исправлена ​​в их VCS:

Исправлено во всех активных ветках, поэтому будет исправлено в выпусках 6.5, 7.4, 8.3 и 9.1.

Кажется, что для этого нет хорошего обходного пути, поэтому теперь это просто вопрос разработчиков GCC, выпускающих новые версии GCC, и вопрос лет, пока они не будут включены в популярные дистрибутивы, пока мы, наконец, не сможем начать использовать эту замечательную современную функцию C + +11. Но это жизнь...

person jotik    schedule 22.01.2019

e.code() возвращает экземпляр ::std::error_code, который перегрузил operator ==, что приводит к неявному построению объекта error_condition как правого выражения из std::errc::no_such_file_or_directory. Сравнение завершится ошибкой, потому что код_ошибки, возвращаемый исключением, имеет system_category, а другой код будет иметь generic_category. Вместо этого вы должны сравнить с error_code::value(). Обратите внимание, что для этого сравнения требуется значение static_cast из std::errc, потому что это enum class и не может быть неявно преобразовано в int. Рабочий пример:

#include <cassert>
#include <cerrno>
#include <system_error>
#include <iostream>
 
int main()
{
    ::std::cout << static_cast< int >(ENOENT) << ::std::endl;
    ::std::cout << static_cast< int >(::std::errc::no_such_file_or_directory) << ::std::endl;
    try
    {
        throw std::system_error(ENOENT, std::system_category());
    }
    catch(::std::system_error const & e)
    {
        assert(e.code().value() == static_cast< int >(::std::errc::no_such_file_or_directory));
    }
}
person user7860670    schedule 07.06.2017
comment
Сравнение не удастся, потому что error_code, возвращаемый исключением, имеет system_category, а другой будет иметь generic_category. Это чепуха. Вы удосужились прочитать спецификацию operator== и функций, которые она вызывает? Вряд ли это простое сравнение на равенство. - person T.C.; 07.06.2017