设计具有不同平台特定实现的功能的最佳实践是什么?
例如,我在库模块中有一个结构看起来像这样的函数(已导出):
void foo()
{
#ifdef PLATFORM_WINDOWS
// windows-specific implementation
#elif PLATFORM_LINUX
// linux-specific implementation
#elif PLATFORM_SOLARIS
// solaris-specific implementation
#endif
}
每个部分都可以(并且确实有atm)包含很多代码,这使得它很难阅读等等。
做这样的事情的正确方法是什么?
答案 0 :(得分:3)
如果实现方式完全不同,则最好不要使用预处理器条件,而是为每个平台使用单独的.c文件。每个都将包含在共享头文件中声明的相同功能的不同平台特定实现。然后,构建系统将选择正确的文件。
例如在GLFW中,有x11_window.c和win32_window.c,并且都实现相同的功能,例如_glfwPlatformGetWindowSize()。
答案 1 :(得分:3)
在C ++中执行此操作的一种流行方法是使用接口抽象出实现细节。 您可以使用它们来创建独立于平台的代码,这也确实有助于处理不再支持API的情况。
例如,假设您有一个想要使用DirectX或OpenGL的引擎,您的类可能看起来像这样。
class IEngine
{
public:
virtual void InitEngine() = 0;
};
class OpenGLEngine : public IEngine
{
public:
void InitEngine() override
{
//OpenGL specific implementation here
}
}
然后,当您初始化IEngine实例时,您的代码将特定于您创建的任何类型的引擎,但是无论实现上的差异如何,您都可以重用相同的接口代码。
答案 2 :(得分:1)
我从C ++的角度答复。 C的答案将大不相同,我对此没有提供任何建议的信心。有些原则可以很好地翻译,但有些则不能。
我建议您将特定于平台的代码隐藏在界面后面。在界面内部,提供一个静态函数以返回指向您的API的指针,但不要在与平台无关的代码中对其进行定义。
然后在平台特定的单独文件中创建从该接口继承的不同类。 在特定于平台的.cpp文件中,您提供了在接口中声明的静态函数的定义。
对于不适当的平台,我建议您从构建脚本中完全排除平台特定的文件。失败的话,您应该将它们的整体包装在适当的ifdef子句中,但是很容易犯错并且不太可靠。
请注意,在这种情况下,在函数内部执行的计算可能对性能至关重要-可以。限制是由于虚函数调用,此类函数不应在紧密的循环内调用。
如果您确实需要压缩每一盎司的性能,则可以摆脱该接口,松开它提供的安全性和美观性,并仅在不同的.h / .cpp文件中实现相同的功能。如果您使用C编写,这是您最有可能做的事情-但是,我还是希望一些C专家对此发表意见。
一个最小的示例如下所示:
MyPlatformSpecificAPI.h
class MyPlatformSpecificAPI
{
public:
virtual ~MyPlatformSpecificAPI() = default; //Don't forget a virtual destructor
static MyPlatformSpecificAPI* getPlatformSpecificAPI(); //Notice, no implementation
virtual uint8_t myPlatformSpecificFoo(uint32_t bar) = 0;
//Because we're declaring an explicit destructor, explicitly default the 4 special member functions, check Rule of Five
MyPlatformSpecificAPI(const MyPlatformSpecificAPI&) = default;
MyPlatformSpecificAPI(MyPlatformSpecificAPI&&) = default;
MyPlatformSpecificAPI& operator=(const MyPlatformSpecificAPI&) = default;
MyPlatformSpecificAPI& operator=(MyPlatformSpecificAPI&&) = default;
};
MyPlatformSpecificAPI_Windows.h
#include "MyPlatformSpecificAPI.h"
class MyPlatformSpecificAPI_WIN64 : public MyPlatformSpecificAPI
{
public:
virtual uint8_t myPlatformSpecificFoo(uint32_t bar) override;
static MyPlatformSpecificAPI_WIN64 s_API;
};
MyPlatformSpecificAPI_Windows.cpp
uint8_t MyPlatformSpecificAPI_WIN64::myPlatformSpecificFoo(uint32_t bar)
{
//Perform windows specific calculations
return 42; //because 42 is always the answer
}
MyPlatformSpecificAPI* MyPlatformSpecificAPI::getPlatformSpecificAPI()
{
return &MyPlatformSpecificAPI_WIN64::s_API;
}
MyPlatformSpecificAPI_Xbox.h
#include "MyPlatformSpecificAPI.h"
class MyPlatformSpecificAPI_Xbox : public MyPlatformSpecificAPI
{
public:
virtual uint8_t myPlatformSpecificFoo(uint32_t bar) override;
static MyPlatformSpecificAPI_Xbox s_API;
}
MyPlatformSpecificAPI_Xbox.cpp
uint8_t MyPlatformSpecificAPI_Xbox::myPlatformSpecificFoo(uint32_t bar)
{
//Perform Xbox specific calculations
return 84;
};
MyPlatformSpecificAPI* MyPlatformSpecificAPI::getPlatformSpecificAPI()
{
return &MyPlatformSpecificAPI_Xbox::s_API;
}