Reputation: 430
I have a function that performs basic string formatting to generate an error description string as input to std::runtime_error
:
std::string errormsg(int i)
{
std::string str;
// .. do something with str
return str;
}
... such that:
void some_process(int x)
{
if (x < 0)
throw std::runtime_error(errmsg(x));
}
My intuition is that this could be simplified by using a function that directly returns an exception type; using a more complete example:
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()));
}
... which simplifies to throw error("Error code %d", x)
.
But I found it difficult to find code examples where exception types are returned from a wrapper function. Hence, would the above be considered bad practice, or otherwise unsafe?
Upvotes: 0
Views: 364
Reputation: 122585
There is nothing special about exception types. They are types like others, just that they are meant to be thrown as exception. Consider that you can throw
objects of any type not just types inheriting from std::exception
.
The reason you did not find examples is perhaps that it is more typical to inherit from std::runtime_error
and then just call the constructor when you want to throw it. Something along the line of
struct my_runtime_error : std::runtime_error {
{
template<typename ... Ts>
my_runtime_error(const char * frmt, Ts ... args);
};
Or as suggested in a comment, write a function that returns void
and instead throw
s the exception itself:
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()));
}
to be used as
void some_process(int x)
{
if (x < 0)
throw_error(errmsg(x));
}
However, this has the disadvantage of always throwing from the same function instead of the place where actually the problem occurs and if I had to choose between this and yours I would go for your solution.
PS: note that std::exception
and its descendants are meant to be inherited from in contrast to most other types in the standard library. std::exception
does have a virtual destructor.
Upvotes: 4