设计模式:C++抽象层
Design pattern: C++ Abstraction Layer
我正试图编写一个抽象层,让我的代码在不同的平台上运行。让我举一个例子,说明我最终想要在高级代码中使用的两个类:
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习语):
class Thread { // ... private: void* impl_data; }
现在,低级代码可以在void指针中存储自己的结构或对象。这里的问题是它读起来很难看,编程起来很痛苦。
我可以让
class Thread
成为纯虚拟的,并通过继承它来实现低级别的功能。高级代码可以通过调用这样的工厂函数来访问低级别的实现:// 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_t createThread()
。这对于访问只存在一次的低级别硬件(如显示器)来说既简单又方便。但对于任何可能多次出现的东西(锁、线程、内存管理),我必须在我的高级代码中携带句柄,这很难看,或者有一个隐藏句柄的高级包装器类。无论哪种方式,我都需要将句柄与高层和底层的相应功能相关联。我最后一个想法是混合结构。纯C风格的
extern "C"
函数用于只存在一次的低级内容。工厂函数(请参见3.)中可以存在多次的东西。但我担心一些混合的东西会导致不一致、不可读的代码。
如果能给我一些提示,让我设计出符合我要求的图案,我将不胜感激。
您不需要有一个与平台无关的基类,因为您的代码一次只为单个具体平台编译。
例如,只需将include路径设置为-Iinclude/generic -Iinclude/platform
,并在每个受支持的平台的include目录中有一个单独的Thread类。
您可以(也应该)编写与平台无关的测试,编译&默认情况下执行,以确认不同的特定于平台的实现遵守相同的接口和语义。
PS。正如StoryTeller所说,Thread是一个糟糕的例子,因为已经有了一个可移植的std::thread
。我假设你确实需要抽象一些其他特定于平台的细节。
PPS。您仍然需要弄清楚通用(与平台无关)代码和特定于平台的代码之间的正确划分:没有什么灵丹妙药可以决定去哪里,只有重用/重复、简单代码与高度参数化代码之间的一系列权衡。
您似乎想要Thread
类的值语义,并想知道在哪里添加间接性以使其可移植。所以你使用了皮条习语和一些条件汇编
根据您希望构建工具的复杂性所在的位置,如果您希望尽可能保持所有低级别代码的自包含性,请执行以下操作:
在您的高级标头Thread.hpp
中,您定义:
class Thread
{
class Impl:
Impl *pimpl; // or better yet, some smart pointer
public:
Thread ();
~Thread();
// Other stuff;
};
然后,在线程源目录中,按照以下方式定义文件:
Thread_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
构建Thread.o
变得很简单,只需获取Thread目录中的所有Thread_*.cpp
文件,并让构建系统为编译器提供正确的-D
选项。
我很好奇,像下面这样设计这种情况会是什么样子(只是坚持线程):
// 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 { ... };
这将自动导致,当您创建"通用"Thread
对象时,您还会自动获得一个依赖于平台的类,该类会自动设置其内部结构,并且可能具有特定于平台的操作,当然,您的PlatformAThread
类可能派生自一个具有您可能需要的常见内容的通用Base
类。
您还需要设置您的构建系统以自动识别特定于平台的目录。
此外,请注意,我倾向于创建阶级继承的层次结构,有些人建议不要这样做:https://en.wikipedia.org/wiki/Composition_over_inheritance
- 无法创建抽象类的实例
- 如何定义一个纯抽象基类
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 用pybind11包装C++抽象类时出错
- 如何处理从一个对象传递到另一个在C++中具有公共抽象类的对象的消息
- 有没有办法按值将纯抽象类的所有子类传递给 C++ 中的函数?
- 抽象类错误,请参阅声明" "是抽象的
- 将自定义函数传递到基抽象类中以延迟执行
- 命名参数习惯用法和(抽象)基类
- 打印抽象对象 c++
- 如何在从抽象基派生的类中实现相同的方法?
- 将包含抽象类和普通类C++包导出到 Python
- C++:处理抽象类中的错误时出现问题
- C ++如何在原始抽象类中创建一个函数,该函数接受派生类的输入
- C++抽象的字节序是中立的吗?
- 在 C++ 中使用另一个头文件中的抽象类
- ATL::CComContainedObject<contained>: C2259 无法实例化抽象类
- C++:从抽象类重写纯虚拟运算符重载
- 让编译器告诉什么确切的纯虚拟方法使结构抽象?
- 位集元素迭代的抽象