预处理程序指令的替代方案

时间:2009-05-13 11:21:33

标签: c++ c-preprocessor

我正在开发Symbian平台上的C ++手机应用程序。其中一个要求是它必须在所有Symbian手机上工作,从第2版手机到第5版手机。现在,跨版本的Symbian SDK存在差异。我必须使用预处理程序指令来有条件地编译与构建应用程序的SDK相关的代码,如下所示:

#ifdef S60_2nd_ED
  Code
#elif S60_3rd_ED
  Code
#else
  Code

现在,由于我正在开发的应用程序并不简单,它很快就会增长到成千上万行代码,并且上面的预处理程序指令将遍布各地。我想知道有没有替代方案,或者在这种情况下使用这些预处理器指令可能是更好的方法。

请帮忙。

9 个答案:

答案 0 :(得分:15)

嗯......这取决于差异的确切性质。如果可以将它们抽象出来并将它们分离成特定的类,那么你可以走这条路。这意味着具有某些类的特定于版本的实现,并且在这里和那里切换整个实现而不仅仅是几行。

你有

  • MyClass.h
  • MyClass_S60_2nd.cpp
  • MyClass_S60_3rd.cpp

等等。您可以选择要编译的CPP文件,方法是使用上面的#ifdef包装整个内部,或者在构建级别(通过Makefile或其他)时控制在为各种目标构建时包含哪些文件。

根据变化的性质,这可能更清晰。

答案 1 :(得分:6)

在我们公司,我们编写了很多跨平台代码(win32 / ps3 / xbox / etc的游戏开发)。
为了尽可能避免与平台相关的宏,我们通常使用接下来的几个技巧:

  • 将与平台相关的代码提取到平台抽象库中,这些库在不同平台上具有相同的接口,但实现方式不同;
  • 将代码拆分为不同平台的不同.cpp文件(例如:“pipe.h”,“pipe_common.cpp”,“pipe_linux.cpp”,“pipe_win32.cpp”,...);
  • 使用宏和辅助函数来统一特定于平台的函数调用(例如:“#define usleep(X)Sleep((X)/ 1000u)”);
  • 使用跨平台的第三方库。

答案 2 :(得分:5)

我一直都在你身边。

一个诀窍是,即使您在代码中有条件,也不要打开Symbian版本。这使得将来很难添加对新版本的支持,或者为某些方面不寻常的手机进行自定义。相反,确定您依赖的实际属性,围绕这些属性编写代码,然后包含一个头文件:

#if S60_3rd_ED
    #define CAF_AGENT 1
    #define HTTP_FILE_UPLOAD 1
#elif S60_2nd_ED
    #define CAF_AGENT 0
    #if S60_2nd_ED_FP2
        #define HTTP_FILE_UPLOAD 1
    #else
        #define HTTP_FILE_UPLOAD 0
    #endif
#endif

等等。显然,如果您愿意,可以按功能而不是版本对定义进行分组,每个配置具有完全不同的标头,或者适合您的任何方案。

我们已经定义了您继承的UI类,因此在S60和UIQ之间存在一些共同的UI代码。事实上,由于产品是什么,我们没有太多与UI相关的代码,因此它的相当大比例很常见。

正如其他人所说,在可能的情况下将变量行为放入类和函数中会更好,并链接不同的版本。

[编辑以回应评论:

我们非常努力避免做任何依赖于解决方案的事情 - 幸运的是,特定应用并没有让这太难,所以我们有限的用户界面非常通用。我们打开屏幕分辨率的主要原因是飞溅/背景图像等。我们有一个脚本来预处理构建文件,它将宽度和高度替换为文件名,splash_240x320.bmp或其他。我们实际上手工生成了图像,因为没有那么多不同的尺寸,图像也没有经常变化。同一脚本生成一个.h文件,其中包含构建文件生成中使用的大多数值的#defines。

这适用于每个设备的构建:我们还有更多通用的SIS文件,它们只是动态调整图像大小,但我们经常对安装的大小有要求(ROM有时非常有限,如果您的应用程序属于基本设备图像),并调整图像大小是一种方法来保持它一点点。为了支持N92,Z8等的屏幕旋转,我们仍然需要一些图像的纵向和横向版本,因为翻转宽高比不能提供与调整到相同或相似比率的良好结果...]

答案 3 :(得分:1)

如果可能,您可以尝试为所有平台定义通用接口。然后,为每个平台实现接口。

使用预处理程序指令选择正确的实现。

这样,您将在代码中较少的位置使用平台选择指令(理想情况下,在一个地方,明确地在声明接口的头文件中)。

这意味着:

commoninterface.h /* declaring the common interface API. Platform identification preprocessor directives might be needed for things like common type definitions */
platform1.c /*specific implementation*/
platform2.c /*specific implementation*/

答案 4 :(得分:1)

看看SQLite。他们有同样的问题。他们将依赖于平台的东西移动到单独的文件中,并通过使用排除整个文件内容的预处理器指令有效地编译所需的东西。这是一种广泛使用的方法。

答案 5 :(得分:1)

S60第二版和第三版应用程序之间存在一些差异,不仅限于代码:应用程序资源文件不同,图形格式和打包它们的工具不同,mmp文件在很多方面有所不同。

根据我的经验,不要尝试将其自动化太多,而是为第二版和第三版编写单独的构建脚本。在代码级别,对具有公共抽象API的类具有不同的差异,仅在极少数情况下使用标记。

答案 6 :(得分:0)

没有关于替代方案的想法,但您可以做的是,使用不同的文件来包含不同版本的操作系统。示例

#ifdef S60_2nd_ED

#include "graphics2"

#elif S60_3rd_ED

#include "graphics3"

#else

#include "graphics"

答案 7 :(得分:0)

你可以像linux内核中的程序集定义一样。每个体系结构都有自己的目录(例如asm-x86)。所有这些文件夹都聚集了相同的高级头文件,呈现相同的界面。配置内核后,将创建一个名为asm的链接,其目标是相应的asm-arch目录。这样,所有C文件都包含像。

答案 8 :(得分:0)

你应该尽量避免在代码中传播#ifs。

相反;使用头文件中的#if来定义备用宏,然后在代码中使用单个宏。

此方法允许您使代码更具可读性。

示例:

 Plop.h
 ======

 #if V1
 #define    MAKE_CALL(X,Y)    makeCallV1(X,Y)
 #elif V2
 #define    MAKE_CALL(X,Y)    makeCallV2("Plop",X,222,Y)
 ....
 #endif


 Plop.cpp
 ========

 if (pushPlop)
 {
     MAKE_CALL(911,"Help");
 }

为了帮助将这种特定于分割版本的代码扩展到它们自己的函数中,然后使用宏来激活上面显示的函数。此外,您可以将SDK的更改部分包装在您自己的类中,以尝试提供一致的界面,然后在包装类中管理所有差异,从而使您的代码更加整洁。

相关问题