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