summaryrefslogblamecommitdiff
path: root/concepts_iter.cc
blob: a6f429580f51f6ce2154d9c4a9b223a7c33c63e8 (plain) (tree)


























































































































































































                                                                                             
#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();
}