Re: [libgpiod][PATCH v3 3/3] bindings: cxx: implement C++ bindings for libgpiod v2.0

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Jul 30, 2021 at 10:41:49AM +0200, Bartosz Golaszewski wrote:
> On Fri, Jul 30, 2021 at 8:42 AM Kent Gibson <warthog618@xxxxxxxxx> wrote:
> >
> > On Tue, Jul 27, 2021 at 04:34:00PM +0200, Bartosz Golaszewski wrote:
> > > This is the bulk of work implementing C++ bindings for the new libgpiod
> > > API. The tests are not converted yet but the examples are fully
> > > functional. More details in the cover letter as this patch will be
> > > squashed with the one for the core C library anyway.
> > >
> >
> > My apologies for not getting around to reviewing v2, so here is a
> > quick(??) review of v3...
> >
> > I don't have any problems with the C API changes in patches 1 and 2.
> > I would prefer that we didn't need the num_lines, offsets and capacity
> > getters, but their addition is a reasonable compromise.
> >
> > <snip>
> >
> > > -GPIOD_CXX_API bool is_gpiochip_device(const ::std::string& path)
> > > +struct chip::impl
> > >  {
> > > -     return ::gpiod_is_gpiochip_device(path.c_str());
> > > -}
> > > +     impl(const ::std::string& path)
> > > +             : chip(open_chip(path)),
> > > +               name(::gpiod_chip_get_name(this->chip.get())),
> > > +               label(::gpiod_chip_get_label(this->chip.get()))
> > > +     {
> > > +
> > > +     }
> > > +
> > > +     impl(const impl& other) = delete;
> > > +     impl(impl&& other) = delete;
> > > +     impl& operator=(const impl& other) = delete;
> > > +     impl& operator=(impl&& other) = delete;
> > > +
> > > +     void throw_if_closed(void) const
> > > +     {
> > > +             if (!this->chip)
> > > +                     throw ::std::logic_error("GPIO chip has been closed");
> > > +     }
> > > +
> >
> > Perhaps throw something more specific than a logic_error?
> >
> 
> There's no standard exception that would inherit from std::logic_error
> that would fit here. I can define new exceptions (gpiod::chip_closed
> and gpiod::request_released) inheriting from logic_error if that works
> for you.
> 

Exactly what I had in mind.

