summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Stoelp <johannes.stoelp@gmail.com>2024-04-11 00:50:57 +0200
committerJohannes Stoelp <johannes.stoelp@gmail.com>2024-04-11 00:50:57 +0200
commit9f94e8c8e80623372b450352f7a24dc8623f4712 (patch)
tree94c24a34728ef2548a9030d10ba6a6f157d69a68
parent2b13efa7b78ab2bb08cba52e09b1f12af1701fd9 (diff)
downloadcpp-templates-9f94e8c8e80623372b450352f7a24dc8623f4712.tar.gz
cpp-templates-9f94e8c8e80623372b450352f7a24dc8623f4712.zip
concepts: iter example, test out syntax
-rw-r--r--.clang-tidy6
-rw-r--r--Makefile2
-rw-r--r--concepts_iter.cc187
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 }
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 <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();
+}