C++ 转换是否从声明中删除 'extern "C"' ?
Does a C++ cast strip the 'extern "C"' from a declaration?
此问题与 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;
}
A reinterpret_cast<T>
要么生成类型为 T
的表达式,要么由于不存在允许的转换而格式错误。 (参考:[expr.reinterpret.cast]/1)。
语言链接是类型的一部分(参考:[dcl.link]/1)。
所以 reinterpret_cast<SignalHandlerFn>
的结果要么格式错误,要么是指向具有 C 语言链接的函数的指针。
因此,将此转换描述为 "stripping extern C" 似乎不正确——尽管编译器当然可能会通过发出诊断来对格式错误的代码作出反应,然后继续处理,就好像代码已经一些任意行为。
在您的代码示例中,reinterpret_cast<SignalHandlerFn>
的两种用法都是合式的,因为 reinterpret_cast
可以将任何函数指针转换为任何其他函数指针(参考:[expr.reinterpret.cast]/ 6).
但是,通过 sa_handler
调用 SignalHandler::NullHandler
将导致未定义的行为(参考:同上)。您的编译器产生的警告可能是为了警告这种情况。
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++ 的名称修改,i.ee。没有 extern "C"
作为:
#include <signal.h>
和
typedef void (*SignalHandlerFn) (int);
此问题与 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;
}
A reinterpret_cast<T>
要么生成类型为 T
的表达式,要么由于不存在允许的转换而格式错误。 (参考:[expr.reinterpret.cast]/1)。
语言链接是类型的一部分(参考:[dcl.link]/1)。
所以 reinterpret_cast<SignalHandlerFn>
的结果要么格式错误,要么是指向具有 C 语言链接的函数的指针。
因此,将此转换描述为 "stripping extern C" 似乎不正确——尽管编译器当然可能会通过发出诊断来对格式错误的代码作出反应,然后继续处理,就好像代码已经一些任意行为。
在您的代码示例中,reinterpret_cast<SignalHandlerFn>
的两种用法都是合式的,因为 reinterpret_cast
可以将任何函数指针转换为任何其他函数指针(参考:[expr.reinterpret.cast]/ 6).
但是,通过 sa_handler
调用 SignalHandler::NullHandler
将导致未定义的行为(参考:同上)。您的编译器产生的警告可能是为了警告这种情况。
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++ 的名称修改,i.ee。没有 extern "C"
作为:
#include <signal.h>
和
typedef void (*SignalHandlerFn) (int);