jww jww - 1 year ago 61
C++ Question

Does a C++ cast strip the 'extern "C"' from a declaration?

This question is related to Warning (Anachronism): Assigning

extern "C" void(*)(int)
. In the cited question, we had a function pointer typedef declared as
extern "C"

extern "C" {
typedef void (*SignalHandlerFn) (int);

When we attempted to assign it:

new_handler.sa_handler = (pfn ? reinterpret_cast<SignalHandlerFn>(pfn) :

It resulted in the error (the line numbers are a bit off, but the line above produces it):

/opt/solarisstudio12.4/bin/CC -DDEBUG -c test.cpp
"ossig.h", line 75: Warning (Anachronism): Using void(*)(int) to initialize extern "C" void(*)(int).
"test.cpp", line 135: Where: While instantiating "SignalHandler<5, 0>::SignalHandler(extern "C" void(*)(int), int)".
"test.cpp", line 135: Where: Instantiated from non-template code.
2 Warning(s) detected.

The best I can tell, the
extern "C"
was discarded when using the
. However, a C cast worked as expected.

I believe Sun Studio 12.4 (SunCC 5.13) uses C++03 by defult. But my question applies to both C++03 and C++11 since we see a lot of both at the moment due to the popularity of GCC 4.8 and 4.9.

Does a C++ cast strip the
extern "C"
from a declaration?

solaris:~$ cat test.cxx
#include <signal.h>

extern "C" {
typedef void (*SignalHandlerFn) (int);

template <int S, bool O=false>
struct SignalHandler
SignalHandler(SignalHandlerFn pfn = NULL, int flags = 0) : m_installed(false)
struct sigaction new_handler;

int ret = 0;

ret = sigaction (S, 0, &m_old);
if (ret != 0) break; // Failed

if (m_old.sa_handler != 0 && !O) break;

new_handler.sa_handler = (pfn ? reinterpret_cast<SignalHandlerFn>(pfn) :
new_handler.sa_flags = (pfn ? flags : 0);

ret = sigemptyset (&new_handler.sa_mask);
if (ret != 0) break; // Failed

ret = sigaction (S, &new_handler, 0);
if (ret != 0) break; // Failed

m_installed = true;

} while(0);

if (m_installed)
sigaction (S, &m_old, 0);

struct sigaction m_old;
bool m_installed;

static void NullHandler(int /*unused*/) { /* continue*/ }

// Not copyable
SignalHandler(const SignalHandler &);
void operator=(const SignalHandler &);

int main(int argc, char* argv[])
SignalHandler<SIGTRAP, 0> handler;
return 0;

Answer Source

A reinterpret_cast<T> either produces an expression of type T, or is ill-formed due to no allowable conversion existing. (ref: [expr.reinterpret.cast]/1).

The language linkage is part of the type (ref: [dcl.link]/1).

So the result of reinterpret_cast<SignalHandlerFn> is either ill-formed, or a pointer to function with C language linkage.

Accordingly, it doesn't seem correct to describe this cast as "stripping extern C" -- although of course a compiler may react to ill-formed code by issuing a diagnostic, and then proceeding as if the code had some arbitrary behaviour.

In your code sample, both uses of reinterpret_cast<SignalHandlerFn> are well-formed , because reinterpret_cast may convert any function pointer to any other function pointer (ref: [expr.reinterpret.cast]/6).

However, calling SignalHandler::NullHandler through sa_handler will cause undefined behaviour (ref: ibid.). The warning produced by your compiler could be intended to warn about this case.