Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
322 views
in Technique[技术] by (71.8m points)

c++ - How to construct a <stdexcept> or <system_error> exception without throwing?

Exceptions defined in <stdexcept> (e.g. std::logic_error, std::runtime_error and their subclasses such as std::system_error) have constructors expecting string arguments, e.g.:

domain_error(const string& what_arg);
domain_error(const char* what_arg);

with postconditions

strcmp(what(), what_arg.c_str()) == 0
strcmp(what(), what_arg) == 0

respectively. There is no requirement that these arguments passed to the constructors remain valid during the lifetime of these exceptions, so the only way to ensure that the postconditions hold, is to duplicate and store these dynamic strings. This requires memory, so I assume that their construction itself may throw std::bad_alloc or similar, which is usually most unexpected. This causes problems, because every code example I've seen in the wild encourages people to write code like

if (haveError)
    throw std::runtime_error("BOO!"); // May throw std::bad_alloc instead?!

whereas it would seem to be much safer to construct the exception beforehand in some other place, e.g:

struct A {
    // During allocation of A one would often expect std::bad_alloc anyway:
    A() : m_someException("BOO!") {}
    void f() {
        /* Do stuff */
        if (haveError)
            throw m_someException;
            /* Note that according to §18.8.1.2 all standard library
               classes deriving from `std::exception` must have publicly
               accessible copy constructors and copy assignment operators
               that do not exit with an exception. In implementations such
               exception instances most likely share the common string
               with all their copies. */
    }
    std::runtime_error const m_someException;
};

This makes me very cautious of libraries which throw any such exceptions, e.g even regex_error from <regex> in C++11!!!

Why don't these exceptions have no-throw/noexcept constructors? Does the C++ core guidelines have a say on this?

PS: Personally I would have left what() a pure abstract method at this point in the exception ancestry chain.

EDIT 09.10.2017: Here's a PoC demonstrating that std::runtime_error construction can throw a std::bad_alloc instead:

#include <cstddef>
#include <cstdlib>
#include <new>
#include <stdexcept>
#include <string>

bool throwOnAllocate = false;

void * operator new(std::size_t size) {
    if (!throwOnAllocate)
        if (void * const r = std::malloc(size))
            return r;
    throw std::bad_alloc();
}

void operator delete(void * ptr) { std::free(ptr); }

int main() {
    std::string const errorMessage("OH NOEZ! =(");
    throwOnAllocate = true;
    throw std::runtime_error(errorMessage);
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Short answer, it is not possible to construct any objects with an absolute guarantee of no exceptions.

You may consider allocating on the stack, but your thread stack could run out and will cause system error/exception. Your CPU may get an external interrupt, right when you throw, that system can't handle and everything gets fried. As others have suggested, don't fret the small stuff. Running out of memory is something most user programs can't recover from, so don't worry about it, elegantly fail. Don't try to handle every bad situation, just ones you can easily recover from.

As a side-note, for the situation about running out of memory, a lot of high graphics games do all of their heap allocation up-front during game initialization and try to avoid after the game starts to reduce running into issues with out of memory/slow allocations in the middle of a game (jittery game & bad user experience). You can similarly be smart about design of your program to reduce the chances of running into bad situations.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...