【问题标题】:Does a C++ cast strip the 'extern "C"' from a declaration?C++ 演员表是否从声明中删除了“extern "C""?
【发布时间】:2016-09-20 00:32:23
【问题描述】:

此问题与Warning (Anachronism): Assigning void(*)(int) to extern "C" void(*)(int) 有关。在引用的问题中,我们有一个函数指针 typedef 声明为 extern "C"

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

当我们尝试分配它时:

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

它导致了错误(行号有点偏离,但上面的行产生了它):

/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.

据我所知,extern "C" 在使用 reinterpret_cast 时被丢弃了。但是,C 转换按预期工作。

我相信 Sun Studio 12.4 (SunCC 5.13) 默认使用 C++03。但我的问题同时适用于 C++03 和 C++11,因为由于 GCC 4.8 和 4.9 的流行,我们现在看到了很多。

C++ 强制转换是否会从声明中去除 extern "C"


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;
}

【问题讨论】:

  • 语言链接是函数类型的一部分(你可以重载它);不幸的是,大多数编译器都没有正确实现这一点。
  • 谢谢克雷克。这是否意味着 SunCC 是正确的,我的 SignalHandlerFn 是错误的,其他编译器应该拒绝它?或者这是否意味着 SunCC 处理不当?不好意思问。我知道如何解决它;但我仍然不清楚问题是什么。
  • 您的NullHandler 肯定是错误的,因为类成员函数从来没有C 链接。事实上,GCC 和 Clang 完全忽略了这一点。您可以仔细检查 Embarcadero,我相信它正确地实施了这些规则。另一方面,pfn 应该是正确的。

标签: c++ linkage reinterpret-cast


【解决方案1】:

reinterpret_cast&lt;T&gt; 要么生成 T 类型的表达式,要么由于不存在允许的转换而格式错误。 (参考:[expr.reinterpret.cast]/1)。

语言链接是类型的一部分(参考:[dcl.link]/1)。

所以reinterpret_cast&lt;SignalHandlerFn&gt; 的结果要么是格式错误的,要么是指向带有 C 语言链接的函数的指针。

因此,将这种转换描述为“剥离外部 C”似乎并不正确——尽管编译器当然可能会通过发出诊断信息来对格式错误的代码做出反应,然后继续处理,就好像代码有一些任意的行为。


在您的代码示例中,reinterpret_cast&lt;SignalHandlerFn&gt; 的两种用法都是格式良好的,因为reinterpret_cast 可以将任何函数指针转换为任何其他函数指针(参考:[expr.reinterpret.cast]/6)。

但是,通过sa_handler 调用SignalHandler::NullHandler 将导致未定义的行为(参考:同上)。您的编译器产生的警告可能是为了警告这种情况。

【讨论】:

  • 我会说:允许从“extern C++”函数到“extern C”函数指针的隐式转换似乎是一种常见的编译器行为。也许编译器供应商出于实际原因故意这样做
  • 因为它是类型的一部分是不理智的。事实上,我从未见过真正发挥作用的案例。现在您不能将类成员函数传递给 C,但这是另一回事。
【解决方案2】:

sigaction结构定义为:

   struct sigaction {
       void     (*sa_handler)(int);
       void     (*sa_sigaction)(int, siginfo_t *, void *);
       sigset_t   sa_mask;
       int        sa_flags;
       void     (*sa_restorer)(void);
   };

而你使用 struct sigaction 的 void (*sa_handler)(int);从一些 SignalHandlerFn 中获得分配。 因此,两个声明都应该在 extern "C" 之内,例如

extern "C" {
  #include <signal.h>
};

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

或者两者都应该支持 C++ 的名称修改,即。没有extern "C" 为:

  #include <signal.h>

  typedef void (*SignalHandlerFn) (int);

【讨论】:

  • signal.h是C标准库函数的头文件,声明需要extern "C"链接(通常头文件会检测C++并正确设置链接,不需要@ 987654331@)
  • 仅供参考...Working with Pointers to Functions 的 Solaris 文档明确指出我们不应包含在 extern "C" 中。
  • 标准头文件应该已经包含extern "C" 在适用的情况下,写你所做的事情是未定义的行为
猜你喜欢
  • 2019-12-22
  • 2019-08-03
  • 2022-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多