168

This is an example of what I often do when I want to add some information to an exception:

std::stringstream errMsg;
errMsg << "Could not load config file '" << configfile << "'";
throw std::exception(errMsg.str().c_str());

Is there a nicer way to do it?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Ben
  • 4,486
  • 6
  • 33
  • 48
  • 13
    I am wondering how did you even managed to work this way — the `std∷exception` have no a constructor with `char*` arg. – Hi-Angel Apr 29 '15 at 12:11
  • 2
    I'm wondering the same thing. Maybe it is a non-standard MS extension to c++? Or maybe something new in C++14? The current [documentation](http://www.cplusplus.com/reference/exception/exception/) says std::exception constructor does not take any arguments. – Chris Warth May 19 '15 at 22:26
  • 2
    Yes, but `std::string` has an implicit constructor which takes a `const char*`... – Brice M. Dempsey Jul 16 '15 at 09:35
  • 7
    @Chris Warth It appears to be part of MS' behind-the-scenes implementation of `std::exception`'s child classes, and is used by their versions of `std::runtime_error` and `std::logic_error`. Apart from the ones defined by the standard, MSVS' version of `` also includes two more constructors, one taking `(const char * const &)` and the other taking `(const char * const &, int)`. They're used to set a private variable, `const char * _Mywhat`; if `_Mywhat != nullptr`, then `what()` defaults to returning it. Code that relies on it probably isn't portable. – Justin Time - Reinstate Monica Jan 25 '16 at 22:07

12 Answers12

231

The standard exceptions can be constructed from a std::string:

#include <stdexcept>

char const * configfile = "hardcode.cfg";
std::string const anotherfile = get_file();

throw std::runtime_error(std::string("Failed: ") + configfile);
throw std::runtime_error("Error: " + anotherfile);

Note that the base class std::exception can not be constructed thus; you have to use one of the concrete, derived classes.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
58

Here is my solution:

#include <stdexcept>
#include <sstream>

class Formatter
{
public:
    Formatter() {}
    ~Formatter() {}

    template <typename Type>
    Formatter & operator << (const Type & value)
    {
        stream_ << value;
        return *this;
    }

    std::string str() const         { return stream_.str(); }
    operator std::string () const   { return stream_.str(); }

    enum ConvertToString 
    {
        to_str
    };
    std::string operator >> (ConvertToString) { return stream_.str(); }

private:
    std::stringstream stream_;

    Formatter(const Formatter &);
    Formatter & operator = (Formatter &);
};

Example:

throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData);   // implicitly cast to std::string
throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData >> Formatter::to_str);    // explicitly cast to std::string
Crouching Kitten
  • 1,135
  • 12
  • 23
Torsten
  • 21,726
  • 5
  • 24
  • 31
  • 1
    omg I've been looking how to do something like this. But probably will change operator >> to explicit function to prevent over-(operator overloading) – Roman Plášil Mar 22 '16 at 09:07
  • 3
    what's the difference between this and an std::stringstream? It appears to contain a stringstream, but has (as far as I can tell), no extra functionality. – matts1 Sep 04 '16 at 01:43
  • 3
    Generally, it is not 100% safe way. std::stringstream methods can throw an exception.The problem is quite good described here: http://www.boost.org/community/error_handling.html – Arthur P. Golubev Aug 03 '17 at 01:06
  • 1
    @ArthurP.Golubev But in this case, a Formatter() instance also instantiates a stringstream behind the scenes, which again, could throw an exception. So what's the difference? – Zuzu Corneliu Jul 17 '18 at 08:19
  • The only added functionality is the ConvertToString trick and the explicit cast to string, which is nice anyway. ;) – Zuzu Corneliu Jul 17 '18 at 08:30
  • 1
    @corneliu-zuzu That's exactly what I wrote about. The crux of the matter is that the answer is not mine. I just left a comment that the solution is not safe too. – Arthur P. Golubev Jul 18 '18 at 12:44
  • @ArthurP.Golubev Ah, ok, I understand. I thought you were saying that this solution works around this problem ;) – Zuzu Corneliu Jul 19 '18 at 05:49
  • What is the benefit of this approach vs simply using @KerrekSB's answer? – gebbissimo Apr 16 '20 at 09:04
