Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

This question is related to Warning (Anachronism): Assigning void(*)(int) to 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) :
                                reinterpret_cast<SignalHandlerFn>(defaultHandler));

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 reinterpret_cast. 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;

    do
    {
      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) :
                                      reinterpret_cast<SignalHandlerFn>(&SignalHandler::NullHandler));
      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);
  }

  ~SignalHandler()
  {
    if (m_installed)
      sigaction (S, &m_old, 0);
  }

private:
  struct sigaction m_old;
  bool m_installed;

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

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

int main(int argc, char* argv[])
{
  SignalHandler<SIGTRAP, 0> handler;
  return 0;
}
like image 227
jww Avatar asked Sep 20 '16 00:09

jww


People also ask

What does extern C do when attached to the front of a function declaration?

In a const variable declaration, it specifies that the variable has external linkage. The extern must be applied to all declarations in all files. (Global const variables have internal linkage by default.) extern "C" specifies that the function is defined elsewhere and uses the C-language calling convention.

Is extern supported in C?

extern "C" and extern "C++" function declarations In C++, when used with a string, extern specifies that the linkage conventions of another language are being used for the declarator(s). C functions and data can be accessed only if they're previously declared as having C linkage.

What does extern mean in a function declaration in C?

the extern keyword is used to extend the visibility of variables/functions. Since functions are visible throughout the program by default, the use of extern is not needed in function declarations or definitions. Its use is implicit. When extern is used with a variable, it's only declared, not defined.

What does extern mean in a function declaration?

The extern keyword in C and C++ extends the visibility of variables and functions across multiple source files. In the case of functions, the extern keyword is used implicitly. But with variables, you have to use the keyword explicitly.


1 Answers

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.

like image 106
M.M Avatar answered Sep 29 '22 09:09

M.M