抽象跨平台实现的常用习惯是什么?

时间:2014-01-13 16:59:59

标签: c++ design-patterns abstraction idioms

我正在编写一个旨在跨平台的程序;因此,它将支持某些操作的多个实现。我的第一个想法是编写一个类的层次结构,具有一个通用接口,可能是每个平台的抽象工厂。

class Operation {
    DoOperation() = 0;
}

class OperationPlatform1 : public Operation {
    DoOperation();
}

class OperationPlatform2 : public Operation {
    DoOperation();
}

#ifdef USING_PLATFORM1
    Operation *op = new OperationPlatform1;
#endif
但是,我意识到,将在编译时使用将要使用的实现。我试着想一想如何使用静态多态实现这一点,之后我意识到我还可以写下这些内容:

class OperationPlatform1 {
    DoOperation();
}

class OperationPlatform2 {
    DoOperation();
}

#ifdef USING_PLATFORM1
typedef OperationPlatform1 Operation;
#endif

Operation op;

抽象多个实现的好方法是什么,其中只有一个在编译时被选中?我也对性能感兴趣,所以除非必须,否则我不想使用虚拟方法。

5 个答案:

答案 0 :(得分:5)

通常的解决方案是只定义一个类 标题,并为其提供不同的源文件,进行编译 并链接任何必要的。如果你需要一些 您也可以在类定义中依赖(例如数据类型) 通过为每个创建一个特定的头文件来实现这一点 平台,包括它。这里最简单的解决方案是 可能是为每个目标平台创建一个目录,并把所有目录 平台相关文件的位置,使用-I / /I来选择 正确的目录(以及正确的平台) 依赖文件)在编译时。

过度使用#ifdef通常表明设计不佳。看到 https://www.usenix.org/legacy/publications/library/proceedings/sa92/spencer.pdf

答案 1 :(得分:2)

你有几个选择:

  1. 使用您问题中列出的方法。但是,您可能需要将平台特定位拆分为单独的文件,并使用构建规则为平台引入正确的文件。

  2. 只需拥有一个类,并使用#ifdef为相应的平台插入正确的代码。

  3. 使用预先存在的包装器库(例如Boost),它可能已经包装了特定于平台的位,并针对包装器进行了编码。

  4. 如果您打算封装各种平台,那么您需要确保抽象不会泄漏到您的实现中。通常,您最终会使用列出的三个项目的组合。

答案 2 :(得分:2)

这里有你常用的东西。如果你绝对知道你永远不会同时有多个实现,你可以使用单个头文件,或者有条件编译的实现文件(cpp),或者有平台预编译器指令从编译中排除一些代码(其他平台)。 / p>

你可以在标题中找到:

class Operation {
    DoOperation();
}

并且多个cpp 可以从您的构建中排除文件:

Platform1.cpp

Operation::DoOperation() { // Platform 1 implementation }

和Platform2.cpp

Operation::DoOperation() { // Platform 2 implementation }



单一实施文件

#ifdef PLATFORM1
    Operation::DoOperation() { // Platform 1 implementation }
elseif defined(PLATFORM2)
    Operation::DoOperation() { // Platform 2 implementation }
#endif



或者如果你不想从构建中排除文件,那么混合2

Platform1.cpp:

#ifdef PLATFORM1
    Operation::DoOperation() { // Platform 1 implementation }
#endif

和Platform2.cpp:

#ifdef PLATFORM2
    Operation::DoOperation() { // Platform 2 implementation }
#endif

答案 3 :(得分:1)

您想要的是在保持源代码清洁的同时为不同平台提供支持。因此,你应该抽象出像某个界面那样的平台依赖关系,就像你的第一个想法那样。

同时您可能希望使用#ifdef进行实施。例如,文件系统功能在很大程度上取决于平台,因此您将定义一个这样的文件系统接口。

class IFileSystem {
public:
    void CopyFile(const string& destName, const string& sourceName);
    void DeleteFile(const string& name);
    // etc.
};

然后你可以实现WindowsFileSystem,LinuxFileSystem,SolarisFileSystem等。

现在,当您的源代码需要文件系统功能时,您将访问所请求的界面,如下所示:

IFileSystem& GetFileSystem() {
   #ifdef WINDOWS
       return WindowsFileSystemInstance;
   #endif
   #ifdef LINUX
       return LinuxFileSystemInstance;
   #endif
   // etc.
}

GetFileSystem().CopyFile("dest", "source");

这种方法可以帮助您分离应用程序逻辑和平台依赖关系的问题。

getter函数的另一个好处是你仍然可以为文件系统实现做运行时选择,例如: Fat32FileSystem,NtfsFileSystem,Ext3FileSystem等。

答案 4 :(得分:0)

查看现有的多平台框架,例如Qt。使用PIMPL idiom