31

There are different exceptions such as runtime_error, range_error, overflow_error, logic_error, etc.. You need to pass the string into its constructor, and you can concatenate whatever you want to your message. That's just a string operation.

std::string errorMessage = std::string("Error: on file ")+fileName;
throw std::runtime_error(errorMessage);

You can also use boost::format like this:

throw std::runtime_error(boost::format("Error processing file %1") % fileName);
Greg Kramida
  • 4,064
  • 5
  • 30
  • 46
Neel Basu
  • 12,638
  • 12
  • 82
  • 146
  • 1
    The boost::format version above will not compile without an explicit conversion, ie: runtime_error((boost::format("Text %1" % 2).str())). C++20 introduces a std::format that will provide similar functionality. – Digicrat Jun 19 '20 at 18:43
17

The following class might come quite handy:

struct Error : std::exception
{
    char text[1000];

    Error(char const* fmt, ...) __attribute__((format(printf,2,3))) {
        va_list ap;
        va_start(ap, fmt);
        vsnprintf(text, sizeof text, fmt, ap);
        va_end(ap);
    }

    char const* what() const throw() { return text; }
};

Usage example:

throw Error("Could not load config file '%s'", configfile.c_str());
clearlight
  • 12,255
  • 11
  • 57
  • 75
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • 4
    Bad practice IMO, why use something like this when there's already a standard library which is built for optimisation? – Jean-Marie Comets Sep 04 '12 at 10:56
  • 3
    `throw std::runtime_error(sprintf("Could not load config file '%s'", configfile.c_str()))` – Jean-Marie Comets Sep 04 '12 at 10:58
  • 4
    `throw std::runtime_error("Could not load config file " + configfile);` (converting one or other argument to `std::string` if necessary). – Mike Seymour Sep 04 '12 at 11:04
  • 9
    @MikeSeymour Yes, but that gets uglier if you need to put strings in the middle and format numbers with certain precision, etc.. It's hard to beat a good old format string in terms of clarity. – Maxim Egorushkin Sep 04 '12 at 11:07
  • @MaximYegorushkin: That rather depends on your aesthetics. I find a fixed size buffer (which might well be too small in some situations and too large in others), and the lack of portable type-safety to be rather "ugly", even if it does allow you to compress a few calls to formatting functions into hieroglyphs embedded in a string. Each to his own, though. – Mike Seymour Sep 04 '12 at 11:20
  • 2
    @MikeSeymour I may agree that the code I posted may be ahead of its time. Portably typesafe `printf` and friends are imminent in C++11. Fixed size buffer is both a blessing and a curse: it doesn't fail in low resource situations but may truncate the message. I consider truncating an error message a better option then failing. Also, convenience of format strings has been proven by many different languages. But you are right, it is largely a matter of taste. – Maxim Egorushkin Sep 04 '12 at 12:01
  • @MaximEgorushkin In 2020, is there a more modern way of using this limited stack buffer approach with the STL? – justinpc Nov 23 '20 at 10:23
  • @justinpc You can allocate memory from the heap in 2020 just like it was possible in 2012. Have a look at `std::string`. – Maxim Egorushkin Nov 23 '20 at 10:38
  • @MaximEgorushkin I was looking for an STL solution that still uses a stack-allocated buffer to avoid heap allocation exceptions, which also avoids things like vsnprintf. – justinpc Nov 23 '20 at 11:10
  • @justinpc The exception itself is heap-allocated. A 2020 solution would be using `fmt` library and `std::string` for the buffer, which gets moved when the exception is copied on `throw`. Creating `std::string` could throw `std::bad_alloc` though. – Maxim Egorushkin Nov 23 '20 at 11:18
14

Use string literal operator if C++14 (operator ""s)

using namespace std::string_literals;

throw std::exception("Could not load config file '"s + configfile + "'"s);

or define your own if in C++11. For instance

std::string operator ""_s(const char * str, std::size_t len) {
    return std::string(str, str + len);
}

Your throw statement will then look like this

throw std::exception("Could not load config file '"_s + configfile + "'"_s);

