C++ 我有一个带有不同前缀的相关函数的C库。如何导出C++;类在不重复代码的情况下调用它们?
我正在使用一个驱动程序库,与不同型号的硬件进行对话。此硬件附带的API对于不同的主要版本具有不同的库,即使许多交互是相同的。例如,如果我正在与4000系列设备通话,我会拨打C++ 我有一个带有不同前缀的相关函数的C库。如何导出C++;类在不重复代码的情况下调用它们?,c++,oop,wrapper,C++,Oop,Wrapper,我正在使用一个驱动程序库,与不同型号的硬件进行对话。此硬件附带的API对于不同的主要版本具有不同的库,即使许多交互是相同的。例如,如果我正在与4000系列设备通话,我会拨打 handle_t handle; dv4000_OpenDevice(&handle); dv4000_DoSomething(handle); dv4000_Close(handle); 对于5000系列设备 handle_t handle; dv5000_OpenDevice(&handle); dv5
handle_t handle;
dv4000_OpenDevice(&handle);
dv4000_DoSomething(handle);
dv4000_Close(handle);
对于5000系列设备
handle_t handle;
dv5000_OpenDevice(&handle);
dv5000_DoSomething(handle);
dv5000_Close(handle);
现在我想创建一个Device
基类和Device\u Series4k
和Device\u Series5k
派生类。如何在不复制大部分实现和交换函数调用的情况下实现这一点?请注意,不同系列的API并不完全相同,因此在编译时使用make规则生成具有名称替换的源文件将不起作用(尽管仅使用公共部分这样做可能是可行的)
使用模板或宏的通用C++解决方案是理想的,但是Linux /GCC的MaMeFrase/shell技巧也会起作用。 编辑 在非常有限的环境下工作的一个例子可能是:
class Device{
public:
typedef uint16_t handle_t;
typedef int (*Opener)(handle_t*);
typedef int (*Closer)(handle_t);
Device(Opener open, Closer close) : _handle(0), _close(close)
{ open(&_handle); }
~Device(){ _close(_handle); }
private:
handle_t _handle;
Closer _close;
};
class Device_Series4k : public Device {
public:
Device_Series4k : Device(dv4000_OpenDevice, dv4000_ClosDevice) {}
};
我可以用模板和额外的抽象层做同样的事情。如果我想在调用open后进行错误检查或执行一些常见的初始化/分配类型的操作,这仍然是一个净好处。但是,如果我需要提供20种不同的剂量计签名,它很快就会变得混乱不堪 首先,您几乎肯定需要一个用于RAII的DeviceHandle类:
typedef void (*CloseDeviceFn)(handle_t);
class DeviceHandle {
handle_t val;
CloseDeviceFn close;
public:
explicit DeviceHandle(handle_t v, CloseDeviceFn closeFn) noexcept : val(v), close(closeFn) {}
DeviceHandle(DeviceHandle&& rhs) noexcept : val(rhs.release()) {}
DeviceHandle& operator=(DeviceHandle&& rhs) noexcept {reset(rhs.release()); return *this;}
~DeviceHandle() noexcept {reset(nullptr);}
handle_t get() noexcept {return val;}
void reset(handle_t v) noexcept {if (val != nullptr) close(val); val = v;}
handle_t release() noexcept {handle_t t = val; val = nullptr; return t;}
};
对于不变的API,您的要求很简单:
class Device {
public:
virtual ~Device() {}; //classes with any virtual methods should have a virtual destructor
virtual void DoSomething();
};
class Device_Series4k: public Device {
DeviceHandle handle;
public:
explicit Device_Series4k(handle_t handle_)
: handle_(handle, &dv4000_Close) {
dv4000_OpenDevice(handle.get());
}
void DoSomething() {dv4000_DoSomething(handle.get());}
};
对于更改的API,您可能需要有版本化的基类:
class DeviceApis {
public:
virtual ~Device() {};
virtual int version() = 0;
virtual void DoSomething() = 0;
};
class Device4000Apis: public Device {
public:
virtual void DoNewerConcept();
};
然后设备系列5K
将扩展设备4000API
,您必须这样做:
DeviceApis myDevice = ...
Device4000Apis deviceAtLeast4000 = dynamic_cast<Device4000Apis>(myDevice);
if (deviceAtLeast4000 != nullptr) {
deviceAtLeast4000.doNewerConcept();
} else {
log.d("device doesn't support concept");
}
DeviceApis myDevice=。。。
Device4000API deviceAtLeast4000=动态广播(myDevice);
如果(deviceAtLeast4000!=nullptr){
deviceAtLeast4000.doNewerConcept();
}否则{
log.d(“设备不支持概念”);
}
如果确实要避免复制粘贴,可以使用模板进行伪装:
template<OpenDeviceFn openDeviceFn,
CloseDeviceFn closeDeviceFn,
DoSomethingFn doSomethingFn>
class DeviceImpl: public Device {
DeviceHandle handle;
public:
explicit DeviceImpl(handle_t handle_)
: handle(handle_, closeDeviceFn) {
openDeviceFn(handle.get());
}
void DoSomething() {doSomethingFn(handle.get());}
};
typedef DeviceImpl<dv4000_OpenDevice, dv4000_Close, dv4000_DoSomething> Device_Series4k;
模板
类DeviceImpl:公共设备{
设备手柄;
公众:
显式DeviceImpl(句柄\u t句柄)
:句柄(句柄,关闭设备){
openDeviceFn(handle.get());
}
void DoSomething(){doSomethingFn(handle.get());}
};
typedef DeviceImpl设备系列4K;
如下所示:
或者另一个选项是创建一个
设备类型
类,该类的成员是指向各种方法的指针,每个设备
都有一个指向设备类型
的指针,这将允许您在不实际使用模板的情况下对模板选项进行排序,从而使实现不受标题影响,但也会干扰内衬。您的两个示例都是针对4000
系列设备…TBH,如果API
s不同,那么很难想象您如何在下一版本发布时,在不发生崩溃风险的情况下,对这一点保持通用性。特别是当操作顺序意外改变时。简短的回答是:你不能。C++不这样工作。你可能能够用外部脚本来拼凑一些东西,这些脚本将为你演示Rabo生成的C++代码,并让编译器吞下它。@ Galik:你对未来版本的改变是正确的,但我相信有些事情不会改变,比如打开和关闭总是我需要做的第一个调用。只要能够包装这些调用并使用适当的RAII携带句柄,这个库就会对我更有用(我可以将一个设备粘贴到共享的ptr中,而不必担心清理),但即使这一点也不直接,也许我遗漏了一些东西,但在您的第一个代码块中,设备系列5K不是用dv5000替换所有dv4000的设备系列4K的完整复制/粘贴吗?我想,您可以通过模板使用较少的复制粘贴来完成,但其他情况下,可以。@greatemu:添加了一个模板选项,该选项可以最大限度地减少复制粘贴。还更正了一些编译器错误。