C++ 设计模式:C++;抽象层

C++ 设计模式:C++;抽象层,c++,c,abstraction,hal,C++,C,Abstraction,Hal,我试图编写一个抽象层,让我的代码在不同的平台上运行。让我举一个我最终希望在高级代码中使用的两个类的例子: class Thread { public: Thread(); virtual ~Thread(); void start(); void stop(); virtual void callback() = 0; }; class Display { public: static void drawText(const char* t

我试图编写一个抽象层,让我的代码在不同的平台上运行。让我举一个我最终希望在高级代码中使用的两个类的例子:

class Thread
{
public:
    Thread();
    virtual ~Thread();

    void start();
    void stop();

    virtual void callback() = 0;
};

class Display 
{
public:
    static void drawText(const char* text);
};
我的问题是:我可以使用什么设计模式让低级代码填充实现? 以下是我的想法以及为什么我认为它们不是一个好的解决方案:

  • 理论上,将上述定义放在
    highLevel/thread.h
    中,将特定于平台的实现放在
    lowLevel/platformA/thread.cpp中是没有问题的。这是一个低开销的解决方案,可在链接时解决。唯一的问题是,低级实现不能向其中添加任何成员变量或成员函数。这使得某些事情无法实施

  • 一种解决方法是将此添加到定义中(基本上是Pimpl习惯用法):

    现在,低级代码可以在void指针中存储自己的结构或对象。这里的问题是,它的丑陋阅读和痛苦的程序

  • 我可以使
    类线程
    纯虚拟,并通过继承实现底层功能。高级代码可以通过调用如下工厂函数来访问低级实现:

    // thread.h, below the pure virtual class definition 
    extern "C" void* makeNewThread();
    
    // in lowlevel/platformA/thread.h 
    class ThreadImpl: public Thread
    { ... };
    
    // in lowLevel/platformA/thread.cpp
    extern "C" void* makeNewThread() { return new ThreadImpl(); }
    
    这将足够整洁,但对于静态类来说是失败的。我的抽象层将用于硬件和IO,我真的希望能够使用
    Display::drawText(…)
    ,而不是携带指向单个
    Display
    类的指针

  • 另一种选择是只使用可以在链接时解析的C风格函数,如下面的
    extern“C”handle\u t createThread()
    。这对于访问只存在一次的低级硬件(如显示器)来说是非常容易的。但是对于任何可能多次出现的东西(锁、线程、内存管理),我必须在我的高级代码中携带句柄,这很难看,或者有一个隐藏句柄的高级包装类。无论哪种方式,我都需要将句柄与高级别和低级别侧的相应功能相关联

  • 我最后的想法是混合结构。纯C风格的
    extern“C”
    函数用于只存在一次的低级内容。工厂功能(见3)中可以多次出现的内容。但我担心混合的东西会导致不一致、不可读的代码


  • 我非常感谢您为设计符合我要求的模式提供的提示。

    您不需要有平台无关的基类,因为您的代码一次只针对一个具体的平台进行编译

    只需将include路径设置为,例如,
    -Iinclude/generic-Iinclude/platform
    ,并在每个受支持平台的include目录中有一个单独的线程类

    您可以(也应该)编写默认编译和执行的平台无关测试,以确认不同的平台特定实现遵循相同的接口和语义

    另外,正如StoryTeller所说,线程是一个坏例子,因为已经有了一个可移植的
    std::Thread
    。我假设您确实需要抽象一些其他特定于平台的细节


    PPS。您仍然需要找出通用(与平台无关)代码和特定于平台的代码之间的正确划分:没有什么灵丹妙药来决定要做什么,只是在重用/复制、简单代码和高度参数化代码之间进行一系列权衡,等等。

    您似乎想要为
    线程
    类添加值语义,并想知道在哪里添加间接寻址以使其可移植。因此,您可以使用pimpl习惯用法和一些条件编译。
    根据您希望构建工具的复杂性在哪里,以及如果您希望使所有低级代码尽可能自包含,请执行以下操作:

    在高级标题
    Thread.hpp
    中,您定义:

    class Thread
    {
      class Impl:
      Impl *pimpl; // or better yet, some smart pointer
    public:
      Thread ();
      ~Thread();
      // Other stuff;
    };
    
    然后,在线程源目录中,您可以按照以下方式定义文件:

    Thread\u PlatformA.cpp

    #ifdef PLATFORM_A
    
    #include <Thread.hpp>
    
    Thread::Thread()
    {
      // Platform A specific code goes here, initialize the pimpl;
    }
    
    Thread::~Thread()
    {
      // Platform A specific code goes here, release the pimpl;
    }
    
    #endif
    
    #ifdef平台A
    #包括
    线程::线程()
    {
    //平台一个特定的代码在这里,初始化pimpl;
    }
    线程::~Thread()
    {
    //平台一个特定的代码放在这里,释放pimpl;
    }
    #恩迪夫
    

    构建
    Thread.o
    变得很简单,只需将所有
    Thread.*.cpp
    文件保存在Thread目录中,并让构建系统为编译器提供正确的
    -D
    选项。

    我很好奇,这种情况下的设计是什么样的(只是坚持线程):

    它不包含任何关于实现的内容,只包含接口

    那么你有:

    // platformA directory 
    class PlatformAThread { ... };
    
    这将自动导致,当您创建“泛型”
    Thread
    对象时,您也会自动获得一个平台相关类,该类会自动设置其内部,并且可能具有特定于平台的操作,当然,您的
    PlatformAThread
    类可能派生自一个通用的
    Base
    类,该类具有您可能需要的公共内容

    您还需要设置生成系统以自动识别特定于平台的目录


    另外,请注意,我倾向于创建类继承的层次结构,有些人建议不要这样做:

    还没有阅读您的整个问题。但是C++从C++ 11起就有了一个线程抽象层。我正在研究一个嵌入的目标,FreeRTOS支持线程的东西。另一个平台是基于计算机的仿真,用于更容易地开发高级代码。我认为C++11不支持这种情况。当然不是一个单独的
    线程
    类,而是
    线程
    类的单独实现。我不遵循include路径,因为这些是源目录:)如果我写两个不同的c
    // Your generic include level:
    // thread.h
    class Thread : public 
    #ifdef PLATFORM_A
        PlatformAThread
    #elif PLATFORM_B
        PlatformBThread
    // any more stuff you need in here
    #endif
    {  
        Thread();
        virtual ~Thread();
    
        void start();
        void stop();
    
        virtual void callback() = 0;
    } ;
    
    // platformA directory 
    class PlatformAThread { ... };