Does the C++ standard mandate that C-linkage functions are `noexcept`?
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:
- 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.
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
.
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!
}
}