Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 我有一个带有不同前缀的相关函数的C库。如何导出C++;类在不重复代码的情况下调用它们?_C++_Oop_Wrapper - Fatal编程技术网

C++ 我有一个带有不同前缀的相关函数的C库。如何导出C++;类在不重复代码的情况下调用它们?

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

我正在使用一个驱动程序库,与不同型号的硬件进行对话。此硬件附带的API对于不同的主要版本具有不同的库,即使许多交互是相同的。例如,如果我正在与4000系列设备通话,我会拨打

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:添加了一个模板选项,该选项可以最大限度地减少复制粘贴。还更正了一些编译器错误。