编写跨平台的C++代码(Windows,Linux和Mac OSX)

36

这是我第一次尝试用C++编写稍微复杂的代码,我试图构建一个共享库,以便可以从Objective-C和.NET应用程序中进行接口调用(好的,后面这部分稍后再做...)

我现有的代码如下 -

#ifdef TARGET_OS_MAC
  // Mac Includes Here
#endif

#ifdef __linux__
  // Linux Includes Here
  #error Can't be compiled on Linux yet
#endif

#ifdef _WIN32 || _WIN64
  // Windows Includes Here
  #error Can't be compiled on Windows yet
#endif

#include <iostream>

using namespace std;

bool probe(){
  #ifdef TARGET_OS_MAC
    return probe_macosx();
  #endif
  #ifdef __linux__
    return probe_linux();
  #endif
  #ifdef _WIN32 || _WIN64
    return probe_win();
  #endif
}

bool probe_win(){
  // Windows Probe Code Here
  return true;
}

int main(){

  return 1;
}

我收到了一个编译器警告,简单地说:untitled: In function ‘bool probe()’:untitled:29: warning: control reaches end of non-void function - 但我也非常感激大家能提供任何关于如何更好地编写这种代码的信息或资源。

5个回答

52

不要重复自己并且反复编写相同的 #ifdef .... 行,你最好在头文件中声明probe()方法,并提供三个不同的源文件,每个平台一个。这样做的好处是,如果你添加一个平台,你不需要修改所有现有的源代码,只需添加新文件。使用构建系统选择适当的源文件。

结构示例:

include/probe.h
src/arch/win32/probe.cpp
src/arch/linux/probe.cpp
src/arch/mac/probe.cpp

警告是因为probe()函数没有返回值。换句话说,三个#ifdef语句中没有一个匹配。


5
听听!(1)这样可以让您的代码更清晰,(2)减轻了只熟悉一种平台的人阅读代码时的心理负担,(3)不依赖于奥秘的非可移植编译器/平台特定标志。 - Alex B
5
不要依赖于编译器定义的平台宏,而是让你的构建系统定义自己的宏。 - Cat Plus Plus
2
我知道预处理器有其用处,但在我看来,预处理器应该被放弃,跨平台编码应该使用其他方法实现。 - T.E.D.
3
阿门!特定于平台的代码最好由构建系统管理,而不是由C++预处理器管理。 - Nemanja Trifunovic
1
@ Roger Pate - 是的,如果你小心谨慎,一切都可以通过 #ifdef 转义(除了可能必须在文件顶部放置 "#pragma once"?),但是不必担心这些问题会更容易。 - Martin Beckett
显示剩余2条评论

33

我来解释一下这个具体的函数:

bool probe() {
#ifdef TARGET_OS_MAC
  return probe_macosx();
#elif defined __linux__
  return probe_linux();
#elif defined _WIN32 || defined _WIN64
  return probe_win();
#else
#error "unknown platform"
#endif
}

以if-elif-else的链式方式编写可以消除错误,因为要么有有效的返回语句,要么会遇到#error。 (我相信WIN32定义了32位和64位的Windows,但如果没有查阅资料,我无法确定。 这将简化代码。)

很遗憾,您不能使用#ifdef _WIN32 || _WIN64:请参见http://codepad.org/3PArXCxo以获取示例错误消息。 您可以像我上面所做的那样使用特殊的预处理-only defined运算符。


关于根据功能或整个文件(如建议所示)拆分平台,您可以选择这样做或不这样做。这将取决于您代码的细节,例如平台之间共享了多少内容以及您(或您的团队)发现什么是保持功能同步的最佳方法,以及其他问题。
此外,您应该在构建系统中处理平台选择,但这并不意味着您不能使用预处理器:为每个平台使用由makefile或构建系统有条件地定义的宏。实际上,这通常是模板和内联函数最实用的解决方案,使其比尝试消除预处理器更加灵活。它与整个文件方法结合得很好,因此在适当的情况下仍然使用它。
您可能希望拥有一个单一的配置头文件,将所有各种编译器和平台特定的宏转换为您控制的众所周知和理解的宏。或者,您可以通过构建系统将-DBEAKS_PLAT_LINUX添加到编译器命令行中,以定义该宏(请记住为宏名称使用前缀)。

5

在您编译代码时,似乎没有定义TARGET_OS_MAC__linux___WIN32_WIN64

因此,就像您的代码是这样的:

bool probe(){
}

这就是为什么编译器会抱怨到达了非void函数的结尾,因为没有return子句。
此外,对于更一般的问题,以下是我在开发多平台/架构软件/库时的指导方针:
避免特定情况。尽量编写与操作系统无关的代码。
处理系统特定的事物时,尝试将其包装成“不透明”类。例如,如果您正在处理文件(Linux和Windows上有不同的API),请尝试创建一个File类,它将嵌入所有逻辑并提供通用接口,无论操作系统如何。如果某些功能在其中一个操作系统上不可用,请处理它:如果该功能对于特定操作系统没有意义,则通常可以不采取任何措施。
简而言之:越少的#ifdef越好。无论您的代码有多便携,都要在发布之前在每个平台上进行测试。
祝您好运;)

非常感谢...我在IRC上得到了帮助(Roger),并给出了“正确”的答案,但这是很好的建议,一如既往。 - Lee Hambley
@Beaks:没问题;)很高兴你得到了你的问题的答案。 - ereOn

1
警告是因为如果没有定义任何定义,那么您的探测函数中就没有return。解决方法是放置一个默认的return

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接