我正在编写一个关于libusb的C ++包装器。我希望我的API完全隐藏底层lib的实现:用户甚至不知道我实际上在使用libusb。所以我创建了两个类:Context
和Device
。使用Device
方法从Context
创建open
。
所以:
//--- context.hpp -------------------
class Context final
{
public:
Context();
~Context();
std::unique_ptr<Device> open(uint16_t vid, uint16_t pid);
private:
struct Impl;
std::unique_ptr<Impl> impl;
};
//--- context.cpp -------------------
struct Context::Impl
{
libusb_context* ctx;
};
Context::Context()
: impl(std::make_unique<Impl>())
{ /* ... */ }
//--- device.hpp -------------------
class Device final
{
public:
Device();
~Device();
private:
struct Impl;
std::unique_ptr<Impl> _impl;
};
//--- device.cpp -------------------
struct Device::Impl
{
libusb_device_handle* handle;
}
Device::Device()
: _impl(std::make_unique<Impl>())
{}
现在的问题是:如何实施open
方法?我需要在实施中调用libusb_open_device_with_vid_pid
,该libusb_context*
将Context::Impl
Device::Impl
Device
,并将句柄存储在Context
中。以下是我认为的选项:
Device
的构造函数,它指向libusb_context*
,但是Context::Impl
的ctor无法访问Device
指针来调用该函数; < / LI>
Context
放在标题中,让Device
成为libusb_context*
的朋友:但这听起来很丑陋; native_handle
的构造函数,它带有一个Context
指针:但是我打破了封装,用户不应该看到libusb的详细信息; open
添加了Device
方法,因此我可以做第一个并获得实现指针但导致与第三个相同的问题。libusb_device_handle*
中进行了函数调用,但如何使用Device
初始化definitions
?我不能让anyOf
使用这样的指针来打破封装。答案 0 :(得分:3)
基于friend
的解决方案实际上是最干净的,它是friend
的设计目标。
// device.hpp
class Device final
{
public:
~Device();
private:
Device();
struct Impl;
std::unique_ptr<Impl> impl;
friend class Context;
};
// device.cpp
struct Device::Impl
{
libusb_device_handle* handle;
Impl() : handle(nullptr) {}
}
Device::Device() : impl(new Device::Impl()) {}
std::unique_ptr<Device> Context::open(uint16_t vid, uint16_t pid) {
std::unique_ptr<Device> result(new Device());
result->impl->handle = libusb_open_device_with_vid_pid(vid, pid);
return result;
}
请注意,您实际上可以返回Device
而不是std::unique_ptr<Device>
,并将所有绑定视为值对象,以避免额外的间接级别。
编辑:另一种选择是使用void指针。这将删除朋友声明,代价是引入可能不安全的演员。
// device.hpp
class Device final
{
public:
~Device();
private:
Device(void *handle);
struct Impl;
std::unique_ptr<Impl> impl;
};
// device.cpp
struct Device::Impl
{
libusb_device_handle* handle;
Impl(void *handle)
: handle(static_cast<libusb_device_handle*>(handle)) {}
}
Device::Device(void *handle)
: impl(new Device::Impl(handle)) {}
std::unique_ptr<Device> Context::open(uint16_t vid, uint16_t pid) {
void *handle = libusb_open_device_with_vid_pid(vid, pid);
std::unique_ptr<Device> result(new Device(handle));
return result;
}