3

Suppose you have a C++ function that only makes calls to C functions, like

int ClearTheBin()
{
    int result = SHEmptyRecycleBinW(
        nullptr,
        nullptr,
        SHERB_NOCONFIRMATION |
        SHERB_NOPROGRESSUI |
        SHERB_NOSOUND);

    if (SUCCEEDED(result) ||
        result == E_UNEXPECTED) // Already empty
    {
        return 0;
    }

    return result;
}

There are obviously a zillion different things that could go wrong with the call to the C function, but since C lacks exceptions such errors will be stored in the resulting code. My question is: should such functions be declared noexcept? Even if the method can't throw, it may give the reader the false impression "Nothing can go wrong with this function and I can assume it's 100% reliable," because that's usually what noexcept functions imply.

What's your thoughts on the matter?

James Ko
  • 32,215
  • 30
  • 128
  • 239
  • 1
    "Nothing can go wrong with this function and I can assume it's 100% reliable" I would not assume that for a function that has a return code... Perhaps your wrapper should throw in case of bad return? Not sure if opinion-based or a matter of best practices. +1 though, because I often face this situation, and usually don't declare `noexcept` just because I'm not used to it ;( – villasv Nov 28 '15 at 03:40

2 Answers2

3

"Nothing can go wrong with this function and I can assume it's 100% reliable"

I wouldn't really assume that so quickly. Typically if I see a function marked as noexcept, I'd tend to look for alternative ways it could give me errors like a boolean state, some kind of global error retrieval function, a success/fail return value, something like that. Only lacking that, and perhaps in the face of a function that causes no side effects, might I sort of make this assumption.

In your case, you got the whole error code kind of thing going for the function, so clearly one glance at the interface documentation should tell me that things can go wrong, yet the noexcept suggests to me that these exceptional cases won't (or at least shouldn't) be reported to me (the caller) in the form of an exception.

Applying noexcept

It seems generally on the safe side to be reluctant to use noexcept. A single introduction of, say, std::string to that function and now it can throw, only the noexcept will turn that into a termination rather than something we can catch. Guaranteeing that a function will never throw at the interface/design level is a hard one to ensure in anything which actually mixes C++ code, especially in a team setting where any random colleague might introduce (maybe on a crunch day) some random C++ function call or object which can throw to this function.

Impossible to Throw

In some cases it's actually easy to make this guarantee. One example is a noexcept function which has its own try/catch block, where everything the function does is contained within. If an exception is caught, translate it into an error code of some sort and return that. In those cases, the try/catch guarantees that the function will swallow the exception and not leak it to the outside world. It becomes easy to apply noexcept here because the function can't throw.

Can't Possibly Throw Correctly

Another example of where noexcept doesn't require too much thought is in the case of a function that should be considered broken if it threw. An example is an API function which is called across module boundaries for an SDK designed to allow plugins to be built with any compiler. Since we shouldn't throw across module boundaries, and especially in this scenario, throwing would already be UB and shouldn't be done. In that case, noexcept can be easy to apply because the correct behavior of the function, by design, should never throw.

If in doubt, leave it out.

In your case, if in doubt, I'd suggest to leave it out. You can go a lot more wrong by using noexcept and inadvertently throwing out of the function and terminating the program. An exception is if you can make a hard guarantee that this ClearTheBin function is always going to be using only C, no operator new (or at least only nothrow versions), no dynamic_casts, no standard lib, etc. Omission seems like erring on the safe side, unless you can make this kind of hard interface-level guarantee for both present and future.

2

noexcept was never intended to mean that "nothing can go wrong". noexcept simply means that function does not throw C++ exceptions. It does not in any way imply that the function is somehow immune to other kinds of problems, like undefined behavior.

So, yes, if all your function does is calls C functions, then from the formal point of view declaring it noexcept is a perfectly reasonable thing to do.

If you personally want to develop your own convention, within which noexcept means something more, like a "100% reliable function" (assuming such thing even exists), then you are free to do so. But that becomes a matter of your own convention, no a matter of language specification.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765