summaryrefslogtreecommitdiff
path: root/concepts_iter.cc
diff options
context:
space:
mode:
Diffstat (limited to 'concepts_iter.cc')
-rw-r--r--concepts_iter.cc187
1 files changed, 187 insertions, 0 deletions
diff --git a/concepts_iter.cc b/concepts_iter.cc
new file mode 100644
index 0000000..a6f4295
--- /dev/null
+++ b/concepts_iter.cc
@@ -0,0 +1,187 @@
+#if 0
+set -xe
+clang++ -std=c++20 concepts_iter.cc && ./a.out
+g++ -std=c++20 concepts_iter.cc && ./a.out
+rm -f a.out
+exit 0
+#endif
+
+#include <concepts>
+#include <optional>
+
+// Experimenting with cpp20 concepts.
+
+// -- BASIC CONCEPTS -----------------------------------------------------------
+
+// Type alias for an optional reference to type T.
+template <typename T>
+using optref = std::optional<std::reference_wrapper<T>>;
+
+// Definition of an ITERATOR concept (trait).
+//
+// The iterator implementation must provide an inner type *item* and a function
+// *next()*.
+template <typename T>
+concept Iter = requires(T iter) {
+ typename T::item;
+
+ { iter.next() } -> std::same_as<optref<typename T::item>>;
+};
+
+// Definition of an BOUND_IDX_ITEMS concept (trait).
+//
+// This concept defines an indexable type, which requires different inner type
+// aliases as well as functions to index into the items and get the bounds.
+template <typename T>
+concept BoundIdxItems = requires(const T t, std::size_t idx) {
+ typename T::value_type;
+
+ { t.size() } -> std::same_as<std::size_t>;
+ { t[idx] } -> std::same_as<typename T::const_reference>;
+};
+
+// -- ITER IMPLEMENTATION ------------------------------------------------------
+
+// Iterator adaptor.
+//
+// An example implementation of the ITER concept, generically for all types that
+// satisfy the BOUND_IDX_ITEMS concept.
+template <BoundIdxItems Items>
+struct iter_adapter {
+ using item = const typename Items::value_type;
+
+ iter_adapter(const Items& i) : items(i) {
+ }
+
+ auto next() -> optref<item> {
+ return idx == items.size() ? std::nullopt : optref<item>{items[idx++]};
+ }
+
+private:
+ const Items& items;
+ std::size_t idx = 0;
+};
+
+// -- EXAMPLE: ALGORITHM USING ITER --------------------------------------------
+
+template <typename T>
+concept CmpLt = requires(T t) { t < t; };
+
+// Example algorithm iterating over the items and finding the max value or
+// returning an empty option if there is none.
+//
+// Puts an additional CMP_LT bound on the items the iterator yields, as we
+// need to compare them.
+template <Iter I>
+auto max(I iter) -> optref<typename I::item>
+ requires CmpLt<typename I::item>
+{
+ auto max = iter.next();
+
+ while (auto val = iter.next()) {
+ if (*max < *val) {
+ max = val;
+ }
+ }
+ return max;
+}
+
+// -- EXAMPLE: VECTOR ITER FIND MAX --------------------------------------------
+
+// Example using std::vector and our iter_adapter to invoke max().
+
+#include <format>
+#include <vector>
+
+template <typename T>
+void report_vector(const std::vector<T>& vec) {
+ printf("vec = {");
+ const char* sep = "";
+ for (const T& v : vec) {
+ printf("%s", std::format("{}{}", sep, v).c_str());
+ sep = ", ";
+ }
+ puts("}");
+}
+
+template <typename T>
+void report_max(const std::vector<T>& vec) {
+ report_vector(vec);
+
+ if (optref<const T> res = max(iter_adapter(vec))) {
+ puts(std::format("Max value found: {}", res->get()).c_str());
+ } else {
+ puts("No max value found!");
+ }
+}
+
+void test_vector_max() {
+ {
+ std::vector<int> vec{1, 4, -9, 42, 1, 3};
+ report_max(vec);
+ }
+ {
+ std::vector<int> vec;
+ report_max(vec);
+ }
+}
+
+// -- EXAMPLE: ITER COMPLEX TYPE -----------------------------------------------
+
+#define LOG puts(__PRETTY_FUNCTION__)
+struct log {
+ log() {
+ LOG;
+ }
+ ~log() {
+ LOG;
+ }
+ log(const log&) {
+ LOG;
+ }
+ log(log&&) {
+ LOG;
+ }
+ log& operator=(const log&) {
+ LOG;
+ return *this;
+ }
+ log& operator=(log&&) {
+ LOG;
+ return *this;
+ }
+};
+#undef LOG
+
+// Example function template in short form using auto and requiring that the
+// iter parameter satisfies the ITER concept.
+// Additionally, this demonstrates the syntax to put extra constraints in the
+// iter param.
+//
+// [1]
+// https://en.cppreference.com/w/cpp/language/function_template#Abbreviated_function_template
+void consume(Iter auto iter)
+ // None sense bound, just to demonstrate the syntax.
+ requires std::same_as<typename decltype(iter)::item, const log>
+{
+ using item = typename decltype(iter)::item;
+ while (optref<item> v = iter.next()) {
+ puts("consumed");
+ // item& i = *v; // would just take a ref to item
+ // item i = *v; // would copy item
+ }
+}
+
+void test_complex_type() {
+ {
+ std::vector<log> logs(4);
+ consume(iter_adapter(logs));
+ }
+}
+
+// -- EXAMPLE: ITER COMPLEX TYPE -----------------------------------------------
+
+int main() {
+ test_vector_max();
+ test_complex_type();
+}