I have an Exception class on which I want to set more information before I throw it. Can I create the Exception object, call some of its functions and then throw it without any copies of it being made?
The only method I've found is throwing a pointer to the object:
class Exception : public std::runtime_error
{
public:
Exception(const std::string& msg) : std::runtime_error(msg) {}
void set_line(int line) {line_ = line;}
int get_line() const {return line_;}
private:
int line_ = 0;
};
std::unique_ptr<Exception> e(new Exception("message"));
e->set_line(__LINE__);
throw e;
...
catch (std::unique_ptr<Exception>& e) {...}
But throwing exceptions by pointer is generally avoided, so is there any other way?
There is also the option of setting all the options through the constructor, but this can quickly become unscalable if more fields are added to the class and you want to have fine-grained control over what fields to set:
throw Exception("message"); // or:
throw Exception("message", __LINE__); // or:
throw Exception("message", __FILE__); // or:
throw Exception("message", __LINE__, __FILE__); // etc.
What about using std::move?
Exception e("message");
e.set_line(__LINE__);
throw std::move(e);
Alternatively, you can create a Java-esque builder like this:
class ExceptionBuilder;
class Exception : public std::runtime_error
{
public:
static ExceptionBuilder create(const std::string &msg);
int get_line() const {return line_;}
const std::string& get_file() const { return file_; }
private:
// Constructor is private so that the builder must be used.
Exception(const std::string& msg) : std::runtime_error(msg) {}
int line_ = 0;
std::string file_;
// Give builder class access to the exception internals.
friend class ExceptionBuilder;
};
// Exception builder.
class ExceptionBuilder
{
public:
ExceptionBuilder& with_line(const int line) { e_.line_ = line; return *this; }
ExceptionBuilder& with_file(const std::string &file) { e_.file_ = file; return *this; }
Exception finalize() { return std::move(e_); }
private:
// Make constructors private so that ExceptionBuilder cannot be instantiated by the user.
ExceptionBuilder(const std::string& msg) : e_(msg) { }
ExceptionBuilder(const ExceptionBuilder &) = default;
ExceptionBuilder(ExceptionBuilder &&) = default;
// Exception class can create ExceptionBuilders.
friend class Exception;
Exception e_;
};
inline ExceptionBuilder Exception::create(const std::string &msg)
{
return ExceptionBuilder(msg);
}
Used like this:
throw Exception::create("TEST")
.with_line(__LINE__)
.with_file(__FILE__)
.finalize();