如何设计一个良好的平台抽象层?

5
我们在大型项目中有一些中央类和函数,用于抽象实际平台类型,例如mutex、file、thread等。这是很好的,但我希望更进一步,不将任何系统包含在头文件中(例如#include <windows.h>),这将是真正的平台抽象和更快的编译。缺点是你不能只使用typedef来定义系统类型(例如Windows HANDLE)。
选项1:PImpl习惯用法
class RwMutex
{
    // .....
private:
   struct Impl;
   Impl*   m_Impl;
}
  • 优点:实现和平台类型在C++中被很好地隐藏。
  • 缺点:涉及两阶段构造('new',我们没有异常),可能会失败。操作繁琐。

选项2:命名空间函数

class RwMutex {
public:
    bool LockRead() {return RwMutexLockRead( this );}
private:
    char m_AnonymousMember[ 16 ];
}
bool RwMutexLockRead( RwMutex* p );
  • 优点:实现可以直接链接使用,非常适合将其放置在自己的库中。
  • 缺点:涉及到重新解释成员所占用的空间,不利于调试。同时也需要大量的工作。

也许我对此有些着迷,但如果项目代码数量庞大并且没有任何平台相关的包含文件,那么通过-nostdinc选项来强制执行这一点会很酷。


1
你究竟想做什么?同步处理、线程、文件系统等都可以使用Boost以平台无关的方式完成。 - Philipp
在Windows上似乎可以不使用std或boost,但如果您的要求几乎需要使用它们,那么没有理由不使用它们。这将节省大量时间。 - ActiveTrayPrntrTagDataStrDrvr
正如我所说:我试图隐藏系统包含文件。我同意使用Boost可以节省很多时间,但据我所知,Boost使用异常,而我们的代码没有。不幸的是,我认为这是一个错误的决定,我们没有像应该使用C++。 - Borph
2个回答

4
选项1使用指针是一个不好的想法。请使用boost::scoped_ptr 或者如果可以的话,使用std::unique_ptr

选项2,您实现的方式不应该使用。请看GotW #28: The Fast Pimpl Idiom。但在C++11中,这可以使用std::aligned_storage<>正确完成。我曾经编写过一个pimpl_ptr<T, Size, Align=default>为您执行转换、复制和析构函数调用,并检查您是否选择了正确的Size

通常情况下,请使用pimpl,除非您已经进行了性能分析并证明它是瓶颈。

但一如既往地,请不要重复造轮子。互斥锁和线程是新的C++11标准的一部分,因此要么升级编译器,要么使用Boost。对于文件,请使用Boost。原因是许多新的C++11库都来自Boost。


关于选项1:当然,我们有自己的UniquePtr<>。关于选项2:感谢提供链接。很抱歉没有更完整的示例。我们确实有一个详细的AnonymousMember<>模板来处理对齐和转换,但是确实有点奇怪。我想“不重复造轮子”是最好的提示。不幸的是,在很长一段时间内要求C++11还不是一个选项 :-(。可能我在过度思考这个问题。 - Borph
请问pimpl_ptr是做什么用的?如果您使用Pimpl惯用语,就不需要为char数组获取大小,因为您可以从cpp中new类,知道正确的类型和大小。如果您像我一样有一个char数组成员,那么它就不再是pimpl了。 - Borph
@Borph:我的pimpl_ptrstd::aligned_storage<>的包装器,但它“感觉”像一个智能指针。 - ipc

2
为什么需要一个实现对象?你的代码不仅会在同一平台上发生变化,而且你将在使用当前平台时使用相同的实现。在头文件中定义接口,并为每个目标平台提供各种*.cpp文件以进行实现。使用#ifndef、#else等。
例如,你的文件系统可能如下所示:
include/
    RwMutex.hpp
    // etc...
src/
    RwMutex/
       RwMutexWin32.cpp
       RwMutexUnix.cpp
       // etc... 

这是 RwMutexWin32.cpp 可能看起来的样子:
#ifdef MYLIBPREFIX_PLATFORM_WIN32
#include "RwMutex.hpp"

#include <Windows.h>

// implement the Windows version of the RwMutex class

另一个例子可能是看一下SDL或其他用C或C++编写的跨平台库。它们主要使用预处理器系统。你不太可能想改变Impl对象指向的内容(例如,在Windows机器上使用Unix实现没有意义,也无法编译)。
当然,如果你真的想这样做,我建议使用CMake。但正如其他人建议的那样,你应该尝试使用其他库,比如boost标准库,因为已经为你编写了。

实际上我们正在使用CMake,通常是你指出的RwMutexWin32.cpp/RwMutexUnix.cpp方式,但我们在CMakeLists.txt中另外加了一个if/else,我不太喜欢这种方法。 如果在RwMutex.hpp类中作为成员使用Windows的CRITICAL_SECTION,那么它需要在头文件中包含#include - Borph
Pimpl-idiom没有任何多态的目的,只是为了将平台类型隐藏在cpp中。 - Borph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接