VRPN C++ 代码可在 Linux 上编译,但不能在 Windows 上编译

Posted

技术标签:

【中文标题】VRPN C++ 代码可在 Linux 上编译,但不能在 Windows 上编译【英文标题】:VRPN C++ code compiles on Linux but not Windows 【发布时间】:2016-01-29 18:06:59 【问题描述】:

我在 Linux 上构建了一个 VRPN 客户端。基于此:http://www.vrgeeks.org/vrpn/tutorial---use-vrpn

以下是部分代码:

vrpn_Analog_Remote * analog = NULL;
vrpn_Button_Remote * button = NULL;
vrpn_Tracker_Remote * tracker = NULL;

// Things happen...

analog = new vrpn_Analog_Remote("pathToAnalog");
analog->register_change_handler(NULL, handleAnalog);
button = new vrpn_Button_Remote("pathToButton");
button->register_change_handler(NULL, handleButton);
tracker = new vrpn_Tracker_Remote("pathToTracker");
tracker->register_change_handler(NULL, handleTracker);

以下是这段代码中引用的回调:

void handleAnalog(void * userData, const vrpn_ANALOGCB a) 
  // Do stuff...

void handleButton(void * userData, const vrpn_BUTTONCB b) 
  // Do stuff...

void handleTracker(void * userData, const vrpn_TRACKERCB t) 
  // Do stuff...

这里是所有这些对 VRPN 的引用的定义:

https://github.com/vrpn/vrpn/blob/master/vrpn_Analog.h#L168 https://github.com/vrpn/vrpn/blob/master/vrpn_Button.h#L225 https://github.com/vrpn/vrpn/blob/master/vrpn_Tracker.h#L284

这些在 Linux 上编译时甚至没有警告,并且可以实际使用。一切都按预期工作。这里的所有类型似乎都满足编译器g++。

但在 Windows 上,无论我使用 Visual Studio 2015 还是 MinGW 的 g++,前两个回调注册都会得到这个:

invalid conversion from 'void (*)(void*, vrpn_ANALOGCB) aka void (*)(void*, _vrpn_ANALOGCB)' to 'vrpn_ANALOGCHANGEHANDLER aka 
 void (__attribute__((__stdcall__)) *)(void*, _vrpn_ANALOGCB)' [-fpermissive]

invalid conversion from 'void (*)(void*, vrpn_BUTTONCB) aka void (*)(void*, _vrpn_BUTTONCB)' to 'vrpn_BUTTONCHANGEHANDLER aka 
 void (__attribute__((__stdcall__)) *)(void*, _vrpn_BUTTONCB)' [-fpermissive]

对于最后一个,我得到一个不同的错误:

call of overloaded 'register_change_handler(NULL, void (&)(void*, vrpn_TRACKERCB))' is 
 ambiguous

现在我正在输入此内容,我想也许 VRPN 在 Windows 上的编译方式不同,这就是编译器现在对我的代码有问题的原因。但我不知道该怎么做。

【问题讨论】:

看起来调用约定 (__stdcall__) 在编译过程中的某个地方丢失了。也许你需要设置一个#define或者编译器预处理器-D才能编译成功, 所以只需添加#define __stdcall__?我不太习惯 C++ 及其所有怪癖。¨ 我添加了 #define __stdcall 并收到警告说它已被定义。我可以在 Microsoft 文档中找到对它的引用,然后将其更改为 #define __stdcall__,现在我遇到了链接器错误。所以它奏效了。你怎么知道那是它?是在标头代码中吗? 你不应该定义__stdcall__。相反,您应该阅读适当的预处理器开关,以便您的库在 Windows 中正确编译。这应该是图书馆的作者给你的,否则你必须去发现它们。如果您正在创建库,仅将源代码从 Linux 获取到 Windows 并进行编译并不是无缝的。如果您不知道自己在做什么,那么您的库可能构建的可能性很大,但是当您尝试使用它时将完全无用(例如崩溃)。 嗯,错误在于函数签名不同,并且您的函数或库的定义中缺少调用约定(缺少的调用约定默认为__cdecl)。与将函数声明为返回 int 并采用单个 int 参数并尝试将指针分配给返回 double 并采用 int 参数的函数没有什么不同。对于 Windows,额外的问题是调用约定必须匹配。 【参考方案1】:

尝试像这样声明你的回调:

void VRPN_CALLBACK handleAnalog(void * userData, const vrpn_ANALOGCB a) 
  // Do stuff...

对于 linux,VRPN_CALLBACK 定义是空的,所以你没有注意到那里有任何问题。对于 Windows,VRPN 库开发人员决定他们期望一个符合 __stdcall 调用约定的回调。因此,您必须相应地声明您的函数,并且最轻松+便携的方法是使用它们提供的相同的 VRPN_CALLBACK 定义。

这方面的线索来自您在 github 上的代码链接:

[vrpn_Analog.h]
typedef void(VRPN_CALLBACK *vrpn_ANALOGCHANGEHANDLER)(void *userdata,
                                                      const vrpn_ANALOGCB info);

回调定义在这里:

[vrpn_Configure.h]
#ifdef _WIN32   // [ ...
#define VRPN_CALLBACK __stdcall
#else // ... ] WIN32 [
#define VRPN_CALLBACK
#endif // ] not WIN32

【讨论】:

以上是关于VRPN C++ 代码可在 Linux 上编译,但不能在 Windows 上编译的主要内容,如果未能解决你的问题,请参考以下文章

C++ 在 Mac OS X 上编译代码并在 Linux x86 上运行

在 Windows 和 Linux 上编译 C++:ifdef 开关 [重复]

BFD_RELOC_64:使用 C++ 在 32 位 linux 上编译汇编器指令

在 Windows 上编译+分发 Linux 代码

将 Visual Studio C++ 项目“移植”到 Linux 上编译的最简单方法是啥?

在 Mingw 上编译的 C/C++ 代码能否保证与 GCC 完全兼容(在 linux 和 Mac 上)