多平台 class 设计 C++
Multiplatform class design C++
我想到了一种非标准的方式来处理 C++ 中的多平台接口,并且想知道这是否通常是一个坏主意以及为什么。
目前我只能想到一个缺点:做这样的事情非常(?)不常见,也许乍一看它的工作原理并不明显:
我有一个将在不同平台上使用的 class,例如 CMat4x4f32(4x4 矩阵 class 使用 32 位浮点数)。
我的平台独立界面如下所示:
class CMat4x4f32
{
public:
//Some methods
#include "Mat4x4f32.platform.inl"
};
Mat4x4f32.platform.inl 看起来像这样:
public:
// Fills the matrix from a DirectX11 SSE matrix
void FromXMMatrix(const XMMatrix& _Matrix);
它只是向矩阵添加了一个依赖于平台的接口 class。
.cpp 和 Mat4x4f32.platform.inl 位于 "win32" 或 "posix" 等子文件夹内,因此在 win32 中我实现了 FromXMMatrix 函数。我的构建系统根据我构建的平台将这些子文件夹添加到包含路径。
我什至可以更进一步,实现一个位于 win32 内部的 .platform.cpp,它只包含我添加到该平台界面的功能。
我个人认为这是个好主意,因为它使编写和使用界面变得非常简单和干净。
特别是在我的渲染器库中,它大量使用了我的基础库中的 Matrix class,我现在可以在 DirectX 部分使用依赖于平台的函数 (FromXMMatrix),就好像我不需要担心任何其他平台一样。
在基础库本身中,我仍然可以使用通用矩阵接口编写平台独立代码。
我还有其他 classes,它在其中很有用:例如 Error class,它收集错误并自动将它们转换为可读消息并提供一些调试选项。
对于 win32,我可以从错误的 DirectX 和 Win32 HResults 创建错误实例,在 Linux 上,我可以从返回的 errno 创建它们。在基础库中,我有一个 class 使用公共错误接口管理这些错误。
它极大地减少了所需的代码,并防止了丑陋的平台依赖于 util classes。
那么这个设计是好是坏,有哪些替代方案?
听起来你在谈论使用桥接模式:
http://c2.com/cgi/wiki?BridgePattern
根据我的个人经验,我开发了很多平台无关的接口,具体实现使用了这种模式并且效果很好,我经常将它与 Pimpl 惯用语一起使用:
http://c2.com/cgi/wiki?PimplIdiom
作为备选方案,我发现这个站点通常非常适合解释各种模式和范例的优缺点:
http://c2.com/cgi/wiki
我建议您改用 "pimpl":
class CMat4x4f32
{
public:
CMat4x4f32();
void Foo();
void Bar();
private:
std::unique_ptr<CMat4x4f32Impl> m_impl;
};
然后在构建文件配置中引入特定于平台的 .cpp 文件,例如您可以在其中定义特定于平台的函数:
class CMat4x4f32::CMat4x4f32Impl
{
public:
void Foo() { /* Actual impl */ }
void Bar() { /* Actual impl */ }
// Fills the matrix from a DirectX11 SSE matrix
void FromXMMatrix(const XMMatrix& _Matrix);
};
CMat4x4f32::CMat4x4f32() : m_impl(new CMat4x4f32Impl()) {}
CMat4x4f32::Foo() { m_impl->Foo(); }
CMat4x4f32::Bar() { m_impl->Bar(); }
我想到了一种非标准的方式来处理 C++ 中的多平台接口,并且想知道这是否通常是一个坏主意以及为什么。
目前我只能想到一个缺点:做这样的事情非常(?)不常见,也许乍一看它的工作原理并不明显:
我有一个将在不同平台上使用的 class,例如 CMat4x4f32(4x4 矩阵 class 使用 32 位浮点数)。 我的平台独立界面如下所示:
class CMat4x4f32
{
public:
//Some methods
#include "Mat4x4f32.platform.inl"
};
Mat4x4f32.platform.inl 看起来像这样:
public:
// Fills the matrix from a DirectX11 SSE matrix
void FromXMMatrix(const XMMatrix& _Matrix);
它只是向矩阵添加了一个依赖于平台的接口 class。
.cpp 和 Mat4x4f32.platform.inl 位于 "win32" 或 "posix" 等子文件夹内,因此在 win32 中我实现了 FromXMMatrix 函数。我的构建系统根据我构建的平台将这些子文件夹添加到包含路径。
我什至可以更进一步,实现一个位于 win32 内部的 .platform.cpp,它只包含我添加到该平台界面的功能。
我个人认为这是个好主意,因为它使编写和使用界面变得非常简单和干净。 特别是在我的渲染器库中,它大量使用了我的基础库中的 Matrix class,我现在可以在 DirectX 部分使用依赖于平台的函数 (FromXMMatrix),就好像我不需要担心任何其他平台一样。
在基础库本身中,我仍然可以使用通用矩阵接口编写平台独立代码。
我还有其他 classes,它在其中很有用:例如 Error class,它收集错误并自动将它们转换为可读消息并提供一些调试选项。 对于 win32,我可以从错误的 DirectX 和 Win32 HResults 创建错误实例,在 Linux 上,我可以从返回的 errno 创建它们。在基础库中,我有一个 class 使用公共错误接口管理这些错误。
它极大地减少了所需的代码,并防止了丑陋的平台依赖于 util classes。
那么这个设计是好是坏,有哪些替代方案?
听起来你在谈论使用桥接模式: http://c2.com/cgi/wiki?BridgePattern
根据我的个人经验,我开发了很多平台无关的接口,具体实现使用了这种模式并且效果很好,我经常将它与 Pimpl 惯用语一起使用: http://c2.com/cgi/wiki?PimplIdiom
作为备选方案,我发现这个站点通常非常适合解释各种模式和范例的优缺点: http://c2.com/cgi/wiki
我建议您改用 "pimpl":
class CMat4x4f32
{
public:
CMat4x4f32();
void Foo();
void Bar();
private:
std::unique_ptr<CMat4x4f32Impl> m_impl;
};
然后在构建文件配置中引入特定于平台的 .cpp 文件,例如您可以在其中定义特定于平台的函数:
class CMat4x4f32::CMat4x4f32Impl
{
public:
void Foo() { /* Actual impl */ }
void Bar() { /* Actual impl */ }
// Fills the matrix from a DirectX11 SSE matrix
void FromXMMatrix(const XMMatrix& _Matrix);
};
CMat4x4f32::CMat4x4f32() : m_impl(new CMat4x4f32Impl()) {}
CMat4x4f32::Foo() { m_impl->Foo(); }
CMat4x4f32::Bar() { m_impl->Bar(); }