bindings: implement C++ bindings
authorBartosz Golaszewski <bartekgola@gmail.com>
Wed, 25 Oct 2017 19:11:37 +0000 (21:11 +0200)
committerBartosz Golaszewski <bartekgola@gmail.com>
Mon, 19 Feb 2018 08:45:45 +0000 (09:45 +0100)
Wrap the C API in a set of C++ classes which allow to use all libgpiod
functionalities in an object-oriented manner.

Include simplified reimplementations of gpio-tools and a set of
tests/examples showing how to use the C++ interface.

The C++ API is fully documented in doxygen.

Signed-off-by: Bartosz Golaszewski <bartekgola@gmail.com>
18 files changed:
.gitignore
Makefile.am
bindings/Makefile.am [new file with mode: 0644]
bindings/cxx/Makefile.am [new file with mode: 0644]
bindings/cxx/chip.cpp [new file with mode: 0644]
bindings/cxx/examples/Makefile.am [new file with mode: 0644]
bindings/cxx/examples/gpio_cxx_tests.cpp [new file with mode: 0644]
bindings/cxx/examples/gpiodetectcxx.cpp [new file with mode: 0644]
bindings/cxx/examples/gpiofindcxx.cpp [new file with mode: 0644]
bindings/cxx/examples/gpiogetcxx.cpp [new file with mode: 0644]
bindings/cxx/examples/gpioinfocxx.cpp [new file with mode: 0644]
bindings/cxx/examples/gpiomoncxx.cpp [new file with mode: 0644]
bindings/cxx/examples/gpiosetcxx.cpp [new file with mode: 0644]
bindings/cxx/gpiod.hpp [new file with mode: 0644]
bindings/cxx/iter.cpp [new file with mode: 0644]
bindings/cxx/line.cpp [new file with mode: 0644]
bindings/cxx/line_bulk.cpp [new file with mode: 0644]
configure.ac

index ccd5bbe67637b41aa2c2c2b392f283f93c80ac24..49c256f8e89842bc5d8031a877bc795fa0836fc6 100644 (file)
@@ -1,5 +1,6 @@
 !.gitignore
 libgpiod.la
+libgpiodcxx.la
 libtools-common.la
 gpiodetect
 gpioinfo
@@ -7,6 +8,13 @@ gpioget
 gpioset
 gpiomon
 gpiofind
+gpio_cxx_tests
+gpiodetectcxx
+gpiofindcxx
+gpiogetcxx
+gpioinfocxx
+gpiomoncxx
+gpiosetcxx
 *.o
 *.lo
 doc
index 9b1d00bba4e576dfdb6adb22b5d31b2f97a35c7b..974e10f9b4218b2dae3cb6b9d5f7e9234c4275c6 100644 (file)
@@ -9,7 +9,7 @@
 
 ACLOCAL_AMFLAGS = -I m4
 AUTOMAKE_OPTIONS = foreign
-SUBDIRS = include src
+SUBDIRS = include src bindings
 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libgpiod.pc
@@ -23,7 +23,9 @@ endif
 if HAS_DOXYGEN
 
 doc:
-       @(cat Doxyfile; echo PROJECT_NUMBER = $(VERSION_STR)) | doxygen -
+       @(cat Doxyfile; \
+               echo PROJECT_NUMBER = $(VERSION_STR); \
+               echo INPUT += bindings/cxx/gpiod.hpp) | doxygen -
 .PHONY: doc
 
 EXTRA_DIST = Doxyfile
