dimanche 29 octobre 2017

C++11 and later: shared_ptr for managing system resources provided by a low-level C library

For the last couple years I've predominantly been a C developer with a bit of Python coding here and there. With the amount of contradictory sources I'm struggling to figure out the correct approach to managing system resources in modern C++.

I'm the author of libgpiod - a library for controlling GPIOs via the character device (which is the new way of managing GPIOs from userspace, as opposed to now deprecated sysfs interface). The core library code is written in C but I plan to provide bindings for other languages starting with C++11.

I don't want to get into much detail on what GPIOs are but in general a GPIO line is a configurable pin we can control associated with a GPIO chip which usually exposes several lines. The C library models this two-level hierarchy with two core structures: struct gpiod_chip and struct gpiod_line. Both are only visible to users as opaque pointers manipulated with provided API functions. Internally, the chip is associated with an open file descriptor (the device file residing in /dev/) and a couple variables containing the object state.

A pointer to an allocated chip object is returned to the user from one of the gpiod_chip_open() variants. The user is responsible for freeing the allocated resources with gpiod_chip_close(). As with most low-level C code, the user is tasked with managing the resource handle. The library is explicitly made thread-aware (in that there's no global state) but not thread-safe.

The chip manages the resources for all line objects associated with it, so my question below is only relevant for chips.

Now from what I've read so far, in modern C++ the new and delete operators should not generally be used manually. My initial idea for a chip class was thus:

namespace gpiod {

class chip {

    // [snip!]

private:

    std::shared_ptr<::gpiod_chip> _m_chip;

};

}

Which would make the chip class hold a reference to the gpiod_chip object. Copy & move constructors and the assignment operator would simply make use of the shared_ptr's reference counting to allow to freely move and copy the chip object around. When the reference count drops to 0, a custom deleter would call gpiod_chip_close() on the C object.

But then I noticed that some people recommend using a factory in such case and letting the users wrap the object in a smart pointer of their choice themselves.

Any advice on what the correct approach for my use case would be as far as modern C++ goes?

Aucun commentaire:

Enregistrer un commentaire