diff options
author | Johannes Stoelp <johannes.stoelp@gmail.com> | 2024-04-11 00:50:57 +0200 |
---|---|---|
committer | Johannes Stoelp <johannes.stoelp@gmail.com> | 2024-04-11 00:50:57 +0200 |
commit | 9f94e8c8e80623372b450352f7a24dc8623f4712 (patch) | |
tree | 94c24a34728ef2548a9030d10ba6a6f157d69a68 | |
parent | 2b13efa7b78ab2bb08cba52e09b1f12af1701fd9 (diff) | |
download | cpp-templates-9f94e8c8e80623372b450352f7a24dc8623f4712.tar.gz cpp-templates-9f94e8c8e80623372b450352f7a24dc8623f4712.zip |
concepts: iter example, test out syntax
-rw-r--r-- | .clang-tidy | 6 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | concepts_iter.cc | 187 |
3 files changed, 193 insertions, 2 deletions
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 } @@ -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 <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(); +} |