which looks nice and clean.

Shreevardhan
  • 12,233
  • 3
  • 36
  • 50
  • 2
    I got this error **c++\7.3.0\bits\exception.h|63|note: no matching function for call to 'std::exception::exception(std::__cxx11::basic_string)** – Haseeb Mir Jun 04 '18 at 10:54
  • 1
    The behavior described by @Shreevardhan is not defined in std library, although MSVC++ will compile it. – jochen Oct 25 '18 at 06:25
  • Thanks for reminding of string literals, that are the actual cleaning solution. If you are still active, you could change `std::exception` to `std::runtime_error` to make it portable – really Jan 14 '23 at 13:25
6

Maybe this?

throw std::runtime_error(
    (std::ostringstream()
        << "Could not load config file '"
        << configfile
        << "'"
    ).str()
);

It creates a temporary ostringstream, calls the << operators as necessary and then you wrap that in round brackets and call the .str() function on the evaluated result (which is an ostringstream) to pass a temporary std::string to the constructor of runtime_error.

Note: the ostringstream and the string are r-value temporaries and so go out of scope after this line ends. Your exception object's constructor MUST take the input string using either copy or (better) move semantics.

Additional: I don't necessarily consider this approach "best practice", but it does work and can be used at a pinch. One of the biggest issues is that this method requires heap allocations and so the operator << can throw. You probably don't want that happening; however, if your get into that state your probably have way more issues to worry about!

evilrix
  • 152
  • 1
  • 3
  • I don't think it is that bad either, given the alternatives. If one is going to fill this with `boost::to_string` or `boost::lexical_cast` or `std::to_string`, these functions would be doing the same thing internally (and several times). – alfC Dec 02 '20 at 21:09
  • This also allows you to append numerical values, etc, into the message too. Upvoted! – Marcio Lucca Feb 12 '22 at 04:39
  • Simplest and most obvious solution IMO. – Allan Bazinet Feb 18 '22 at 17:10
  • 2
    This does not seem to work... `error: ‘class std::basic_ostream’ has no member named ‘str’`, also see [this](https://stackoverflow.com/questions/59473325/no-member-named-str-in-stdbasic-ostreamchar-with-gcc-and-clang-but-no-p). – Martin 'Kvík' Baláž Mar 03 '22 at 11:24
2

There are two points to answer in regards to what you want:

1.

The first point is that the nicer way is creating special types (classes) for custom exceptions and passing parameters as fields of the classes.

Something like the following:

class BaseFor_Exceptions : public std::exception {
protected:
    BaseFor_Exceptions();
};

class Exception1 : public BaseFor_Exceptions {
public:
    Exception1(uint32_t value1);
private:
    uint32_t value1;
};
throw Exception1(0);

The second point is that you are performing memory allocations when preparing the exception object because of trying to pass a value of variable size (filename).

There is a possibility (when changing objects of the both std::string and std::stringstream ) of std::bad_alloc exception to be thrown in the process of it, so that you fail to prepare or throw (*) your exception – you will lose the info and the state.

In a well-designed program it is easy to avoid memory allocation when preparing or handling an exception. All that you need is just:

  • either guarantee the value is still life when handling the exception and pass some kind of link to the value as a part of the exception – either reference or some kind (most likely smart) of pointer,
  • or get the value when handling the exception using the exception type info or/and fixed-size values; for example,
} catch (const ConfigurationLoadError & ex) {

    std::cerr
        << “Some message 1 ”
        << serviceLocator1.SomeGetMethod1().Get_ConfigurationFileName();

} catch (const SomeException & ex) {

    std::cerr
        << “Some message 2 ”
        << serviceLocator1.SomeGetMethod2().GetEventDetailsString(ex.Get_Value1());

}

Of course, you always have an option to accept buffer size limitations and use a pre-allocated buffer.

