From 9f94e8c8e80623372b450352f7a24dc8623f4712 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Thu, 11 Apr 2024 00:50:57 +0200 Subject: concepts: iter example, test out syntax --- .clang-tidy | 6 +- Makefile | 2 +- concepts_iter.cc | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 concepts_iter.cc diff --git a/.clang-tidy b/.clang-tidy index fa33f96..062455a 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -21,13 +21,17 @@ WarningsAsErrors: '*' # some code; Checks: > -*, - readability-*, + readability-identifier-naming*, # readability-identifier-naming.* options: # https://clang.llvm.org/extra/clang-tidy/checks/readability/identifier-naming.html CheckOptions: - { key: readability-identifier-naming.ClassCase, value: lower_case } + - { key: readability-identifier-naming.ConceptCase, value: CamelCase } - { key: readability-identifier-naming.MemberCase, value: lower_case } - { key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE } - { key: readability-identifier-naming.TemplateParameterCase, value: CamelCase } - { key: readability-identifier-naming.TypeAliasCase, value: lower_case } + + # https://github.com/llvm/llvm-project/issues/46097 + - { key: readability-identifier-naming.TypeTemplateParameterIgnoredRegexp, value: expr-type } diff --git a/Makefile b/Makefile index 16d98bb..3096d6a 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ fmt : $(SRC:.cc=.f) %.l: %.cc clang-format -n --Werror $^ - clang-tidy $^ + clang-tidy $^ -- -std=c++20 %.f: %.cc clang-format -i $^ 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 +#include + +// Experimenting with cpp20 concepts. + +// -- BASIC CONCEPTS ----------------------------------------------------------- + +// Type alias for an optional reference to type T. +template +using optref = std::optional>; + +// Definition of an ITERATOR concept (trait). +// +// The iterator implementation must provide an inner type *item* and a function +// *next()*. +template +concept Iter = requires(T iter) { + typename T::item; + + { iter.next() } -> std::same_as>; +}; + +// 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 +concept BoundIdxItems = requires(const T t, std::size_t idx) { + typename T::value_type; + + { t.size() } -> std::same_as; + { t[idx] } -> std::same_as; +}; + +// -- ITER IMPLEMENTATION ------------------------------------------------------ + +// Iterator adaptor. +// +// An example implementation of the ITER concept, generically for all types that +// satisfy the BOUND_IDX_ITEMS concept. +template +struct iter_adapter { + using item = const typename Items::value_type; + + iter_adapter(const Items& i) : items(i) { + } + + auto next() -> optref { + return idx == items.size() ? std::nullopt : optref{items[idx++]}; + } + +private: + const Items& items; + std::size_t idx = 0; +}; + +// -- EXAMPLE: ALGORITHM USING ITER -------------------------------------------- + +template +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 +auto max(I iter) -> optref + requires CmpLt +{ + 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 +#include + +template +void report_vector(const std::vector& vec) { + printf("vec = {"); + const char* sep = ""; + for (const T& v : vec) { + printf("%s", std::format("{}{}", sep, v).c_str()); + sep = ", "; + } + puts("}"); +} + +template +void report_max(const std::vector& vec) { + report_vector(vec); + + if (optref 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 vec{1, 4, -9, 42, 1, 3}; + report_max(vec); + } + { + std::vector 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 +{ + using item = typename decltype(iter)::item; + while (optref 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 logs(4); + consume(iter_adapter(logs)); + } +} + +// -- EXAMPLE: ITER COMPLEX TYPE ----------------------------------------------- + +int main() { + test_vector_max(); + test_complex_type(); +} -- cgit v1.2.3