Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the C++ standard mandate that C-linkage functions are `noexcept`?

I can't find anything in the standard that forces functions declared with extern "C" to be noexcept, either implicitly or explicitly.

Yet, it should be clear that C calling conventions cannot support exceptions... or is it?

Does the standard mention this, somewhere that I've missed? If not, why not? Is it simply left as an implementation detail of sorts?

like image 520
Lightness Races in Orbit Avatar asked Jun 23 '14 09:06

Lightness Races in Orbit


People also ask

What is the difference between C and C++ linkage rules?

C++ lets you declare all sorts of things that aren't valid in C, such as classes, namespaces, and linkage specifications. Not surprisingly, these features make the distinction between definitions and other declarations, as well as the linkage rules, more complicated in C++ than in C.

Does N have external linkage in C?

As before, n has external linkage, but now the declaration is not a definition–not even a tentative one in C. You can place this declaration in a header, include that header in several source files, compile each source file, and link them together.

How is internal linkage implemented in C++?

Internal Linkage: An identifier implementing internal linkage is not accessible outside the translation unit it is declared in. Any identifier within the unit can access an identifier having internal linkage. It is implemented by the keyword static.

What types of names in C++ have internal linkage?

In contrast, other names in C++ can have external linkage, including names for classes, enumeration types and constants, namespaces, references, and templates. References and function templates can also have internal linkage.


3 Answers

As far as I can tell there is no guarantee that function defined with "C" linkage will not throw exceptions. The standard allows a C++ program both to call an external function with "C" language linkage, and to define functions written in C++ that have "C" language linkage. Therefore there is nothing to prevent a C++ program from calling a function with "C" language linkage that is actually written in C++ (in another compilation unit maybe, although even this is not necessary). It would be a strange thing to do, but it is hard to rule out. Also I don't see where in the standard it says that doing so would lead to undefined behaviour (in fact since the Standard cannot define the behaviour of function not written in C++, this would be the only usage where there is not formally undefined behaviour).

As a consequence I think it would be an error to assume that "C" linkage implies noexcept.

like image 96
Marc van Leeuwen Avatar answered Oct 18 '22 04:10

Marc van Leeuwen


Um, I assume extern "C" just use C-linkage, not C function. It prevents the compiler from doing C++ name mangling.

More directly - Suppose this code.

// foo.cpp
extern "C" void foo()
{
    throw 1;
}

// bar.cpp
extern "C" void foo();
void bar()
{
    try
    {
        foo();
    }
    catch (int)
    {
        // yeah!
    }
}
like image 24
ikh Avatar answered Oct 18 '22 04:10

ikh


Marc van Leeuwen's answer is correct: looking up the current working draft, nothing seems to mandate that that functions declared extern "C" be implicitly noexcept. It is interesting to know that standard C++ prohibits that the C standard library functions within the C++ standard library from throwing. Those function are themselves usually specified as extern "C" (but this is implementation defined see 16.4.3.3-2). Take a look at clause 16.4.6.13 [Restriction on exception handling] and the accompanying footnotes 174 and 175.

Functions from the C standard library shall not throw exceptions [footnote 174] except when such a function calls a program-supplied function that throws an exception.[footnote 175]

Footnote 174:

  1. That is, the C library functions can all be treated as if they are marked noexcept. This allows implementations to make performance optimizations based on the absence of exceptions at runtime.

Footnote 175:

The functions qsort() and bsearch() ([alg.c.library]) meet this condition.

That being said, following the same strategy as the standard library is usually a good design guideline, and for reasons mention in Marc van Leeuwen's answer I think it's a good idea that user defined extern "C" functions be also specified with noexcept, unless it is handed a pointer to C++ function as callback argument like qsort and the likes. I made a small experiment with clang10, gcc10 with the following code:

#include <cstring>
#include <cstdlib>
#include <iostream>

extern "C" int cmp(const void* lhs, const void* rhs) noexcept;
extern "C" int non_throwing();

int main()
{
    constexpr int src[] = {10, 9, 8, 7, 6, 5};
    constexpr auto sz = sizeof *src;
    constexpr auto count = sizeof src / sz;

    int dest[count];
    int key = 7;

    std::cout << std::boolalpha
    // noexcept is unevaluated so no worries about UB here
        << "non_throwing: " << noexcept(non_throwing()) << '\n'
        << "memcpy: " << noexcept(std::memcpy(dest, src, sizeof dest)) << '\n'
        << "malloc: "<< noexcept(std::malloc(16u)) << '\n'
        << "free: " << noexcept(std::free(dest)) << '\n'
        << "exit: " << noexcept(std::exit(0)) << '\n'
        << "atexit: " << noexcept(std::atexit(nullptr)) << '\n'
        << "qsort: " << noexcept(std::qsort(dest, count, sz, cmp)) << '\n' // should fail
        << "bsearch: " << noexcept(std::bsearch(&key, dest, count, sz, cmp)) << '\n'; // should fail
}

The output for both gcc10 and clang10 was:

non_throwing: false
memcpy: true
malloc: true
free: true
exit: true
atexit: true
qsort: false
bsearch: false

For msvc142, if compiled with /EHsc then all outputs are obviously true. And with /EHs, all outputs are false, which makes the 'c' in /EHsc necessary for strict conformance.

like image 3
Julien Villemure-Fréchette Avatar answered Oct 18 '22 03:10

Julien Villemure-Fréchette