diff options
Diffstat (limited to 'concepts_iter.cc')
-rw-r--r-- | concepts_iter.cc | 187 |
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(); +} |