I’m writing for practice a C++ wrapper over libusb. I want my API to fully hide the implementation of the underlying lib: the user should not even know I’m actually using libusb. So I created two classes: Context and Device. A Device is created from a Context using the open method.
So:
//--- 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>())
{}
Now the question is: how do I implement the open method? I need to call libusb_open_device_with_vid_pid in the implementation which will take the libusb_context* of my Context::Impl and store the handle in Device::Impl. Here are options I thought:
- I create a constructor of
Devicetaking a pointer toContext, but the ctor ofDevicedoes not have access to thelibusb_context*pointer to call the function; - I follow point number one and put the
Context::Implin a header and makeDevicea friend ofContext: this sounds ugly though; - I create a constructor of
Devicethat takes alibusb_context*pointer: but I’d be breaking the encapsulation, the user should’nt see the libusb details; - I add a
native_handlemethod toContextso I can do number one and get the implementation pointer but causing the same problem as number three. - I make the function call in
openbut how do I initialize myDevicewith thelibusb_device_handle*I get? I can’t have a ctor ofDevicetaking such a pointer, breaking the encapsulation.
Device? I'm not familiar withlibusbbut often context type objects are aChandle to maintain instance information. InC++that same job is often done internal to the class - the class object is its own handle.