> > <snip>
> >
> > > diff --git a/bindings/cxx/edge-event.cpp b/bindings/cxx/edge-event.cpp
> > > new file mode 100644
> > > index 0000000..d6dce22
> > > --- /dev/null
> > > +++ b/bindings/cxx/edge-event.cpp
> > > @@ -0,0 +1,123 @@
> > > +// SPDX-License-Identifier: LGPL-3.0-or-later
> > > +// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@xxxxxxxx>
> > > +
> > > +#include <map>
> > > +#include <stdexcept>
> > > +#include <system_error>
> > > +#include <utility>
> > > +
> > > +#include "internal.hpp"
> > > +
> > > +namespace gpiod {
> > > +
> > > +namespace {
> > > +
> > > +const ::std::map<int, edge_event::type> event_type_mapping = {
> > > +     { GPIOD_LINE_EVENT_RISING_EDGE,         edge_event::type::RISING_EDGE },
> > > +     { GPIOD_LINE_EVENT_FALLING_EDGE,        edge_event::type::FALLING_EDGE }
> > > +};
> > > +
> >
> > Use of a map for just two event types seems like overkill.
> > Wouldn't an if-else in edge_event::get_type(void) be simpler and cleaner?
> > Also, as written, edge_event::get_type(void) can throw std::out_of_range
> > which doesn't seem right. Wouldn't std::range_error be more appropriate?
> > Or adding an unknown event type for failed mappings, so get_type() can
> > never throw??
> >
> 
> While it's true that we can throw the range_error, it's very unlikely
> and would occur only if a serious programming error was present in the
> C library or the kernel.

No - you will currently throw ::std::out_of_range, which is weird.
I'd be ok with ::std::range_error, but that would involve a catch
and re-throw.

And it could also happen if the kernel adds new event types that this
version of the library is unaware of.
While that isn't terribly likely, don't box yourself in.

> I'm fine with this possibility - adding an
> unknown type, would suggest this is a normal situation and not an
> exception.
> 

Fair enough.
That suggestion was coming from "a simple getter shouldn't throw" PoV,
btw.

> For the mapping: to me this looks more consistent and also allows to
> detect the rare and unlikely error mentioned above. In any case - this
> is an implementation detail and can be changed later.
> 
> > Same applies for mappings throughout.  If the C++ values match the C
> > values then you could just perform a range check and cast for the cases
> > where there are more than two values.
> >
> 
> Casting in general is to be avoided in C++ - as defined in the
> document Jack linked recently. Mapping isn't expensive and looks more
> robust IMO. I would prefer to leave it like this.
> 

If casting is so frowned upon then why have they defined so many types
of casts?  I thought the trick was to pick the right one ;-).
And we are only talking about casting between int enums here, so
really...

> > <snip>
> >
> > > +     /**
> > > +      * @brief Retrieve the current snapshot of line information for a
> > > +      *        single line.
> > > +      * @param offset Offset of the line to get the info for.
> > > +      * @param watch Indicates whether the caller wants to watch for line
> > > +      *              info changes.
> > > +      * @return New ::gpiod::line_info object.
> > > +      */
> > > +     line_info get_line_info(unsigned int offset, bool watch = false) const;
> > > +
> > > +     /**
> > > +      * @brief Wrapper around ::gpiod::chip::get_line_info that retrieves
> > > +      *        the line info and starts watching the line for changes.
> > > +      * @param offset Offset of the line to get the info for.
> > > +      * @return New ::gpiod::line_info object.
> > > +      */
> > > +     line_info watch_line_info(unsigned int offset) const;
> > > +
> >
> > I repeat my objection to get_line_info() accepting a watch parameter and
> > watch_line_info() wrapping it.  While they both happen to return
> > line_info, the purpose of the two methods are quite different and so
> > should be kept separate.
> >
> 
> OK, I will change it.
> 
> > Also, should be watch_line_info() really be allowed on a const chip?
> > While it does not alter the C++ object, and so technically can be allowed
> > on a const, it does alter the state of the underlying C object and so I
> > would argue it should not be allowed.
> >
> 
> Makes sense.
> 
> > <snip>
> >
> > > +     /**
> > > +      * @brief Get the constant reference to the edge event at given index.
> > > +      * @param index Index of the event in the buffer.
> > > +      * @return Constant reference to the edge event.
> > > +      */
> > > +     const edge_event& get_event(unsigned int index) const;
> > > +
> > > +     /**
> > > +      * @brief Get the number of edge events currently stored in the buffer.
> > > +      * @return Number of edge events in the buffer.
> > > +      */
> > > +     unsigned int num_events(void) const;
> > > +
> >
> > A getter not prefixed with "get_" ;-).
> > (note that I'm kidding - I prefer it this way - more on that later in
> > class line_info)
> >
> > > +     /**
> > > +      * @brief Maximum capacity of the buffer.
> > > +      * @return Buffer capacity.
> > > +      */
> > > +     unsigned int capacity(void) const noexcept;
> > > +
> >
> > And again.
> >
> > <snip>
> >
> > > +/**
> > > + * @brief Contains a set of line config options used in line requests and
> > > + *        reconfiguration.
> > > + */
> > > +class line_config
> > > +{
> > > +public:
> > > +
> >
> > This class has setters but no getters.
> > There is no use case for getters?
> > Also applies to C API - just wondering given the addition of the getters
> > in patch 1 and 2 - there may be cases where the user would like to read
> > back the config?
> >
> 
> IMO if a use-case like this appears, we can extend the interface. The
> reason for adding the new functions was because we added the all-lines
> getters and now the user would need to store the offsets outside the
> request if we didn't provide them. Adding getters for config
> structures now would unnecessarily bloat the interface. Let me know if
> you disagree though. I usually eventually get convinced by you. :)
> 

Agreed keep the interface minimal and easy enough to add later if the need
arises.  Just throwing it out there in case there are use cases I've
overlooked.

> > > +     /**
> > > +      * @brief Direction settings.
> > > +      */
> > > +     enum class direction : unsigned int
> > > +     {
> > > +             AS_IS = 1,
> > > +             /**< Request the line(s), but don't change current direction. */
> > > +             INPUT,
> > > +             /**< Request the line(s) for reading the GPIO line state. */
> > > +             OUTPUT
> > > +             /**< Request the line(s) for setting the GPIO line state. */
> > > +     };
> > > +
> >
> > line_info and line_config should use common direction types (also
> > applies to C API)? Perhaps in a ::gpiod::line namespace?
> >
> 
> So that would seem obvious to do this but the values stored in line
> info and passed to line config are not always the same. BIAS_UNKNOWN
> and BIAS_AS_IS are not the same and the former is only suitable for
> line_info while the latter only works for the config.

Sure they are distinct - so give them different values in the C API.
line_info will never return BIAS_AS_IS, and line_config will reject
setting BIAS_UNKNOWN.  So document that.

On a related note, the C API should have a GPIOD_LINE_CONFIG_BIAS_AS_IS,
and that should be the default - as it is in the kernel.
GPIOD_LINE_CONFIG_BIAS_DISABLED is actively disabling bias, which is a
different thing.
Or are you intending for libgpiod to always actively specify bias?

> I agree though
> that it may be a bit confusing for users. If we'd change it - where
> should these go wrt doxygen sections of the core API?
> 

You would think that doxygen should be subservient to the code, not the
other way around :-|.

Given I'm suggesting the GPIOD_LINE_CONFIG_xxx enums get folded into
the GPIOD_LINE_xxx enums, they would end up wherever those are?

Cheers,
Kent.




[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux