diff --git a/bindings/cxx/examples/.gitignore b/bindings/cxx/examples/.gitignore
index 2209497..268b3f6 100644
--- a/bindings/cxx/examples/.gitignore
+++ b/bindings/cxx/examples/.gitignore
@@ -8,3 +8,7 @@ gpioinfocxx
 gpiomoncxx
 gpionotifycxx
 gpiosetcxx
+async_watch_line_value
+get_line_value
+toggle_line_value
+watch_line_value
diff --git a/bindings/cxx/examples/Makefile.am b/bindings/cxx/examples/Makefile.am
index 36977ef..0213973 100644
--- a/bindings/cxx/examples/Makefile.am
+++ b/bindings/cxx/examples/Makefile.am
@@ -12,7 +12,11 @@ noinst_PROGRAMS = \
 	gpioinfocxx \
 	gpiomoncxx \
 	gpionotifycxx \
-	gpiosetcxx
+	gpiosetcxx \
+	async_watch_line_value \
+	get_line_value \
+	toggle_line_value \
+	watch_line_value
 
 gpiodetectcxx_SOURCES = gpiodetectcxx.cpp
 
@@ -27,3 +31,11 @@ gpiomoncxx_SOURCES = gpiomoncxx.cpp
 gpionotifycxx_SOURCES = gpionotifycxx.cpp
 
 gpiosetcxx_SOURCES = gpiosetcxx.cpp
+
+async_watch_line_value_SOURCES = async_watch_line_value.cpp
+
+get_line_value_SOURCES = get_line_value.cpp
+
+toggle_line_value_SOURCES = toggle_line_value.cpp
+
+watch_line_value_SOURCES = watch_line_value.cpp
diff --git a/bindings/cxx/examples/async_watch_line_value.cpp b/bindings/cxx/examples/async_watch_line_value.cpp
new file mode 100644
index 0000000..965e7dd
--- /dev/null
+++ b/bindings/cxx/examples/async_watch_line_value.cpp
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023 Kent Gibson <warthog618@gmail.com>
+
+/* Minimal example of asynchronously watching for edges on a single line. */
+
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <gpiod.hpp>
+#include <iostream>
+#include <poll.h>
+
+const char *edge_event_type_str(const ::gpiod::edge_event &event)
+{
+	switch (event.type()) {
+	case ::gpiod::edge_event::event_type::RISING_EDGE:
+		return "Rising ";
+	case ::gpiod::edge_event::event_type::FALLING_EDGE:
+		return "Falling";
+	default:
+		return "Unknown";
+	}
+}
+
+int main(void)
+{
+	// example configuration - customize to suit your situation
+	const char *chip_path = "/dev/gpiochip0";
+	int line_offset = 5;
+
+	// assume a button connecting the pin to ground,
+	// so pull it up and provide some debounce.
+	auto request =
+		::gpiod::chip(chip_path)
+			.prepare_request()
+			.set_consumer("async-watch-line-value")
+			.add_line_settings(
+				line_offset,
+				::gpiod::line_settings()
+					.set_direction(
+						::gpiod::line::direction::INPUT)
+					.set_edge_detection(
+						::gpiod::line::edge::BOTH)
+					.set_bias(::gpiod::line::bias::PULL_UP)
+					.set_debounce_period(
+						std::chrono::milliseconds(10)))
+			.do_request();
+
+	// a larger buffer is an optimisation for reading bursts of events from
+	// the kernel, but that is not necessary in this case, so 1 is fine.
+	::gpiod::edge_event_buffer buffer(1);
+
+	struct pollfd pollfd;
+	pollfd.fd = request.fd();
+	pollfd.events = POLLIN;
+
+	for (;;) {
+		// other fds could be registered with the poll and be handled
+		// separately using the pollfd.revents after poll()
+		int ret = poll(&pollfd, 1, -1);
+		if (ret == -1) {
+			::std::cerr << "error waiting for edge events: "
+				    << strerror(errno) << ::std::endl;
+
+			return EXIT_FAILURE;
+		}
+
+		request.read_edge_events(buffer);
+
+		for (const auto &event : buffer)
+			::std::cout << "offset: " << event.line_offset()
+				    << ", type: " << edge_event_type_str(event)
+				    << ", event #" << event.line_seqno()
+				    << ::std::endl;
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/bindings/cxx/examples/get_line_value.cpp b/bindings/cxx/examples/get_line_value.cpp
new file mode 100644
index 0000000..3e14b56
--- /dev/null
+++ b/bindings/cxx/examples/get_line_value.cpp
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023 Kent Gibson <warthog618@gmail.com>
+
+/* Minimal example of reading a single line. */
+
+#include <cstdlib>
+#include <gpiod.hpp>
+#include <iostream>
+
+int main(void)
+{
+	using ::gpiod::line_settings;
+	/* example configuration - customize to suit your situation */
+	const char *chip_path = "/dev/gpiochip0";
+	int line_offset = 5;
+
+	auto request = ::gpiod::chip(chip_path)
+			       .prepare_request()
+			       .set_consumer("get-line-value")
+			       .add_line_settings(
+				       line_offset,
+				       ::gpiod::line_settings().set_direction(
+					       ::gpiod::line::direction::INPUT))
+			       .do_request();
+
+	::gpiod::line::value value = request.get_value(line_offset);
+	::std::cout << value << ::std::endl;
+	return EXIT_SUCCESS;
+}
diff --git a/bindings/cxx/examples/toggle_line_value.cpp b/bindings/cxx/examples/toggle_line_value.cpp
new file mode 100644
index 0000000..2f47553
--- /dev/null
+++ b/bindings/cxx/examples/toggle_line_value.cpp
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023 Kent Gibson <warthog618@gmail.com>
+
+/* Minimal example of toggling a single line. */
+
+#include <gpiod.hpp>
+#include <chrono>
+#include <thread>
+#include <cstdlib>
+#include <iostream>
+
+using ::gpiod::line::value;
+
+value toggle_value(value v)
+{
+	return (v == value::ACTIVE) ? value::INACTIVE : value::ACTIVE;
+}
+
+int main(void)
+{
+	/* example configuration - customize to suit your situation */
+	const char *chip_path = "/dev/gpiochip0";
+	int line_offset = 5;
+
+	value v = value::ACTIVE;
+
+	auto request =
+		::gpiod::chip(chip_path)
+			.prepare_request()
+			.set_consumer("toggle-line-value")
+			.add_line_settings(
+				line_offset,
+				::gpiod::line_settings().set_direction(
+					::gpiod::line::direction::OUTPUT))
+			.do_request();
+
+	for (;;) {
+		::std::cout << v << ::std::endl;
+		std::this_thread::sleep_for(std::chrono::seconds(1));
+		v = toggle_value(v);
+		request.set_value(line_offset, v);
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/bindings/cxx/examples/watch_line_value.cpp b/bindings/cxx/examples/watch_line_value.cpp
new file mode 100644
index 0000000..d3f2390
--- /dev/null
+++ b/bindings/cxx/examples/watch_line_value.cpp
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023 Kent Gibson <warthog618@gmail.com>
+
+/* Minimal example of watching for edges on a single line. */
+
+#include <cstdlib>
+#include <gpiod.hpp>
+#include <iostream>
+
+const char *edge_event_type_str(const ::gpiod::edge_event &event)
+{
+	switch (event.type()) {
+	case ::gpiod::edge_event::event_type::RISING_EDGE:
+		return "Rising ";
+	case ::gpiod::edge_event::event_type::FALLING_EDGE:
+		return "Falling";
+	default:
+		return "Unknown";
+	}
+}
+
+int main(void)
+{
+	/* example configuration - customize to suit your situation */
+	const char *chip_path = "/dev/gpiochip0";
+	int line_offset = 5;
+
+	// assume a button connecting the pin to ground,
+	// so pull it up and provide some debounce.
+	auto request =
+		::gpiod::chip(chip_path)
+			.prepare_request()
+			.set_consumer("watch-line-value")
+			.add_line_settings(
+				line_offset,
+				::gpiod::line_settings()
+					.set_direction(
+						::gpiod::line::direction::INPUT)
+					.set_edge_detection(
+						::gpiod::line::edge::BOTH)
+					.set_bias(::gpiod::line::bias::PULL_UP)
+					.set_debounce_period(
+						std::chrono::milliseconds(10)))
+			.do_request();
+
+	// a larger buffer is an optimisation for reading bursts of events from
+	// the kernel, but that is not necessary in this case, so 1 is fine.
+	::gpiod::edge_event_buffer buffer(1);
+
+	for (;;) {
+		// blocks until at least one event is available
+		request.read_edge_events(buffer);
+
+		for (const auto &event : buffer)
+			::std::cout << "offset: " << event.line_offset()
+				    << ", type: " << edge_event_type_str(event)
+				    << ", event #" << event.line_seqno()
+				    << ::std::endl;
+	}
+
+	return EXIT_SUCCESS;
+}