diff --git a/bindings/Makefile.am b/bindings/Makefile.am
new file mode 100644 (file)
index 0000000..b398a2d
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or (at
+# your option) any later version.
+#
+
+SUBDIRS = .
+
+if WITH_BINDINGS_CXX
+
+SUBDIRS += cxx
+
+endif
diff --git a/bindings/cxx/Makefile.am b/bindings/cxx/Makefile.am
new file mode 100644 (file)
index 0000000..bbf938f
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or (at
+# your option) any later version.
+#
+
+lib_LTLIBRARIES = libgpiodcxx.la
+libgpiodcxx_la_SOURCES = chip.cpp iter.cpp line.cpp line_bulk.cpp
+libgpiodcxx_la_CPPFLAGS = -Wall -Wextra -g -std=gnu++11
+libgpiodcxx_la_CPPFLAGS += -fvisibility=hidden -I$(top_srcdir)/include/
+libgpiodcxx_la_LDFLAGS = -version-number $(subst .,:,$(PACKAGE_VERSION))
+libgpiodcxx_la_LDFLAGS += -lgpiod -L$(top_builddir)/src/lib
+
+include_HEADERS = gpiod.hpp
+
+SUBDIRS = . examples
diff --git a/bindings/cxx/chip.cpp b/bindings/cxx/chip.cpp
new file mode 100644 (file)
index 0000000..5c626a9
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <gpiod.hpp>
+
+#include <system_error>
+#include <functional>
+#include <utility>
+#include <map>
+
+namespace gpiod {
+
+namespace {
+
+::gpiod_chip* open_lookup(const ::std::string& device)
+{
+       return ::gpiod_chip_open_lookup(device.c_str());
+}
+
+::gpiod_chip* open_by_path(const ::std::string& device)
+{
+       return ::gpiod_chip_open(device.c_str());
+}
+
+::gpiod_chip* open_by_name(const ::std::string& device)
+{
+       return ::gpiod_chip_open_by_name(device.c_str());
+}
+
+::gpiod_chip* open_by_label(const ::std::string& device)
+{
+       return ::gpiod_chip_open_by_label(device.c_str());
+}
+
+::gpiod_chip* open_by_number(const ::std::string& device)
+{
+       return ::gpiod_chip_open_by_number(::std::stoul(device));
+}
+
+using open_func = ::std::function<::gpiod_chip* (const ::std::string&)>;
+
+const ::std::map<int, open_func> open_funcs = {
+       { chip::OPEN_LOOKUP,    open_lookup,    },
+       { chip::OPEN_BY_PATH,   open_by_path,   },
+       { chip::OPEN_BY_NAME,   open_by_name,   },
+       { chip::OPEN_BY_LABEL,  open_by_label,  },
+       { chip::OPEN_BY_NUMBER, open_by_number, },
+};
+
+void chip_deleter(::gpiod_chip* chip)
+{
+       ::gpiod_chip_close(chip);
+}
+
+} /* namespace */
+
+chip::chip(const ::std::string& device, int how)
+       : _m_chip()
+{
+       this->open(device, how);
+}
+
+chip::chip(::gpiod_chip* chip)
+       : _m_chip(chip, chip_deleter)
+{
+
+}
+
+void chip::open(const ::std::string& device, int how)
+{
+       auto func = open_funcs.at(how);
+
+       ::gpiod_chip *chip = func(device);
+       if (!chip)
+               throw ::std::system_error(errno, ::std::system_category(),
+                                         "cannot open GPIO device " + device);
+
+       this->_m_chip.reset(chip, chip_deleter);
+}
+
+void chip::reset(void) noexcept
+{
+       this->_m_chip.reset();
+}
+
+::std::string chip::name(void) const
+{
+       this->throw_if_noref();
+
+       const char* name = ::gpiod_chip_name(this->_m_chip.get());
+
+       return ::std::move(name ? ::std::string(name) : ::std::string());
+}
+
+::std::string chip::label(void) const
+{
+       this->throw_if_noref();
+
+       const char* label = ::gpiod_chip_label(this->_m_chip.get());
+
+       return ::std::move(label ? ::std::string(label) : ::std::string());
+}
+
+unsigned int chip::num_lines(void) const
+{
+       this->throw_if_noref();
+
+       return ::gpiod_chip_num_lines(this->_m_chip.get());
+}
+
+line chip::get_line(unsigned int offset) const
+{
+       this->throw_if_noref();
+
+       if (offset >= this->num_lines())
+               throw ::std::out_of_range("line offset greater than the number of lines");
+
+       ::gpiod_line* line_handle = ::gpiod_chip_get_line(this->_m_chip.get(), offset);
+       if (!line_handle)
+               throw ::std::system_error(errno, ::std::system_category(),
+                                         "error getting GPIO line from chip");
+
+       return ::std::move(line(line_handle, *this));
+}
+
+line chip::find_line(const ::std::string& name) const
+{
+       this->throw_if_noref();
+
+       ::gpiod_line* handle = ::gpiod_chip_find_line(this->_m_chip.get(), name.c_str());
+       if (!handle && errno != ENOENT)
+               throw ::std::system_error(errno, ::std::system_category(),
+                                         "error looking up GPIO line by name");
+
+       return ::std::move(handle ? line(handle, *this) : line());
+}
+
+line_bulk chip::get_lines(const ::std::vector<unsigned int>& offsets) const
+{
+       line_bulk lines;
+
+       for (auto& it: offsets)
+               lines.add(this->get_line(it));
+
+       return ::std::move(lines);
+}
+
+line_bulk chip::find_lines(const ::std::vector<::std::string>& names) const
+{
+       line_bulk lines;
+
+       for (auto& it: names)
+               lines.add(this->find_line(it));
+
+       return ::std::move(lines);
+}
+
+bool chip::operator==(const chip& rhs) const noexcept
+{
+       return this->_m_chip.get() == rhs._m_chip.get();
+}
+
+bool chip::operator!=(const chip& rhs) const noexcept
+{
+       return this->_m_chip.get() != rhs._m_chip.get();
+}
+
+chip::operator bool(void) const noexcept
+{
+       return this->_m_chip.get() != nullptr;
+}
+
+bool chip::operator!(void) const noexcept
+{
+       return this->_m_chip.get() == nullptr;
+}
+
+void chip::throw_if_noref(void) const
+{
+       if (!this->_m_chip.get())
+               throw ::std::logic_error("object not associated with an open GPIO chip");
+}
+
+} /* namespace gpiod */
diff --git a/bindings/cxx/examples/Makefile.am b/bindings/cxx/examples/Makefile.am
new file mode 100644 (file)
index 0000000..4f5ce83
--- /dev/null
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or (at
+# your option) any later version.
+#
+
+AM_CPPFLAGS = -I$(top_srcdir)/bindings/cxx/ -I$(top_srcdir)/include
+AM_CPPFLAGS += -Wall -Wextra -g -std=gnu++11
+AM_LDFLAGS = -lgpiodcxx -L$(top_builddir)/bindings/cxx/
+
+check_PROGRAMS =       gpio_cxx_tests \
+                       gpiodetectcxx \
+                       gpiofindcxx \
+                       gpiogetcxx \
+                       gpioinfocxx \
+                       gpiomoncxx \
+                       gpiosetcxx
+
+gpio_cxx_tests_SOURCES = gpio_cxx_tests.cpp
+
+gpiodetectcxx_SOURCES = gpiodetectcxx.cpp
+
+gpiofindcxx_SOURCES = gpiofindcxx.cpp
+
+gpiogetcxx_SOURCES = gpiogetcxx.cpp
+
+gpioinfocxx_SOURCES = gpioinfocxx.cpp
+
+gpiomoncxx_SOURCES = gpiomoncxx.cpp
+
+gpiosetcxx_SOURCES = gpiosetcxx.cpp
diff --git a/bindings/cxx/examples/gpio_cxx_tests.cpp b/bindings/cxx/examples/gpio_cxx_tests.cpp
new file mode 100644 (file)
index 0000000..b6de15c
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* Misc tests/examples of the C++ API. */
+
+#include <gpiod.hpp>
+
+#include <stdexcept>
+#include <cstdlib>
+#include <iostream>
+
+namespace {
+
+using test_func = ::std::function<void (void)>;
+
+::std::vector<::std::pair<::std::string, test_func>> test_funcs;
+
+struct test_case
+{
+       test_case(const ::std::string& name, test_func& func)
+       {
+               test_funcs.push_back(::std::make_pair(name, func));
+       }
+};
+
+#define TEST_CASE(_func)                                               \
+       test_func _test_func_##_func(_func);                            \
+       test_case _test_case_##_func(#_func, _test_func_##_func)
+
+void chip_info(void)
+{
+       ::gpiod::chip chip("gpiochip0");
+
+       ::std::cout << "chip name: " << chip.name() << ::std::endl;
+       ::std::cout << "chip label: " << chip.label() << ::std::endl;
+       ::std::cout << "number of lines " << chip.num_lines() << ::std::endl;
+}
+TEST_CASE(chip_info);
+
+void chip_open_different_modes(void)
+{
+       ::gpiod::chip by_name("gpiochip0", ::gpiod::chip::OPEN_BY_NAME);
+       ::gpiod::chip by_path("/dev/gpiochip0", ::gpiod::chip::OPEN_BY_PATH);
+       ::gpiod::chip by_label("gpio-mockup-A", ::gpiod::chip::OPEN_BY_LABEL);
+       ::gpiod::chip by_number("0", ::gpiod::chip::OPEN_BY_NUMBER);
+}
+TEST_CASE(chip_open_different_modes);
+
+void chip_line_ops(void)
+{
+       ::gpiod::chip chip("gpiochip0");
+
+       ::gpiod::line line = chip.get_line(3);
+       ::std::cout << "Got line by offset: " << line.offset() << ::std::endl;
+
+       line = chip.find_line("gpio-mockup-A-4");
+       ::std::cout << "Got line by name: " << line.name() << ::std::endl;
+
+       ::gpiod::line_bulk lines = chip.get_lines({ 1, 2, 3, 6, 6 });
+       ::std::cout << "Got multiple lines by offset: ";
+       for (auto& it: lines)
+               ::std::cout << it.offset() << " ";
+       ::std::cout << ::std::endl;
+
+       lines = chip.find_lines({ "gpio-mockup-A-1", "gpio-mockup-A-4", "gpio-mockup-A-7" });
+       ::std::cout << "Got multiple lines by name: ";
+       for (auto& it: lines)
+               ::std::cout << it.name() << " ";
+       ::std::cout << ::std::endl;
+}
+TEST_CASE(chip_line_ops);
+
+void line_info(void)
+{
+       ::gpiod::chip chip("gpiochip0");
+       ::gpiod::line line = chip.get_line(2);
+
+       ::std::cout << "line offset: " << line.offset() << ::std::endl;
+       ::std::cout << "line name: " << line.name() << ::std::endl;
+       ::std::cout << "line direction: "
+                   << (line.direction() == ::gpiod::line::DIRECTION_INPUT ?
+                       "input" : "output") << ::std::endl;
+}
+TEST_CASE(line_info);
+
+void empty_objects(void)
+{
+       ::std::cout << "Are initialized line & chip objects 'false'?" << ::std::endl;
+
+       ::gpiod::line line;
+       if (line)
+               throw ::std::logic_error("line built with a default constructor should be 'false'");
+
+       ::gpiod::chip chip;
+       if (chip)
+               throw ::std::logic_error("chip built with a default constructor should be 'false'");
+
+       ::std::cout << "YES" << ::std::endl;
+}
+TEST_CASE(empty_objects);
+
+void line_bulk_iterator(void)
+{
+       ::std::cout << "Checking line_bulk iterators" << ::std::endl;
+
+       ::gpiod::chip chip("gpiochip0");
+       ::gpiod::line_bulk bulk = chip.get_lines({ 0, 1, 2, 3, 4 });
+
+       for (auto& it: bulk)
+               ::std::cout << it.name() << ::std::endl;
+
+       ::std::cout << "DONE" << ::std::endl;
+}
+TEST_CASE(line_bulk_iterator);
+
+void single_line_test(void)
+{
+       const ::std::string line_name("gpio-mockup-A-4");
+
+       ::std::cout << "Looking up a GPIO line by name (" << line_name << ")" << ::std::endl;
+
+       ::gpiod::line line = ::gpiod::find_line(line_name);
+       if (!line)
+               throw ::std::runtime_error(line_name + " line not found");
+
+       ::std::cout << "Requesting a single line" << ::std::endl;
+
+       ::gpiod::line_request conf;
+       conf.consumer = "gpiod_cxx_tests";
+       conf.request_type = ::gpiod::line_request::DIRECTION_OUTPUT;
+
+       line.request(conf, 1);
+       ::std::cout << "Reading value" << ::std::endl;
+       ::std::cout << line.get_value() << ::std::endl;
+       ::std::cout << "Changing value to 0" << ::std::endl;
+       line.set_value(0);
+       ::std::cout << "Reading value again" << ::std::endl;
+       ::std::cout << line.get_value() << ::std::endl;
+}
+TEST_CASE(single_line_test);
+
+} /* namespace */
+
+int main(int, char **)
+{
+       for (auto& it: test_funcs) {
+               ::std::cout << "=================================================" << ::std::endl;
+               ::std::cout << it.first << ":\n" << ::std::endl;
+               it.second();
+       }
+
+       return EXIT_SUCCESS;
+}
diff --git a/bindings/cxx/examples/gpiodetectcxx.cpp b/bindings/cxx/examples/gpiodetectcxx.cpp
new file mode 100644 (file)
index 0000000..257a84c
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* C++ reimplementation of the gpiodetect tool. */
+
+#include <gpiod.hpp>
+
+#include <cstdlib>
+#include <iostream>
+
+int main(int argc, char **argv)
+{
+       if (argc != 1) {
+               ::std::cerr << "usage: " << argv[0] << ::std::endl;
+               return EXIT_FAILURE;
+       }
+
+       for (auto& it: ::gpiod::make_chip_iterator()) {
+               ::std::cout << it.name() << " ["
+                         << it.label() << "] ("
+                         << it.num_lines() << " lines)" << ::std::endl;
+       }
+
+       return EXIT_SUCCESS;
+}
diff --git a/bindings/cxx/examples/gpiofindcxx.cpp b/bindings/cxx/examples/gpiofindcxx.cpp
new file mode 100644 (file)
index 0000000..382032b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* C++ reimplementation of the gpiofind tool. */
+
+#include <gpiod.hpp>
+
+#include <cstdlib>
+#include <iostream>
+
+int main(int argc, char **argv)
+{
+       if (argc != 2) {
+               ::std::cerr << "usage: " << argv[0] << " <line name>" << ::std::endl;
+               return EXIT_FAILURE;
+       }
+
+       ::gpiod::line line = ::gpiod::find_line(argv[1]);
+       if (!line)
+               return EXIT_FAILURE;
+
+       ::std::cout << line.get_chip().name() << " " << line.offset() << ::std::endl;
+
+       return EXIT_SUCCESS;
+}
diff --git a/bindings/cxx/examples/gpiogetcxx.cpp b/bindings/cxx/examples/gpiogetcxx.cpp
new file mode 100644 (file)
index 0000000..fb372f7
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* Simplified C++ reimplementation of the gpioget tool. */
+
+#include <gpiod.hpp>
+
+#include <cstdlib>
+#include <iostream>
+
+int main(int argc, char **argv)
+{
+       if (argc < 3) {
+               ::std::cerr << "usage: " << argv[0] << " <chip> <line_offset0> ..." << ::std::endl;
+               return EXIT_FAILURE;
+       }
+
+       ::std::vector<unsigned int> offsets;
+
+       for (int i = 2; i < argc; i++)
+               offsets.push_back(::std::stoul(argv[i]));
+
+       ::gpiod::chip chip(argv[1]);
+       auto lines = chip.get_lines(offsets);
+
+       lines.request({
+               argv[0],
+               ::gpiod::line_request::DIRECTION_INPUT,
+               0
+       });
+
+       auto vals = lines.get_values();
+
+       for (auto& it: vals)
+               ::std::cout << it << ' ';
+       ::std::cout << ::std::endl;
+
+       return EXIT_SUCCESS;
+}
diff --git a/bindings/cxx/examples/gpioinfocxx.cpp b/bindings/cxx/examples/gpioinfocxx.cpp
new file mode 100644 (file)
index 0000000..1559c6b
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* Simplified C++ reimplementation of the gpioinfo tool. */
+
+#include <gpiod.hpp>
+
+#include <cstdlib>
+#include <iostream>
+
+int main(int argc, char **argv)
+{
+       if (argc != 1) {
+               ::std::cerr << "usage: " << argv[0] << ::std::endl;
+               return EXIT_FAILURE;
+       }
+
+       for (auto& cit: ::gpiod::make_chip_iterator()) {
+               ::std::cout << cit.name() << " - " << cit.num_lines() << " lines:" << ::std::endl;
+
+               for (auto& lit: ::gpiod::line_iterator(cit)) {
+                       ::std::cout << "\tline ";
+                       ::std::cout.width(3);
+                       ::std::cout << lit.offset() << ": ";
+
+                       ::std::cout.width(12);
+                       ::std::cout << (lit.name().empty() ? "unnamed" : lit.name());
+                       ::std::cout << " ";
+
+                       ::std::cout.width(12);
+                       ::std::cout << (lit.consumer().empty() ? "unused" : lit.consumer());
+                       ::std::cout << " ";
+
+                       ::std::cout.width(8);
+                       ::std::cout << (lit.direction() == ::gpiod::line::DIRECTION_INPUT ? "input" : "output");
+                       ::std::cout << " ";
+
+                       ::std::cout.width(10);
+                       ::std::cout << (lit.active_state() == ::gpiod::line::ACTIVE_LOW
+                                                               ? "active-low" : "active-high");
+
+                       ::std::cout << ::std::endl;
+               }
+       }
+
+       return EXIT_SUCCESS;
+}
diff --git a/bindings/cxx/examples/gpiomoncxx.cpp b/bindings/cxx/examples/gpiomoncxx.cpp
new file mode 100644 (file)
index 0000000..640aba8
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* Simplified C++ reimplementation of the gpiomon tool. */
+
+#include <gpiod.hpp>
+
+#include <cstdlib>
+#include <iostream>
+
+namespace {
+
+void print_event(const ::gpiod::line_event& event)
+{
+       if (event.event_type == ::gpiod::line_event::RISING_EDGE)
+               ::std::cout << " RISING EDGE";
+       else if (event.event_type == ::gpiod::line_event::FALLING_EDGE)
+               ::std::cout << "FALLING EDGE";
+       else
+               throw ::std::logic_error("invalid event type");
+
+       ::std::cout << " ";
+
+       ::std::cout << ::std::chrono::duration_cast<::std::chrono::seconds>(event.timestamp).count();
+       ::std::cout << ".";
+       ::std::cout << event.timestamp.count() % 1000000000;
+
+       ::std::cout << " line: " << event.source.offset();
+
+       ::std::cout << ::std::endl;
+}
+
+} /* namespace */
+
+int main(int argc, char **argv)
+{
+       if (argc < 3) {
+               ::std::cout << "usage: " << argv[0] << " <chip> <offset0> ..." << ::std::endl;
+               return EXIT_FAILURE;
+       }
+
+       ::std::vector<unsigned int> offsets;
+       offsets.reserve(argc);
+       for (int i = 2; i < argc; i++)
+               offsets.push_back(::std::stoul(argv[i]));
+
+       ::gpiod::chip chip(argv[1]);
+       auto lines = chip.get_lines(offsets);
+
+       lines.request({
+               argv[0],
+               ::gpiod::line_request::EVENT_BOTH_EDGES,
+               0,
+       });
+
+       for (;;) {
+               auto events = lines.event_wait(::std::chrono::nanoseconds(1000000000));
+               if (events) {
+                       for (auto& it: events)
+                               print_event(it.event_read());
+               }
+       }
+
+       return EXIT_SUCCESS;
+}
diff --git a/bindings/cxx/examples/gpiosetcxx.cpp b/bindings/cxx/examples/gpiosetcxx.cpp
new file mode 100644 (file)
index 0000000..d563629
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* Simplified C++ reimplementation of the gpioset tool. */
+
+#include <gpiod.hpp>
+
+#include <cstdlib>
+#include <iostream>
+
+int main(int argc, char **argv)
+{
+       if (argc < 3) {
+               ::std::cerr << "usage: " << argv[0] << " <chip> <line_offset0>=<value0> ..." << ::std::endl;
+               return EXIT_FAILURE;
+       }
+
+       ::std::vector<unsigned int> offsets;
+       ::std::vector<int> values;
+
+       for (int i = 2; i < argc; i++) {
+               ::std::string arg(argv[i]);
+
+               size_t pos = arg.find('=');
+
+               ::std::string offset(arg.substr(0, pos));
+               ::std::string value(arg.substr(pos + 1, ::std::string::npos));
+
+               if (offset.empty() || value.empty())
+                       throw ::std::invalid_argument("invalid argument: " + ::std::string(argv[i]));
+
+               offsets.push_back(::std::stoul(offset));
+               values.push_back(::std::stoul(value));
+       }
+
+       ::gpiod::chip chip(argv[1]);
+       auto lines = chip.get_lines(offsets);
+
+       lines.request({
+               argv[0],
+               ::gpiod::line_request::DIRECTION_OUTPUT,
+               0
+       }, values);
+
+       ::std::cin.get();
+
+       return EXIT_SUCCESS;
+}
diff --git a/bindings/cxx/gpiod.hpp b/bindings/cxx/gpiod.hpp
new file mode 100644 (file)
index 0000000..da3747a
--- /dev/null
@@ -0,0 +1,959 @@
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef __LIBGPIOD_GPIOD_CXX_HPP__
+#define __LIBGPIOD_GPIOD_CXX_HPP__
+
+#include <gpiod.h>
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <bitset>
+#include <chrono>
+
+namespace gpiod {
+
+class line;
+class line_bulk;
+class line_event;
+class line_iterator;
+class chip_iterator;
+
+/**
+ * @defgroup __gpiod_cxx__ C++ bindings
+ * @{
+ */
+
+/**
+ * @brief Represents a GPIO chip.
+ *
+ * Internally this class holds a smart pointer to an open GPIO chip descriptor.
+ * Multiple objects of this class can reference the same chip. The chip is
+ * closed and all resources freed when the last reference is dropped.
+ */
+class chip
+{
+public:
+
+       /**
+        * @brief Default constructor. Creates an empty GPIO chip object.
+        */
+       GPIOD_API chip(void) = default;
+
+       /**
+        * @brief Constructor. Opens the chip using chip::open.
+        * @param device String describing the GPIO chip.
+        * @param how Indicates how the chip should be opened.
+        */
+       GPIOD_API chip(const ::std::string& device, int how = OPEN_LOOKUP);
+
+       /**
+        * @brief Copy constructor. References the object held by other.
+        * @param other Other chip object.
+        */
+       GPIOD_API chip(const chip& other) = default;
+
+       /**
+        * @brief Move constructor. References the object held by other.
+        * @param other Other chip object.
+        */
+       GPIOD_API chip(chip&& other) = default;
+
+       /**
+        * @brief Assignment operator. References the object held by other.
+        * @param other Other chip object.
+        * @return Reference to this object.
+        */
+       GPIOD_API chip& operator=(const chip& other) = default;
+
+       /**
+        * @brief Move assignment operator. References the object held by other.
+        * @param other Other chip object.
+        * @return Reference to this object.
+        */
+       GPIOD_API chip& operator=(chip&& other) = default;
+
+       /**
+        * @brief Destructor. Unreferences the internal chip object.
+        */
+       GPIOD_API ~chip(void) = default;
+
+       /**
+        * @brief Open a GPIO chip.
+        * @param device String describing the GPIO chip.
+        * @param how Indicates how the chip should be opened.
+        *
+        * If the object already holds a reference to an open chip, it will be
+        * closed and the reference reset.
+        */
+       GPIOD_API void open(const ::std::string &device, int how = OPEN_LOOKUP);
+
+       /**
+        * @brief Reset the internal smart pointer owned by this object.
+        */
+       GPIOD_API void reset(void) noexcept;
+
+       /**
+        * @brief Return the name of the chip held by this object.
+        * @return Name of the GPIO chip.
+        */
+       GPIOD_API ::std::string name(void) const;
+
+       /**
+        * @brief Return the label of the chip held by this object.
+        * @return Label of the GPIO chip.
+        */
+       GPIOD_API ::std::string label(void) const;
+
+       /**
+        * @brief Return the number of lines exposed by this chip.
+        * @return Number of lines.
+        */
+       GPIOD_API unsigned int num_lines(void) const;
+
+       /**
+        * @brief Get the line exposed by this chip at given offset.
+        * @param offset Offset of the line.
+        * @return Line object.
+        */
+       GPIOD_API line get_line(unsigned int offset) const;
+
+       /**
+        * @brief Get the line exposed by this chip by name.
+        * @param name Line name.
+        * @return Line object.
+        */
+       GPIOD_API line find_line(const ::std::string& name) const;
+
+       /**
+        * @brief Get a set of lines exposed by this chip at given offsets.
+        * @param offsets Vector of line offsets.
+        * @return Set of lines held by a line_bulk object.
+        */
+       GPIOD_API line_bulk get_lines(const ::std::vector<unsigned int>& offsets) const;
+
+       /**
+        * @brief Get a set of lines exposed by this chip by their names.
+        * @param names Vector of line names.
+        * @return Set of lines held by a line_bulk object.
+        */
+       GPIOD_API line_bulk find_lines(const ::std::vector<::std::string>& names) const;
+
+       /**
+        * @brief Equality operator.
+        * @param rhs Right-hand side of the equation.
+        * @return True if rhs references the same chip. False otherwise.
+        */
+       GPIOD_API bool operator==(const chip& rhs) const noexcept;
+
+       /**
+        * @brief Inequality operator.
+        * @param rhs Right-hand side of the equation.
+        * @return False if rhs references the same chip. True otherwise.
+        */
+       GPIOD_API bool operator!=(const chip& rhs) const noexcept;
+
+       /**
+        * @brief Check if this object holds a reference to a GPIO chip.
+        * @return True if this object references a GPIO chip, false otherwise.
+        */
+       GPIOD_API operator bool(void) const noexcept;
+
+       /**
+        * @brief Check if this object doesn't hold a reference to a GPIO chip.
+        * @return False if this object references a GPIO chip, true otherwise.
+        */
+       GPIOD_API bool operator!(void) const noexcept;
+
+       /**
+        * @brief Affect the way in which chip::chip and chip::open will try
+        *        to open a GPIO chip character device.
+        */
+       enum : int {
+               OPEN_LOOKUP = 1,
+               /**< Open based on the best guess what the supplied string is. */
+               OPEN_BY_PATH,
+               /**< Assume the string is a path to the GPIO chardev. */
+               OPEN_BY_NAME,
+               /**< Assume the string is the name of the chip */
+               OPEN_BY_LABEL,
+               /**< Assume the string is the label of the GPIO chip. */
+               OPEN_BY_NUMBER,
+               /**< Assume the string is the number of the GPIO chip. */
+       };
+
+private:
+
+       chip(::gpiod_chip* chip);
+
+       void throw_if_noref(void) const;
+
+       ::std::shared_ptr<::gpiod_chip> _m_chip;
+
+       friend chip_iterator;
+       friend line_iterator;
+};
+
+/**
+ * @brief Stores the configuration for line requests.
+ */
+struct line_request
+{
+       /**
+        * @brief Request types.
+        */
+       enum : int {
+               DIRECTION_AS_IS = 1,
+               /**< Request for values, don't change the direction. */
+               DIRECTION_INPUT,
+               /**< Request for reading line values. */
+               DIRECTION_OUTPUT,
+               /**< Request for driving the GPIO lines. */
+               EVENT_FALLING_EDGE,
+               /**< Listen for falling edge events. */
+               EVENT_RISING_EDGE,
+               /**< Listen for rising edge events. */
+               EVENT_BOTH_EDGES,
+               /**< Listen for all types of events. */
+       };
+
+       static const ::std::bitset<32> FLAG_ACTIVE_LOW;
+       /**< Set the active state to 'low' (high is the default). */
+       static const ::std::bitset<32> FLAG_OPEN_SOURCE;
+       /**< The line is an open-source port. */
+       static const ::std::bitset<32> FLAG_OPEN_DRAIN;
+       /**< The line is an open-drain port. */
+
+       ::std::string consumer;
+       /**< Consumer name to pass to the request. */
+       int request_type;
+       /**< Type of the request. */
+       ::std::bitset<32> flags;
+       /**< Additional request flags. */
+};
+
+/**
+ * @brief Represents a single GPIO line.
+ *
+ * Internally this class holds a raw pointer to a GPIO line descriptor and a
+ * reference to the parent chip. All line resources are freed when the last
+ * reference to the parent chip is dropped.
+ */
+class line
+{
+public:
+
+       /**
+        * @brief Default constructor. Creates an empty line object.
+        */
+       GPIOD_API line(void);
+
+       /**
+        * @brief Copy constructor.
+        * @param other Other line object.
+        */
+       GPIOD_API line(const line& other) = default;
+
+       /**
+        * @brief Move constructor.
+        * @param other Other line object.
+        */
+       GPIOD_API line(line&& other) = default;
+
+       /**
+        * @brief Assignment operator.
+        * @param other Other line object.
+        * @return Reference to this object.
+        */
+       GPIOD_API line& operator=(const line& other) = default;
+
+       /**
+        * @brief Move assignment operator.
+        * @param other Other line object.
+        * @return Reference to this object.
+        */
+       GPIOD_API line& operator=(line&& other) = default;
+
+       /**
+        * @brief Destructor.
+        */
+       GPIOD_API ~line(void) = default;
+
+       /**
+        * @brief Get the offset of this line.
+        * @return Offet of this line.
+        */
+       GPIOD_API unsigned int offset(void) const;
+
+       /**
+        * @brief Get the name of this line (if any).
+        * @return Name of this line or an empty string if it is unnamed.
+        */
+       GPIOD_API ::std::string name(void) const;
+
+       /**
+        * @brief Get the consumer of this line (if any).
+        * @return Name of the consumer of this line or an empty string if it
+        *         is unused.
+        */
+       GPIOD_API ::std::string consumer(void) const;
+
+       /**
+        * @brief Get current direction of this line.
+        * @return Current direction setting.
+        */
+       GPIOD_API int direction(void) const noexcept;
+
+       /**
+        * @brief Get current active state of this line.
+        * @return Current active state setting.
+        */
+       GPIOD_API int active_state(void) const noexcept;
+
+       /**
+        * @brief Check if this line is used by the kernel or other user space
+        *        process.
+        * @return True if this line is in use, false otherwise.
+        */
+       GPIOD_API bool is_used(void) const;
+
+       /**
+        * @brief Check if this line represents an open-drain GPIO.
+        * @return True if the line is an open-drain GPIO, false otherwise.
+        */
+       GPIOD_API bool is_open_drain(void) const;
+
+       /**
+        * @brief Check if this line represents an open-source GPIO.
+        * @return True if the line is an open-source GPIO, false otherwise.
+        */
+       GPIOD_API bool is_open_source(void) const;
+
+       /**
+        * @brief Request this line.
+        * @param config Request config (see gpiod::line_request).
+        * @param default_val Default value - only matters for OUTPUT direction.
+        */
+       GPIOD_API void request(const line_request& config, int default_val = 0) const;
+
+       /**
+        * @brief Check if this user has ownership of this line.
+        * @return True if the user has ownership of this line, false otherwise.
+        */
+       GPIOD_API bool is_requested(void) const;
+
+       /**
+        * @brief Read the line value.
+        * @return Current value (0 or 1).
+        */
+       GPIOD_API int get_value(void) const;
+
+       /**
+        * @brief Set the value of this line.
+        * @param val New value (0 or 1).
+        */
+       GPIOD_API void set_value(int val) const;
+
+       /**
+        * @brief Wait for an event on this line.
+        * @param timeout Time to wait before returning if no event occurred.
+        * @return True if an event occurred and can be read, false if the wait
+        *         timed out.
+        */
+       GPIOD_API bool event_wait(const ::std::chrono::nanoseconds& timeout) const;
+
+       /**
+        * @brief Read a line event.
+        * @return Line event object.
+        */
+       GPIOD_API line_event event_read(void) const;
+
+       /**
+        * @brief Get the event file descriptor associated with this line.
+        * @return File descriptor number.
+        */
+       GPIOD_API int event_get_fd(void) const;
+
+       /**
+        * @brief Get the reference to the parent chip.
+        * @return Reference to the parent chip object.
+        */
+       GPIOD_API const chip& get_chip(void) const;
+
+       /**
+        * @brief Reset the state of this object.
+        *
+        * This is useful when the user needs to e.g. keep the line_event object
+        * but wants to drop the reference to the GPIO chip indirectly held by
+        * the line being the source of the event.
+        */
+       GPIOD_API void reset(void);
+
+       /**
+        * @brief Check if two line objects reference the same GPIO line.
+        * @param rhs Right-hand side of the equation.
+        * @return True if both objects reference the same line, fale otherwise.
+        */
+       GPIOD_API bool operator==(const line& rhs) const noexcept;
+
+       /**
+        * @brief Check if two line objects reference different GPIO lines.
+        * @param rhs Right-hand side of the equation.
+        * @return False if both objects reference the same line, true otherwise.
+        */
+       GPIOD_API bool operator!=(const line& rhs) const noexcept;
+
+       /**
+        * @brief Check if this object holds a reference to any GPIO line.
+        * @return True if this object references a GPIO line, false otherwise.
+        */
+       GPIOD_API operator bool(void) const noexcept;
+
+       /**
+        * @brief Check if this object doesn't reference any GPIO line.
+        * @return True if this object doesn't reference any GPIO line, true
+        *         otherwise.
+        */
+       GPIOD_API bool operator!(void) const noexcept;
+
+       /**
+        * @brief Possible direction settings.
+        */
+       enum : int {
+               DIRECTION_INPUT = 1,
+               /**< Line's direction setting is input. */
+               DIRECTION_OUTPUT,
+               /**< Line's direction setting is output. */
+       };
+
+       /**
+        * @brief Possible active state settings.
+        */
+       enum : int {
+               ACTIVE_LOW = 1,
+               /**< Line's active state is low. */
+               ACTIVE_HIGH,
+               /**< Line's active state is high. */
+       };
+
+private:
+
+       line(::gpiod_line* line, const chip& owner);
+
+       void throw_if_null(void) const;
+
+       ::gpiod_line* _m_line;
+       chip _m_chip;
+
+       friend chip;
+       friend line_bulk;
+       friend line_iterator;
+};
+
+/**
+ * @brief Find a GPIO line by name. Search all GPIO chips present on the system.
+ * @param name Name of the line.
+ * @return Returns a line object - empty if the line was not found.
+ */
+GPIOD_API line find_line(const ::std::string& name);
+
+/**
+ * @brief Describes a single GPIO line event.
+ */
+struct line_event
+{
+
+       /**
+        * @brief Possible event types.
+        */
+       enum : int {
+               RISING_EDGE = 1,
+               /**< Rising edge event. */
+               FALLING_EDGE,
+               /**< Falling edge event. */
+       };
+
+       ::std::chrono::nanoseconds timestamp;
+       /**< Best estimate of time of event occurrence in nanoseconds. */
+       int event_type;
+       /**< Type of the event that occurred. */
+       line source;
+       /**< Line object referencing the GPIO line on which the event occurred. */
+};
+
+/**
+ * @brief Represents a set of GPIO lines.
+ *
+ * Internally an object of this class stores an array of line objects
+ * owned by a single chip.
+ */
+class line_bulk
+{
+public:
+
+       /**
+        * @brief Default constructor. Creates an empty line_bulk object.
+        */
+       GPIOD_API line_bulk(void) = default;
+
+       /**
+        * @brief Construct a line_bulk from a vector of lines.
+        * @param lines Vector of gpiod::line objects.
+        * @note All lines must be owned by the same GPIO chip.
+        */
+       GPIOD_API line_bulk(const ::std::vector<line>& lines);
+
+       /**
+        * @brief Copy constructor.
+        * @param other Other line_bulk object.
+        */
+       GPIOD_API line_bulk(const line_bulk& other) = default;
+
+       /**
+        * @brief Move constructor.
+        * @param other Other line_bulk object.
+        */
+       GPIOD_API line_bulk(line_bulk&& other) = default;
+
+       /**
+        * @brief Assignment operator.
+        * @param other Other line_bulk object.
+        * @return Reference to this object.
+        */
+       GPIOD_API line_bulk& operator=(const line_bulk& other) = default;
+
+       /**
+        * @brief Move assignment operator.
+        * @param other Other line_bulk object.
+        * @return Reference to this object.
+        */
+       GPIOD_API line_bulk& operator=(line_bulk&& other) = default;
+
+       /**
+        * @brief Destructor.
+        */
+       GPIOD_API ~line_bulk(void) = default;
+
+       /**
+        * @brief Add a line to this line_bulk object.
+        * @param new_line Line to add.
+        * @note The new line must be owned by the same chip as all the other
+        *       lines already held by this line_bulk object.
+        */
+       GPIOD_API void add(const line& new_line);
+
+       /**
+        * @brief Get the line at given offset.
+        * @param offset Offset of the line to get.
+        * @return Reference to the line object.
+        */
+       GPIOD_API line& get(unsigned int offset);
+
+       /**
+        * @brief Get the line at given offset without bounds checking.
+        * @param offset Offset of the line to get.
+        * @return Reference to the line object.
+        * @note No bounds checking is performed.
+        */
+       GPIOD_API line& operator[](unsigned int offset);
+
+       /**
+        * @brief Get the number of lines currently held by this object.
+        * @return Number of elements in this line_bulk.
+        */
+       GPIOD_API unsigned int size(void) const noexcept;
+
+       /**
+        * @brief Check if this line_bulk doesn't hold any lines.
+        * @return True if this object is empty, false otherwise.
+        */
+       GPIOD_API bool empty(void) const noexcept;
+
+       /**
+        * @brief Remove all lines from this object.
+        */
+       GPIOD_API void clear(void);
+
+       /**
+        * @brief Request all lines held by this object.
+        * @param config Request config (see gpiod::line_request).
+        * @param default_vals Vector of default values. Only relevant for
+        *                     output direction requests.
+        */
+       GPIOD_API void request(const line_request& config,
+                              std::vector<int> default_vals = std::vector<int>()) const;
+
+       /**
+        * @brief Read values from all lines held by this object.
+        * @return Vector containing line values the order of which corresponds
+        *         with the order of lines in the internal array.
+        */
+       GPIOD_API ::std::vector<int> get_values(void) const;
+
+       /**
+        * @brief Set values of all lines held by this object.
+        * @param values Vector of values to set. Must be the same size as the
+        *        number of lines held by this line_bulk.
+        */
+       GPIOD_API void set_values(const ::std::vector<int>& values) const;
+
+       /**
+        * @brief Poll the set of lines for line events.
+        * @param timeout Number of nanoseconds to wait before returning an
+        *        empty line_bulk.
+        * @return Returns a line_bulk object containing lines on which events
+        *         occurred.
+        */
+       GPIOD_API line_bulk event_wait(const ::std::chrono::nanoseconds& timeout) const;
+
+       /**
+        * @brief Check if this object holds any lines.
+        * @return True if this line_bulk holds at least one line, false otherwise.
+        */
+       GPIOD_API operator bool(void) const noexcept;
+
+       /**
+        * @brief Check if this object doesn't hold any lines.
+        * @return True if this line_bulk is empty, false otherwise.
+        */
+       GPIOD_API bool operator!(void) const noexcept;
+
+       /**
+        * @brief Max number of lines that this object can hold.
+        */
+       GPIOD_API static const unsigned int MAX_LINES;
+
+       /**
+        * @brief Iterator for iterating over lines held by line_bulk.
+        */
+       class iterator
+       {
+       public:
+
+               /**
+                * @brief Default constructor. Builds an empty iterator object.
+                */
+               GPIOD_API iterator(void) = default;
+
+               /**
+                * @brief Copy constructor.
+                * @param other Other line_bulk iterator.
+                */
+               GPIOD_API iterator(const iterator& other) = default;
+
+               /**
+                * @brief Move constructor.
+                * @param other Other line_bulk iterator.
+                */
+               GPIOD_API iterator(iterator&& other) = default;
+
+               /**
+                * @brief Assignment operator.
+                * @param other Other line_bulk iterator.
+                * @return Reference to this iterator.
+                */
+               GPIOD_API iterator& operator=(const iterator& other) = default;
+
+               /**
+                * @brief Move assignment operator.
+                * @param other Other line_bulk iterator.
+                * @return Reference to this iterator.
+                */
+               GPIOD_API iterator& operator=(iterator&& other) = default;
+
+               /**
+                * @brief Destructor.
+                */
+               GPIOD_API ~iterator(void) = default;
+
+               /**
+                * @brief Advance the iterator by one element.
+                * @return Reference to this iterator.
+                */
+               GPIOD_API iterator& operator++(void);
+
+               /**
+                * @brief Dereference current element.
+                * @return Current GPIO line by reference.
+                */
+               GPIOD_API const line& operator*(void) const;
+
+               /**
+                * @brief Member access operator.
+                * @return Current GPIO line by pointer.
+                */
+               GPIOD_API const line* operator->(void) const;
+
+               /**
+                * @brief Check if this operator points to the same element.
+                * @param rhs Right-hand side of the equation.
+                * @return True if this iterator points to the same GPIO line,
+                *         false otherwise.
+                */
+               GPIOD_API bool operator==(const iterator& rhs) const noexcept;
+
+               /**
+                * @brief Check if this operator doesn't point to the same element.
+                * @param rhs Right-hand side of the equation.
+                * @return True if this iterator doesn't point to the same GPIO
+                *         line, false otherwise.
+                */
+               GPIOD_API bool operator!=(const iterator& rhs) const noexcept;
+
+       private:
+
+               iterator(const ::std::vector<line>::iterator& it);
+
+               ::std::vector<line>::iterator _m_iter;
+
+               friend line_bulk;
+       };
+
+       /**
+        * @brief Returns an iterator to the first line.
+        * @return A line_bulk iterator.
+        */
+       GPIOD_API iterator begin(void) noexcept;
+
+       /**
+        * @brief Returns an iterator to the element following the last line.
+        * @return A line_bulk iterator.
+        */
+       GPIOD_API iterator end(void) noexcept;
+
+private:
+
+       void throw_if_empty(void) const;
+       void to_line_bulk(::gpiod_line_bulk* bulk) const;
+
+       ::std::vector<line> _m_bulk;
+};
+
+/**
+ * @brief Create a new chip_iterator.
+ * @return New chip iterator object pointing to the first GPIO chip on the system.
+ * @note This function is needed as we already use the default constructor of
+ *       gpiod::chip_iterator as the return value of gpiod::end.
+ */
+GPIOD_API chip_iterator make_chip_iterator(void);
+
+/**
+ * @brief Support for range-based loops for chip iterators.
+ * @param iter A chip iterator.
+ * @return Iterator unchanged.
+ */
+GPIOD_API chip_iterator begin(chip_iterator iter) noexcept;
+
+/**
+ * @brief Support for range-based loops for chip iterators.
+ * @param iter A chip iterator.
+ * @return New end iterator.
+ */
+GPIOD_API chip_iterator end(const chip_iterator& iter) noexcept;
+
+/**
+ * @brief Allows to iterate over all GPIO chips present on the system.
+ */
+class chip_iterator
+{
+public:
+
+       /**
+        * @brief Default constructor. Creates the end iterator.
+        */
+       GPIOD_API chip_iterator(void) = default;
+
+       /**
+        * @brief Copy constructor.
+        * @param other Other chip_iterator.
+        */
+       GPIOD_API chip_iterator(const chip_iterator& other) = default;
+
+       /**
+        * @brief Move constructor.
+        * @param other Other chip_iterator.
+        */
+       GPIOD_API chip_iterator(chip_iterator&& other) = default;
+
+       /**
+        * @brief Assignment operator.
+        * @param other Other chip_iterator.
+        * @return Reference to this iterator.
+        */
+       GPIOD_API chip_iterator& operator=(const chip_iterator& other) = default;
+
+       /**
+        * @brief Move assignment operator.
+        * @param other Other chip_iterator.
+        * @return Reference to this iterator.
+        */
+       GPIOD_API chip_iterator& operator=(chip_iterator&& other) = default;
+
+       /**
+        * @brief Destructor.
+        */
+       GPIOD_API ~chip_iterator(void) = default;
+
+       /**
+        * @brief Advance the iterator by one element.
+        * @return Reference to this iterator.
+        */
+       GPIOD_API chip_iterator& operator++(void);
+
+       /**
+        * @brief Dereference current element.
+        * @return Current GPIO chip by reference.
+        */
+       GPIOD_API const chip& operator*(void) const;
+
+       /**
+        * @brief Member access operator.
+        * @return Current GPIO chip by pointer.
+        */
+       GPIOD_API const chip* operator->(void) const;
+
+       /**
+        * @brief Check if this operator points to the same element.
+        * @param rhs Right-hand side of the equation.
+        * @return True if this iterator points to the same chip_iterator,
+        *         false otherwise.
+        */
+       GPIOD_API bool operator==(const chip_iterator& rhs) const noexcept;
+
+       /**
+        * @brief Check if this operator doesn't point to the same element.
+        * @param rhs Right-hand side of the equation.
+        * @return True if this iterator doesn't point to the same chip_iterator,
+        *         false otherwise.
+        */
+       GPIOD_API bool operator!=(const chip_iterator& rhs) const noexcept;
+
+private:
+
+       chip_iterator(::gpiod_chip_iter* iter);
+
+       ::std::shared_ptr<::gpiod_chip_iter> _m_iter;
+       chip _m_current;
+
+       friend chip_iterator make_chip_iterator(void);
+};
+
+/**
+ * @brief Support for range-based loops for line iterators.
+ * @param iter A line iterator.
+ * @return Iterator unchanged.
+ */
+GPIOD_API line_iterator begin(line_iterator iter) noexcept;
+
+/**
+ * @brief Support for range-based loops for line iterators.
+ * @param iter A line iterator.
+ * @return New end iterator.
+ */
+GPIOD_API line_iterator end(const line_iterator& iter) noexcept;
+
+/**
+ * @brief Allows to iterate over all lines owned by a GPIO chip.
+ */
+class line_iterator
+{
+public:
+
+       /**
+        * @brief Default constructor. Creates the end iterator.
+        */
+       GPIOD_API line_iterator(void) = default;
+
+       /**
+        * @brief Constructor. Creates the begin iterator.
+        * @param owner Chip owning the GPIO lines over which we want to iterate.
+        */
+       GPIOD_API line_iterator(const chip& owner);
+
+       /**
+        * @brief Copy constructor.
+        * @param other Other line iterator.
+        */
+       GPIOD_API line_iterator(const line_iterator& other) = default;
+
+       /**
+        * @brief Move constructor.
+        * @param other Other line iterator.
+        */
+       GPIOD_API line_iterator(line_iterator&& other) = default;
+
+       /**
+        * @brief Assignment operator.
+        * @param other Other line iterator.
+        * @return Reference to this line_iterator.
+        */
+       GPIOD_API line_iterator& operator=(const line_iterator& other) = default;
+
+       /**
+        * @brief Move assignment operator.
+        * @param other Other line iterator.
+        * @return Reference to this line_iterator.
+        */
+       GPIOD_API line_iterator& operator=(line_iterator&& other) = default;
+
+       /**
+        * @brief Destructor.
+        */
+       GPIOD_API ~line_iterator(void) = default;
+
+       /**
+        * @brief Advance the iterator by one element.
+        * @return Reference to this iterator.
+        */
+       GPIOD_API line_iterator& operator++(void);
+
+       /**
+        * @brief Dereference current element.
+        * @return Current GPIO line by reference.
+        */
+       GPIOD_API const line& operator*(void) const;
+
+       /**
+        * @brief Member access operator.
+        * @return Current GPIO line by pointer.
+        */
+       GPIOD_API const line* operator->(void) const;
+
+       /**
+        * @brief Check if this operator points to the same element.
+        * @param rhs Right-hand side of the equation.
+        * @return True if this iterator points to the same line_iterator,
+        *         false otherwise.
+        */
+       GPIOD_API bool operator==(const line_iterator& rhs) const noexcept;
+
+       /**
+        * @brief Check if this operator doesn't point to the same element.
+        * @param rhs Right-hand side of the equation.
+        * @return True if this iterator doesn't point to the same line_iterator,
+        *         false otherwise.
+        */
+       GPIOD_API bool operator!=(const line_iterator& rhs) const noexcept;
+
+private:
+
+       ::std::shared_ptr<::gpiod_line_iter> _m_iter;
+       line _m_current;
+};
+
+/**
+ * @}
+ */
+
+} /* namespace gpiod */
+
+#endif /* __LIBGPIOD_GPIOD_CXX_HPP__ */
diff --git a/bindings/cxx/iter.cpp b/bindings/cxx/iter.cpp
new file mode 100644 (file)
index 0000000..14b4bdb
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <gpiod.hpp>
+
+#include <system_error>
+
+namespace gpiod {
+
+namespace {
+
+void chip_iter_deleter(::gpiod_chip_iter* iter)
+{
+       ::gpiod_chip_iter_free_noclose(iter);
+}
+
+void line_iter_deleter(::gpiod_line_iter* iter)
+{
+       ::gpiod_line_iter_free(iter);
+}
+
+::gpiod_line_iter* make_line_iterator(::gpiod_chip* chip)
+{
+       ::gpiod_line_iter* iter;
+
+       iter = ::gpiod_line_iter_new(chip);
+       if (!iter)
+               throw ::std::system_error(errno, ::std::system_category(),
+                                         "error creating GPIO line iterator");
+
+       return iter;
+}
+
+} /* namespace */
+
+chip_iterator make_chip_iterator(void)
+{
+       ::gpiod_chip_iter* iter = ::gpiod_chip_iter_new();
+       if (!iter)
+               throw ::std::system_error(errno, ::std::system_category(),
+                                         "error creating GPIO chip iterator");
+
+       return ::std::move(chip_iterator(iter));
+}
+
+bool chip_iterator::operator==(const chip_iterator& rhs) const noexcept
+{
+       return this->_m_current == rhs._m_current;
+}
+
+bool chip_iterator::operator!=(const chip_iterator& rhs) const noexcept
+{
+       return this->_m_current != rhs._m_current;
+}
+
+chip_iterator::chip_iterator(::gpiod_chip_iter *iter)
+       : _m_iter(iter, chip_iter_deleter),
+         _m_current(chip(::gpiod_chip_iter_next_noclose(this->_m_iter.get())))
+{
+
+}
+
+chip_iterator& chip_iterator::operator++(void)
+{
+       ::gpiod_chip* next = ::gpiod_chip_iter_next_noclose(this->_m_iter.get());
+
+       this->_m_current = next ? chip(next) : chip();
+
+       return *this;
+}
+
+const chip& chip_iterator::operator*(void) const
+{
+       return this->_m_current;
+}
+
+const chip* chip_iterator::operator->(void) const
+{
+       return ::std::addressof(this->_m_current);
+}
+
+chip_iterator begin(chip_iterator iter) noexcept
+{
+       return iter;
+}
+
+chip_iterator end(const chip_iterator&) noexcept
+{
+       return ::std::move(chip_iterator());
+}
+
+line_iterator begin(line_iterator iter) noexcept
+{
+       return iter;
+}
+
+line_iterator end(const line_iterator&) noexcept
+{
+       return ::std::move(line_iterator());
+}
+
+line_iterator::line_iterator(const chip& owner)
+       : _m_iter(make_line_iterator(owner._m_chip.get()), line_iter_deleter),
+         _m_current(line(::gpiod_line_iter_next(this->_m_iter.get()), owner))
+{
+
+}
+
+line_iterator& line_iterator::operator++(void)
+{
+       ::gpiod_line* next = ::gpiod_line_iter_next(this->_m_iter.get());
+
+       this->_m_current = next ? line(next, this->_m_current._m_chip) : line();
+
+       return *this;
+}
+
+const line& line_iterator::operator*(void) const
+{
+       return this->_m_current;
+}
+
+const line* line_iterator::operator->(void) const
+{
+       return ::std::addressof(this->_m_current);
+}
+
+bool line_iterator::operator==(const line_iterator& rhs) const noexcept
+{
+       return this->_m_current._m_line == rhs._m_current._m_line;
+}
+
+bool line_iterator::operator!=(const line_iterator& rhs) const noexcept
+{
+       return this->_m_current._m_line != rhs._m_current._m_line;
+}
+
+} /* namespace gpiod */
diff --git a/bindings/cxx/line.cpp b/bindings/cxx/line.cpp
new file mode 100644 (file)
index 0000000..6603ef7
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <gpiod.hpp>
+
+#include <system_error>
+
+namespace gpiod {
+
+line::line(void)
+       : _m_line(nullptr),
+         _m_chip()
+{
+
+}
+
+line::line(::gpiod_line* line, const chip& owner)
+       : _m_line(line),
+         _m_chip(owner)
+{
+
+}
+
+unsigned int line::offset(void) const
+{
+       this->throw_if_null();
+
+       return ::gpiod_line_offset(this->_m_line);
+}
+
+::std::string line::name(void) const
+{
+       this->throw_if_null();
+
+       const char* name = ::gpiod_line_name(this->_m_line);
+
+       return ::std::move(name ? ::std::string(name) : ::std::string());
+}
+
+::std::string line::consumer(void) const
+{
+       this->throw_if_null();
+
+       const char* consumer = ::gpiod_line_consumer(this->_m_line);
+
+       return ::std::move(consumer ? ::std::string(consumer) : ::std::string());
+}
+
+int line::direction(void) const noexcept
+{
+       this->throw_if_null();
+
+       int dir = ::gpiod_line_direction(this->_m_line);
+
+       return dir == GPIOD_LINE_DIRECTION_INPUT ? DIRECTION_INPUT : DIRECTION_OUTPUT;
+}
+
+int line::active_state(void) const noexcept
+{
+       this->throw_if_null();
+
+       int active = ::gpiod_line_active_state(this->_m_line);
+
+       return active == GPIOD_LINE_ACTIVE_STATE_HIGH ? ACTIVE_HIGH : ACTIVE_LOW;
+}
+
+bool line::is_used(void) const
+{
+       this->throw_if_null();
+
+       return ::gpiod_line_is_used(this->_m_line);
+}
+
+bool line::is_open_drain(void) const
+{
+       this->throw_if_null();
+
+       return ::gpiod_line_is_open_drain(this->_m_line);
+}
+
+bool line::is_open_source(void) const
+{
+       this->throw_if_null();
+
+       return ::gpiod_line_is_open_source(this->_m_line);
+}
+
+void line::request(const line_request& config, int default_val) const
+{
+       this->throw_if_null();
+
+       line_bulk bulk({ *this });
+
+       bulk.request(config, { default_val });
+}
+
+bool line::is_requested(void) const
+{
+       this->throw_if_null();
+
+       return ::gpiod_line_is_requested(this->_m_line);
+}
+
+/*
+ * REVISIT: Check the performance of get/set_value & event_wait compared to
+ * the C API. Creating a line_bulk object involves a memory allocation every
+ * time this method if called. If the performance is significantly lower,
+ * switch to calling the C functions for setting/getting line values and
+ * polling for events on single lines directly.
+ */
+
+int line::get_value(void) const
+{
+       this->throw_if_null();
+
+       line_bulk bulk({ *this });
+
+       return bulk.get_values()[0];
+}
+
+void line::set_value(int val) const
+{
+       this->throw_if_null();
+
+       line_bulk bulk({ *this });
+
+       bulk.set_values({ val });
+}
+
+bool line::event_wait(const ::std::chrono::nanoseconds& timeout) const
+{
+       this->throw_if_null();
+
+       line_bulk bulk({ *this });
+
+       line_bulk event_bulk = bulk.event_wait(timeout);
+
+       return ::std::move(event_bulk);
+}
+
+line_event line::event_read(void) const
+{
+       this->throw_if_null();
+
+       ::gpiod_line_event event_buf;
+       line_event event;
+       int rv;
+
+       rv = ::gpiod_line_event_read(this->_m_line, ::std::addressof(event_buf));
+       if (rv < 0)
+               throw ::std::system_error(errno, ::std::system_category(),
+                                         "error reading line event");
+
+       if (event_buf.event_type == GPIOD_LINE_EVENT_RISING_EDGE)
+               event.event_type = line_event::RISING_EDGE;
+       else if (event_buf.event_type == GPIOD_LINE_EVENT_FALLING_EDGE)
+               event.event_type = line_event::FALLING_EDGE;
+
+       event.timestamp = ::std::chrono::nanoseconds(
+                               event_buf.ts.tv_nsec + (event_buf.ts.tv_sec * 1000000000));
+
+       event.source = *this;
+
+       return ::std::move(event);
+}
+
+int line::event_get_fd(void) const
+{
+       this->throw_if_null();
+
+       int ret = ::gpiod_line_event_get_fd(this->_m_line);
+
+       if (ret < 0)
+               ::std::system_error(errno, ::std::system_category(),
+                                   "unable to get the line event file descriptor");
+
+       return ret;
+}
+
+const chip& line::get_chip(void) const
+{
+       return this->_m_chip;
+}
+
+void line::reset(void)
+{
+       this->_m_line = nullptr;
+       this->_m_chip.reset();
+}
+
+bool line::operator==(const line& rhs) const noexcept
+{
+       return this->_m_line == rhs._m_line;
+}
+
+bool line::operator!=(const line& rhs) const noexcept
+{
+       return this->_m_line != rhs._m_line;
+}
+
+line::operator bool(void) const noexcept
+{
+       return this->_m_line != nullptr;
+}
+
+bool line::operator!(void) const noexcept
+{
+       return this->_m_line == nullptr;
+}
+
+void line::throw_if_null(void) const
+{
+       if (!this->_m_line)
+               throw ::std::logic_error("object not holding a GPIO line handle");
+}
+
+line find_line(const ::std::string& name)
+{
+       line ret;
+
+       for (auto& it: make_chip_iterator()) {
+               ret = it.find_line(name);
+               if (ret)
+                       break;
+       }
+
+       return ::std::move(ret);
+}
+
+} /* namespace gpiod */
diff --git a/bindings/cxx/line_bulk.cpp b/bindings/cxx/line_bulk.cpp
new file mode 100644 (file)
index 0000000..72dd7d4
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <gpiod.hpp>
+
+#include <system_error>
+#include <map>
+
+namespace gpiod {
+
+namespace {
+
+const ::std::map<int, int> reqtype_mapping = {
+       { line_request::DIRECTION_AS_IS,        GPIOD_LINE_REQUEST_DIRECTION_AS_IS, },
+       { line_request::DIRECTION_INPUT,        GPIOD_LINE_REQUEST_DIRECTION_INPUT, },
+       { line_request::DIRECTION_OUTPUT,       GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, },
+       { line_request::EVENT_FALLING_EDGE,     GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE, },
+       { line_request::EVENT_RISING_EDGE,      GPIOD_LINE_REQUEST_EVENT_RISING_EDGE, },
+       { line_request::EVENT_BOTH_EDGES,       GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES, },
+};
+
+struct bitset_cmp
+{
+       bool operator()(const ::std::bitset<32>& lhs, const ::std::bitset<32>& rhs)
+       {
+               return lhs.to_ulong() < rhs.to_ulong();
+       }
+};
+
+const ::std::map<::std::bitset<32>, int, bitset_cmp> reqflag_mapping = {
+       { line_request::FLAG_ACTIVE_LOW,        GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW, },
+       { line_request::FLAG_OPEN_DRAIN,        GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN, },
+       { line_request::FLAG_OPEN_SOURCE,       GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE, },
+};
+
+} /* namespace */
+
+const ::std::bitset<32> line_request::FLAG_ACTIVE_LOW("001");
+const ::std::bitset<32> line_request::FLAG_OPEN_SOURCE("010");
+const ::std::bitset<32> line_request::FLAG_OPEN_DRAIN("100");
+
+const unsigned int line_bulk::MAX_LINES = GPIOD_LINE_BULK_MAX_LINES;
+
+line_bulk::line_bulk(const ::std::vector<line>& lines)
+       : _m_bulk()
+{
+       this->_m_bulk.reserve(lines.size());
+
+       for (auto& it: lines)
+               this->add(it);
+}
+
+void line_bulk::add(const line& new_line)
+{
+       if (!new_line)
+               throw ::std::logic_error("line_bulk cannot hold empty line objects");
+
+       if (this->_m_bulk.size() >= MAX_LINES)
+               throw ::std::logic_error("maximum number of lines reached");
+
+       if (this->_m_bulk.size() >= 1 && this->_m_bulk.begin()->get_chip() != new_line.get_chip())
+               throw std::logic_error("line_bulk cannot hold GPIO lines from different chips");
+
+       this->_m_bulk.push_back(new_line);
+}
+
+line& line_bulk::get(unsigned int offset)
+{
+       return this->_m_bulk.at(offset);
+}
+
+line& line_bulk::operator[](unsigned int offset)
+{
+       return this->_m_bulk[offset];
+}
+
+unsigned int line_bulk::size(void) const noexcept
+{
+       return this->_m_bulk.size();
+}
+
+bool line_bulk::empty(void) const noexcept
+{
+       return this->_m_bulk.empty();
+}
+
+void line_bulk::clear(void)
+{
+       this->_m_bulk.clear();
+}
+
+void line_bulk::request(const line_request& config, std::vector<int> default_vals) const
+{
+       this->throw_if_empty();
+
+       if (!default_vals.empty() && this->size() != default_vals.size())
+               throw ::std::invalid_argument("the number of default values must correspond with the number of lines");
+
+       ::gpiod_line_request_config conf;
+       ::gpiod_line_bulk bulk;
+       int rv;
+
+       this->to_line_bulk(::std::addressof(bulk));
+
+       conf.consumer = config.consumer.c_str();
+       conf.request_type = reqtype_mapping.at(config.request_type);
+       conf.flags = 0;
+
+       for (auto& it: reqflag_mapping) {
+               if ((it.first & config.flags).to_ulong())
+                       conf.flags |= it.second;
+       }
+
+       rv = ::gpiod_line_request_bulk(::std::addressof(bulk),
+                                      ::std::addressof(conf), default_vals.data());
+       if (rv)
+               throw ::std::system_error(errno, ::std::system_category(),
+                                         "error requesting GPIO lines");
+}
+
+::std::vector<int> line_bulk::get_values(void) const
+{
+       this->throw_if_empty();
+
+       ::std::vector<int> values;
+       ::gpiod_line_bulk bulk;
+       int rv;
+
+       this->to_line_bulk(::std::addressof(bulk));
+       values.resize(this->_m_bulk.size());
+
+       rv = ::gpiod_line_get_value_bulk(::std::addressof(bulk), values.data());
+       if (rv)
+               throw ::std::system_error(errno, ::std::system_category(),
+                                         "error reading GPIO line values");
+
+       return ::std::move(values);
+}
+
+void line_bulk::set_values(const ::std::vector<int>& values) const
+{
+       this->throw_if_empty();
+
+       if (values.size() != this->_m_bulk.size())
+               throw ::std::invalid_argument("the size of values array must correspond with the number of lines");
+
+       ::gpiod_line_bulk bulk;
+       int rv;
+
+       this->to_line_bulk(::std::addressof(bulk));
+
+       rv = ::gpiod_line_set_value_bulk(::std::addressof(bulk), values.data());
+       if (rv)
+               throw ::std::system_error(errno, ::std::system_category(),
+                                         "error setting GPIO line values");
+}
+
+line_bulk line_bulk::event_wait(const ::std::chrono::nanoseconds& timeout) const
+{
+       this->throw_if_empty();
+
+       ::gpiod_line_bulk bulk, event_bulk;
+       ::timespec ts;
+       line_bulk ret;
+       int rv;
+
+       this->to_line_bulk(::std::addressof(bulk));
+
+       ::gpiod_line_bulk_init(::std::addressof(event_bulk));
+
+       ts.tv_sec = timeout.count() / 1000000000ULL;
+       ts.tv_nsec = timeout.count() % 1000000000ULL;
+
+       rv = ::gpiod_line_event_wait_bulk(::std::addressof(bulk),
+                                         ::std::addressof(ts),
+                                         ::std::addressof(event_bulk));
+       if (rv < 0) {
+               throw ::std::system_error(errno, ::std::system_category(),
+                                         "error polling for events");
+       } else if (rv > 0) {
+               for (unsigned int i = 0; i < event_bulk.num_lines; i++)
+                       ret.add(line(event_bulk.lines[i], this->_m_bulk[i].get_chip()));
+       }
+
+       return ::std::move(ret);
+}
+
+line_bulk::operator bool(void) const noexcept
+{
+       return !this->_m_bulk.empty();
+}
+
+bool line_bulk::operator!(void) const noexcept
+{
+       return this->_m_bulk.empty();
+}
+
+line_bulk::iterator::iterator(const ::std::vector<line>::iterator& it)
+       : _m_iter(it)
+{
+
+}
+
+line_bulk::iterator& line_bulk::iterator::operator++(void)
+{
+       this->_m_iter++;
+
+       return *this;
+}
+
+const line& line_bulk::iterator::operator*(void) const
+{
+       return *this->_m_iter;
+}
+
+const line* line_bulk::iterator::operator->(void) const
+{
+       return this->_m_iter.operator->();
+}
+
+bool line_bulk::iterator::operator==(const iterator& rhs) const noexcept
+{
+       return this->_m_iter == rhs._m_iter;
+}
+
+bool line_bulk::iterator::operator!=(const iterator& rhs) const noexcept
+{
+       return this->_m_iter != rhs._m_iter;
+}
+
+line_bulk::iterator line_bulk::begin(void) noexcept
+{
+       return ::std::move(line_bulk::iterator(this->_m_bulk.begin()));
+}
+
+line_bulk::iterator line_bulk::end(void) noexcept
+{
+       return ::std::move(line_bulk::iterator(this->_m_bulk.end()));
+}
+
+void line_bulk::throw_if_empty(void) const
+{
+       if (this->_m_bulk.empty())
+               throw std::logic_error("line_bulk not holding any GPIO lines");
+}
+
+void line_bulk::to_line_bulk(::gpiod_line_bulk *bulk) const
+{
+       ::gpiod_line_bulk_init(bulk);
+       for (auto& it: this->_m_bulk)
+               ::gpiod_line_bulk_add(bulk, it._m_line);
+}
+
+} /* namespace gpiod */
index 7aaad08430b449dddab32ce39b12012b87ab0dae..8ee988cdbdca621cbd6554fda35165e1d8057f4e 100644 (file)
@@ -33,6 +33,7 @@ AC_SUBST(AR_FLAGS, [cr])
 
 AM_PROG_AR
 AC_PROG_CC
+AC_PROG_CXX
 AC_PROG_LIBTOOL
 AC_PROG_INSTALL
 
@@ -113,6 +114,27 @@ then
        PKG_CHECK_MODULES([UDEV], [libudev >= 215])
 fi
 
+AC_ARG_ENABLE([bindings-cxx],
+       [AC_HELP_STRING([--enable-bindings-cxx],
+               [enable C++ bindings [default=no]])],
+       [
+               if test "x$enableval" = xyes
+               then
+                       with_bindings_cxx=true
+               else
+                       with_bindings_cxx=false
+               fi
+       ],
+       [with_bindings_cxx=false])
+AM_CONDITIONAL([WITH_BINDINGS_CXX], [test "x$with_bindings_cxx" = xtrue])
+
+if test "x$with_bindings_cxx" = xtrue
+then
+       AC_LIBTOOL_CXX
+       # This needs autoconf-archive
+       AX_CXX_COMPILE_STDCXX_11([ext], [mandatory])
+fi
+
 AC_CHECK_PROG([has_doxygen], [doxygen], [true], [false])
 AM_CONDITIONAL([HAS_DOXYGEN], [test "x$has_doxygen" = xtrue])
 if test "x$has_doxygen" = xfalse
@@ -126,6 +148,9 @@ AC_CONFIG_FILES([libgpiod.pc
                 src/Makefile
                 src/lib/Makefile
                 src/tools/Makefile
-                tests/Makefile])
+                tests/Makefile
+                bindings/Makefile
+                bindings/cxx/Makefile
+                bindings/cxx/examples/Makefile])
 
 AC_OUTPUT