Also, please note that the type (classes) used for exceptions are not permitted to throw exceptions out of their copy constructors since, if the initial exception is attempted to be caught by value, a call of copy constructor is possible (in case is not elided by the compiler) and this additional exception will interrupt the initial exception handling before the initial exception is caught, which causes calling std::terminate. Since C++11 compilers are permitted to eliminate the copying in some cases when catching, but both the elision is not always sensible and, if sensible, it is only permission but not obligation (see https://en.cppreference.com/w/cpp/language/copy_elision for details; before C++11 the standards of the language didn’t regulate the matter).

'*' Also, you should avoid exceptions (will call them the additional) to be thrown out of constructors and move constructors of your types (classes) used for exceptions (will call them initial) since the constructors and move constructors could be called when throwing objects of the types as initial exceptions, then throwing out an additional exception would prevent creation of an initial exception object, and the initial would just be lost. As well as an additional exception from a copy constructor, when throwing an initial one, would cause the same.

2

As of ISO C++ 20, std::format works nicely:

throw std::runtime_error(std::format("{0}: Open error {1}: {2}", input_filename, errno, strerror(errno)));
UweBaemayr
  • 1,861
  • 1
  • 18
  • 21
0

Ran into a similar issue, in that creating custom error messages for my custom exceptions make ugly code. This was my solution:

class MyRunTimeException: public std::runtime_error
{
public:
      MyRunTimeException(const std::string &filename):std::runtime_error(GetMessage(filename)) {}
private:
      static std::string GetMessage(const std::string &filename)
     {
           // Do your message formatting here. 
           // The benefit of returning std::string, is that the compiler will make sure the buffer is good for the length of the constructor call
           // You can use a local std::ostringstream here, and return os.str()
           // Without worrying that the memory is out of scope. It'll get copied
           // You also can create multiple GetMessage functions that take all sorts of objects and add multiple constructors for your exception
     }
}

This separates the logic for creating the messages. I had originally thought about overriding what(), but then you have to capture your message somewhere. std::runtime_error already has an internal buffer.

bpeikes
  • 3,495
  • 9
  • 42
  • 80
0

Whenever I need a custom message to be thrown in an exception, I construct a C-style string with snprintf() and pass it to the exception constructor.

if (problem_occurred) {
    char buffer[200];
    snprintf(buffer, 200, "Could not load config file %s", configfile);
    string error_mesg(buffer);
    throw std::runtime_error(error_mesg);
}

I'm not sure if the extra string string error_mesg(buffer) is necessary. I reason that the buffer is on stack memory, and if the exception catcher keeps running, then allowing the catcher to keep a reference to a stack-allocated C string is problematic. Instead, passing a string to the exception will invoke copy-by-value, and the buffer array will be deep-copied.

stackoverflowuser2010
  • 38,621
  • 48
  • 169
  • 217
0

Here's another way:

Create some helper functions to convert formatted strings (i.e. printf) into an std::string (It's recommended to put these into a namespace):

#include <string>
#include <cstring>
#include <memory>
#include <cstdarg>

std::string vformat(const char* fmt_str, va_list args)
{
    int final_n, n = 256;
    std::unique_ptr<char[]> formatted;

    while(1)
    {
        formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */
        strcpy(&formatted[0], fmt_str);

        final_n = vsnprintf(formatted.get(), n, fmt_str, args);

        if (final_n < 0 || final_n >= n)
        {
            n += abs(final_n - n + 1);
        }
        else
        {
            break;
        }
    }

    return std::string(formatted.get());
}

std::string format(const char* fmt_str, ...)
{
    va_list args;
    va_start(args, fmt_str);
    auto retval = vformat(fmt_str, args);
    va_end(args);
    return retval;
}

Then can just raise any standard exception as so:

raise std::runtime_error(format("err code: %d, err msg: %s", retcode, retmsg));

Can also create a macro:

#define RAISE_EXCEPTION(msg, ...) raise std::runtime_error(vformat(msg, ## __VA_ARGS__))
driedler
  • 3,750
  • 33
  • 26
0

Code for custom Assert for throwing invalid_argument error and runtime_error error:-

#include<stdexcept>
#define custArgAssert(x,y) if(!x) throw std::invalid_argument(y);
#define custAssert(x,y) if(!(x)) throw std::runtime_error(y);

example:-

custAssert(4==3, "4 is not equal to 3") // no ';' needed

You can define more types of error in similar manner