/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2020, Danny Robson */ #pragma once #include #include #include #include /////////////////////////////////////////////////////////////////////////////// /// A minimal implementation of the standard concept library for use with /// compilers that lack it. /// /// Portions of this code are adapted from the standard and as such do not /// fall under the top copyright notice. /// /// clang#xxx: Remove me when clang includes an implementation of these. namespace cruft::concepts { template concept floating_point = std::is_floating_point_v; template concept integral = std::is_integral_v; template concept signed_integral = integral && std::is_signed_v; template concept unsigned_integral = integral && std::is_unsigned_v; template concept destructible = std::is_nothrow_destructible_v; template concept constructible_from = destructible && std::is_constructible_v; template concept default_constructible = constructible_from; template concept copy_constructible = std::is_copy_constructible_v; template concept move_constructible = std::is_move_constructible_v; template concept same_as = std::is_same_v and std::is_same_v; template concept convertible_to = std::is_convertible_v && requires (From (&f)()) { static_cast (f()); }; template < typename LHS, typename RHS > concept assignable_from = std::is_lvalue_reference_v && // clang#xxx: common_reference_t is too annoying to implement as // a temporary fix for this temporary fix. Reintroduce this when // clang knows about common_reference_t. // std::common_reference_with< // std::remove_reference_t const&, // std::remove_reference_t const& // > && requires(LHS lhs, RHS&& rhs) { { lhs = std::forward(rhs) } -> same_as; }; template concept swappable = requires (T &&a, T &&b) { std::swap (a, b); }; template < class T > concept movable = std::is_object_v && move_constructible && assignable_from && swappable; template concept boolean = movable> && requires ( std::remove_reference_t const& b1, std::remove_reference_t const& b2, bool const a ) { { b1 } -> convertible_to; { !b1 } -> convertible_to; { b1 && b2 } -> same_as; { b1 && a } -> same_as; { a && b2 } -> same_as; { b1 || b2 } -> same_as; { b1 || a } -> same_as; { a || b2 } -> same_as; { b1 == b2 } -> convertible_to; { b1 == a } -> convertible_to; { a == b2 } -> convertible_to; { b1 != b2 } -> convertible_to; { b1 != a } -> convertible_to; { a != b2 } -> convertible_to; }; template concept equality_comparable = requires ( std::remove_reference_t const &a, std::remove_reference_t const &b ) { { a == b } -> boolean; { a != b } -> boolean; { b == a } -> boolean; { b != a } -> boolean; }; template concept invocable = requires (FunctionT &&function, ArgsT &&...args) { std::invoke ( std::forward (function), std::forward (args)... ); }; template concept regular_invocable = invocable; template concept predicate = regular_invocable && boolean>; } /////////////////////////////////////////////////////////////////////////////// // C++ named requirements namespace cruft::concepts { /// Corresponds to the "Container" named requirement. template concept container = default_constructible && copy_constructible && move_constructible && destructible && equality_comparable && requires (T a, T b) { typename T::value_type; typename T::reference; typename T::const_reference; typename T::iterator; typename T::const_iterator; typename T::difference_type; typename T::size_type; { a = b } -> same_as; { a = std::move (b) } -> same_as; { a.begin () } -> same_as; { a.end () } -> same_as; { a.cbegin () } -> same_as; { a.cend () } -> same_as; { a.swap (b) } -> same_as; { std::swap (a, b) } -> same_as; { a.size () } -> same_as; { a.max_size () } -> same_as; { a.empty () } -> boolean; }; template concept move_assignable = requires (T a, T b) { { a = std::move (b) } -> same_as; }; template concept copy_assignable = move_assignable && requires (T a, T b) { { a = b } -> same_as; }; template concept legacy_iterator = copy_constructible && copy_assignable && destructible && swappable && requires (T t) { typename std::iterator_traits::value_type; typename std::iterator_traits::difference_type; typename std::iterator_traits::reference; typename std::iterator_traits::pointer; typename std::iterator_traits::iterator_category; { *t }; { ++t } -> same_as; }; template concept legacy_input_iterator = legacy_iterator && equality_comparable && requires (T a, T b) { typename std::iterator_traits::reference; typename std::iterator_traits::value_type; { a != b } -> boolean; { ++a } -> same_as; { a++ }; }; } /////////////////////////////////////////////////////////////////////////////// // Some handy non-standard concepts #include namespace cruft::concepts { /// Tests if the type has all typedefs required for use with /// std::iterator_traits. template concept supports_iterator_traits = requires { typename T::difference_type; typename T::value_type; typename T::reference; typename T::iterator_category; // C++20 defines `pointer` as void if it's not present. #if __cplusplus <= 201703L typename T::pointer; #endif }; template concept arithmetic = std::is_arithmetic_v; template concept scalar = std::is_scalar_v; template concept enumeration = std::is_enum_v; /// A type that supports arithmetic operators. template concept numeric = requires (T t) { { t * t } -> convertible_to; { t / t } -> convertible_to; { t - t } -> convertible_to; { t + t } -> convertible_to; }; /// Anything that can be looped over using begin/end template concept iterable = requires (T t) { { std::begin (t) } -> legacy_iterator; { std::end (t) } -> legacy_iterator; { std::cbegin (t) } -> legacy_iterator; { std::cend (t) } -> legacy_iterator; }; /// A class that supports tuple manipulators. template concept tuple = requires (T a, T b) { // We should be checking this, but it fails for zero length tuples and // it's kind of a low priority right now. // { std::tuple_element<0,T> {} }; { std::tuple_size::value } -> convertible_to; { std::tuple_cat (a, b) }; }; }