Возврат исключения из функции (не добавление в функцию) для форматирования сообщения? - PullRequest
0 голосов
/ 30 января 2020

У меня есть функция, которая выполняет базовое c форматирование строки, чтобы сгенерировать строку описания ошибки в качестве входных данных для std::runtime_error:

std::string errormsg(int i)
{
  std::string str;
  // .. do something with str
  return str;
}

..., например:

void some_process(int x)
{
  if (x < 0)
    throw std::runtime_error(errmsg(x));
}

Моя интуиция заключается в том, что это можно упростить с помощью функции, которая напрямую возвращает тип исключения; используя более полный пример:

template<typename ... Ts>
std::runtime_error error(const char * frmt, Ts ... args)
{
    const int n = std::snprintf(nullptr, 0, frmt, args...);
    std::vector<char> buff(n + 1);
    std::snprintf(&buff[0], n + 1, frmt, args...);
    return std::runtime_error(std::string(buff.data()));
}

... что упрощает до throw error("Error code %d", x).

Но мне было трудно найти примеры кода, где типы исключений возвращаются из функции-оболочки. Следовательно, будет ли это считаться плохой практикой или иным образом небезопасным?

1 Ответ

4 голосов
/ 30 января 2020

В типах исключений нет ничего особенного. Они такие же типы, как и другие, просто они предназначены для исключения. Учтите, что вы можете throw объекты любого типа, а не только типы, унаследованные от std::exception.

Причина, по которой вы не нашли примеров, возможно, в том, что более типично наследовать от std::runtime_error, а затем просто вызывать конструктор, когда вы хотите его выбросить. Что-то вроде строки

struct my_runtime_error : std::runtime_error {
{
    template<typename ... Ts>
    my_runtime_error(const char * frmt, Ts ... args);
};

Или, как предлагается в комментарии, напишите функцию, которая возвращает void и вместо throw s само исключение:

template<typename ... Ts>
[[ noreturn ]] void throw_error(const char * frmt, Ts ... args)
{
    const int n = std::snprintf(nullptr, 0, frmt, args...);
    std::vector<char> buff(n + 1);
    std::snprintf(&buff[0], n + 1, frmt, args...);
    throw std::runtime_error(std::string(buff.data()));
}

, чтобы быть используется как

void some_process(int x)
{
   if (x < 0)
      throw_error(errmsg(x));
}

Тем не менее, у этого недостатка является то, что он всегда бросает одну и ту же функцию вместо того места, где на самом деле возникает проблема, и если бы мне пришлось выбирать между этим и вашим, я бы go для вашего решение.

PS: обратите внимание, что std::exception и его потомки предназначены для наследования в отличие от большинства других типов в стандартной библиотеке. std::exception имеет виртуальный деструктор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...