抽象跨平台实现的常见习语是什么
What is a common idiom to abstract cross-platform implementations?
我正在编写一个旨在跨平台的程序;因此,它将支持某些操作的多种实现。我的第一个想法是编写一个类的层次结构,有一个通用的接口,也许是每个平台的抽象工厂。
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;
抽象多个实现的好方法是什么,在编译时只会选择其中一个实现?我也对性能感兴趣,所以除非必要,否则我不想使用虚拟方法。
通常的解决方案是只定义一个类标头,并为其提供不同的源文件,编译并在必要时链接。 如果你需要一些类定义中的依赖关系(例如数据类型),您还可以为此,通过为每个平台,并包括它。 这里最简单的解决方案是可能为每个目标平台创建一个目录,并把所有那里的平台相关文件,使用 -I
//I
进行选择向上正确的目录(从而正确的平台)依赖文件)在编译时。
过度使用#ifdef
通常是设计不佳的标志。 看https://www.usenix.org/legacy/publications/library/proceedings/sa92/spencer.pdf。
您有几个选择:
-
使用问题中概述的方法。但是,您可能需要将平台特定的位拆分为单独的文件,并使用构建规则为平台引入正确的文件。
-
只需有一个类,并使用
#ifdef
为相应的平台插入正确的代码。 -
使用预先存在的包装器库(例如 Boost),它可能已经包装了平台特定的位,并针对包装器编写代码。
如果你要封装各种平台,那么你需要确保抽象不会泄漏到你的实现中。通常,您最终会使用列出的三个项目的组合。
你在这里拥有的是常用的。如果你绝对知道你永远不会同时有多个实现,你可以使用单个头文件,或者有条件编译的实现文件(cpp)或有平台预编译器指令从编译中排除某些代码(其他平台)。
您可以在标题中包含:
class Operation {
DoOperation();
}
多个 cpp 您可以从构建中排除文件:
平台1.cpp
Operation::DoOperation() { // Platform 1 implementation }
和平台2.cpp
Operation::DoOperation() { // Platform 2 implementation }
或单个实现文件:
#ifdef PLATFORM1
Operation::DoOperation() { // Platform 1 implementation }
elseif defined(PLATFORM2)
Operation::DoOperation() { // Platform 2 implementation }
#endif
或者混合使用 2 个,如果你不想弄乱从构建中排除文件:
平台1.cpp:
#ifdef PLATFORM1
Operation::DoOperation() { // Platform 1 implementation }
#endif
和平台 2.cpp:
#ifdef PLATFORM2
Operation::DoOperation() { // Platform 2 implementation }
#endif
你想要的是提供对不同平台的支持,同时保持源代码干净。因此,您应该像您的第一个想法一样将平台依赖项抽象到某个接口中。
同时,您可能希望使用 #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、Ext3File System 等。
看看现有的多平台框架,例如Qt。使用痘痘成语
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- C++避免重复声明的语法是什么
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- C++从另一个类访问公共静态向量的正确方法是什么
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- C++中名称篡改的目的是什么
- 在 c++ 中拥有一组结构的正确方法是什么?
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值
- 是什么阻止DOMTimerCoordinator::NextID进入无休止的循环
- 派生类销毁的最佳实践是什么
- 这个语法std::class<>{}(arg1, arg2) 在C++中是什么意思?
- 通过JNI传递数据数组的最快方法是什么
- "using namespace std;"在C++的作用是什么?
- 在两台机器之间进行时间戳的最佳c++chrono函数是什么
- 文件系统:复制功能的速度秘诀是什么
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 抽象跨平台实现的常见习语是什么
- c++常数值1j是什么意思?
- 对于非内置类型,函数通过常量值返回的用例是什么