user2535650
user2535650

Reputation: 375

Avoid throwing exception in C++ assignment-expression of a throw statement (while keeping some sort of readability and maintainability)

TL;DR version

How to achieve exception-free C++ assignment-expression in throwing statement without sacrificing code readability and maintainability?


Yadi Yada version

Goal and current implementation

Recently I'm trying to figure out a way to avoid throwing exception in the assignment-expression part of C++ throw statement. But I cannot find a way to accomplish my goal without sacrificing readability and ease of maintainability.

To be more precise, I'm trying to conform to MISRA-C++ 2008 Rule 15-1-1 (page 157).

Before the issue came to my notice from the static program analysis tool used in the company, my throwing statement would often looks something like this

int someValue;
if (/*something is wrong*/) {
    throw MyExceptionClass(string("Blah some message [").
                                  append(to_string(somevalue)).
                                  append("]"));
}

Basically I would dump the relevant context out into a standard string, pass the said string into constructor of the soon-to-be-thrown exception then throws it.

Problem with the original implementation

The problem is either my custom to-string function (e.g., wrapper for inet_ntop) or the std::to_string function may throw exceptions (in this case, std::bad_alloc). What happens next is the exception thrown in the assignment-expression would be propagated, instead of the original-to-be-thrown exception. Since the original exception was never constructed successfully, effectively shadowing the original exception.

Some alternatives I can think of and their problem

In order to abide by the rule, I think I would have to completely steer away from dynamic allocation. That leaves me with

  1. Purely static error output string
if (/*something is wrong*/) {
    throw MyExceptionClass("Something is wrong");
}
  1. Locally statically allocated buffer, keep track of proper buffer index myself, if the error message exceed the pre-defined buffer size, the message would have to be truncated.
int someValue;
if (/*something is wrong*/) {
    char buf[ERROR_MESSAGE_SIZE] = {0};
    size_t bufIndex = 0;
    int ret = snprintf(buf, sizeof(buf), "Something is wrong, somevalue: [%d]", someValue);
    if (ret < 0) {
        /* Deal with the sprintf error here, at this point probably would just abort */
        /* Note that throwing stuff here is also effectively equivalent of shadowing the original exception */
    } else if (ret >= sizeof(buf)) {
        /* The message is written, but truncated, not sure how to handle that (if meaningful context is dropped) */
    }
    throw MyExceptionClass(buf);
}
Problems with solution 1

The problem with solution 1 is there is not sufficient context given in the message. If someone is calling my function / library but insisting in sending illegal argument, and at the same time ignore returned error code / no proper handling of any exception (more often than not these things will happen in the same piece of garbage code). It would take some extra time in figuring out what the current state inside my own code.

As a side note here, I wrap all my throwing statement with a layer of extra macro I wrote (omitted in the given example) using the compiler provided __FILE__, __LINE__ and __func__ macro. So in this case I'm not actually worried about figuring out where the actual exception is thrown.

Problems with solution 2

Basically means in all my exception-handling part of the code I would have to use pure C language. The code would bloat really quickly, and not inline with the coding style of the rest of the code, making it hard to track and maintain. Plus after a while someone would probably try to come up with a common helper function and fuck up again.

Some feature I thought may comes in handy but doesn't seem to be the case

  1. std::string_view
  2. std::to_char

Originally I thought these two relatively new feature in C++ could help me a bit (haven't actually use them before to be honest). But after some thought and looking at the document, these two still requires a pre-allocated buffer to work on (an std::string on the frist one, and a char buffer on the second. Plus I still have to check for error code return in the to_char case.

Looking for alternatives

I wish know if there are other options that can abide by the rule without significantly sacrifice the readability and maintainability. Or maybe I should stand proud and strong behind my current implementation arguing it's currently at a acceptable balance between maintainability and robustness, then happily click the ignore button on the static code analysis tool?

A random question that popped up in my head while I was writting this question

Also, come to think of it, how does std::throw_with_nested deal with the problem? I would imagine having to construct a new exception would take up some more memory space, does that mean std::throw_with_nested may throw some other stuff in the process? (The cpp reference API does not have noexcept on the function itself)

Thanks for any suggestion / feedback in advance, cheers.

Upvotes: 3

Views: 339

Answers (1)

ecatmur
ecatmur

Reputation: 157464

int someValue;
if (/*something is wrong*/) {
    auto ex = MyExceptionClass("Blah some message");
    try {
        ex = MyExceptionClass(string("Blah some message [").
                                      append(to_string(somevalue)).
                                      append("]"));
    }
    catch (...)
    {
    }
    throw ex;
}

Generally, you want your MyExceptionClass to contain a char const* to a static description string (literal), and an auxiliary data element (of type e.g. std::string) that supplements or replaces the static description string. You swallow exceptions populating the auxiliary data, so that if this process fails you still have the static description string to fall back on.

Upvotes: 1

Related Questions