From 6ed70a4839244e0af3e3820a70e93e73fe0ea8e1 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Fri, 18 Mar 2022 11:38:30 +1000 Subject: [PATCH] concepts: split and remove C++20 re-implementations --- CMakeLists.txt | 1 + cast.hpp | 14 +-- cmdopt2/arg.hpp | 2 +- cmdopt2/parser.cpp | 2 + cmdopt2/parser.hpp | 2 +- concepts.hpp | 282 ++------------------------------------------ concepts/named.hpp | 94 +++++++++++++++ concepts/string.hpp | 23 ++++ concepts/traits.hpp | 23 ++++ maths.hpp | 87 +++++++------- test/concepts.cpp | 6 +- types/tagged.hpp | 3 +- 12 files changed, 215 insertions(+), 324 deletions(-) create mode 100644 concepts/named.hpp create mode 100644 concepts/string.hpp create mode 100644 concepts/traits.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6499816d..bd1c1eaa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -294,6 +294,7 @@ list ( colour.hpp concepts.hpp concepts/clock.hpp + concepts/string.hpp container.hpp coord.hpp coord/fwd.hpp diff --git a/cast.hpp b/cast.hpp index 07177a0f..0ae2b779 100644 --- a/cast.hpp +++ b/cast.hpp @@ -9,7 +9,7 @@ #pragma once -#include "concepts.hpp" +#include "concepts/traits.hpp" #include "debug/assert.hpp" #include "platform.hpp" @@ -74,8 +74,8 @@ namespace cruft::cast { /// Identity casts are allowed so as to simplify the use of this routine /// in template code. template < - concepts::arithmetic NarrowT, - concepts::arithmetic WideT + concepts::traits::arithmetic NarrowT, + concepts::traits::arithmetic WideT > requires (std::is_signed_v == std::is_signed_v) && @@ -150,8 +150,8 @@ namespace cruft::cast { /// /// Runtime checks will be compiled out if NDEBUG is defined. template < - concepts::pointer DstT, - concepts::pointer SrcT + concepts::traits::pointer DstT, + concepts::traits::pointer SrcT > DstT alignment (SrcT src) @@ -172,7 +172,7 @@ namespace cruft::cast { /// the converted value. Note: this is only a debug-time check and is /// compiled out in optimised builds. template < - concepts::pointer T, + concepts::traits::pointer T, typename V > T @@ -188,7 +188,7 @@ namespace cruft::cast { //------------------------------------------------------------------------- template < - concepts::reference T, + concepts::traits::reference T, typename V > T diff --git a/cmdopt2/arg.hpp b/cmdopt2/arg.hpp index 12ec245f..12a9508d 100644 --- a/cmdopt2/arg.hpp +++ b/cmdopt2/arg.hpp @@ -38,7 +38,7 @@ namespace cruft::cmdopt2 { bind (ValueT &ref) { CHECK (!acceptor1); - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v or std::is_same_v) { acceptor1 = [&ref] (std::string_view str) { ref = str; }; } else { acceptor1 = [&ref] (std::string_view str) { ref = parse::from_string (str); }; diff --git a/cmdopt2/parser.cpp b/cmdopt2/parser.cpp index 31a34a54..b1ac372c 100644 --- a/cmdopt2/parser.cpp +++ b/cmdopt2/parser.cpp @@ -1,5 +1,7 @@ #include "./parser.hpp" +#include "./arg.hpp" + using cruft::cmdopt2::parser; diff --git a/cmdopt2/parser.hpp b/cmdopt2/parser.hpp index 8f46dd35..87ac2090 100644 --- a/cmdopt2/parser.hpp +++ b/cmdopt2/parser.hpp @@ -8,7 +8,7 @@ #pragma once -#include "./arg.hpp" +#include "./fwd.hpp" #include #include diff --git a/concepts.hpp b/concepts.hpp index 785d922f..65dd727b 100644 --- a/concepts.hpp +++ b/concepts.hpp @@ -8,268 +8,12 @@ #pragma once -#include -#include -#include -#include +#include "concepts/named.hpp" - -/////////////////////////////////////////////////////////////////////////////// -/// 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++ }; - }; -} - - -/////////////////////////////////////////////////////////////////////////////// -// Trivial wrappers around traits -namespace cruft::concepts { - template - concept arithmetic = std::is_arithmetic_v; - - template - concept scalar = std::is_scalar_v; - - template - concept enumeration = std::is_enum_v; - - template - concept pointer = std::is_pointer_v; - - template - concept reference = std::is_reference_v; -} - - -/////////////////////////////////////////////////////////////////////////////// -// Some handy non-standard concepts +#include #include + namespace cruft::concepts { /// Tests if the type has all typedefs required for use with /// std::iterator_traits. @@ -301,10 +45,10 @@ namespace cruft::concepts { template concept numeric = requires (T t) { - { t * t } -> convertible_to; - { t / t } -> convertible_to; - { t - t } -> convertible_to; - { t + t } -> convertible_to; + { t * t } -> std::convertible_to; + { t / t } -> std::convertible_to; + { t - t } -> std::convertible_to; + { t + t } -> std::convertible_to; }; @@ -312,10 +56,10 @@ namespace cruft::concepts { 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; + { std::begin (t) } -> named::legacy_iterator; + { std::end (t) } -> named::legacy_iterator; + { std::cbegin (t) } -> named::legacy_iterator; + { std::cend (t) } -> named::legacy_iterator; }; @@ -326,7 +70,7 @@ namespace cruft::concepts { // 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_size::value } -> std::convertible_to; { std::tuple_cat (a, b) }; }; -} +} \ No newline at end of file diff --git a/concepts/named.hpp b/concepts/named.hpp new file mode 100644 index 00000000..70f3883b --- /dev/null +++ b/concepts/named.hpp @@ -0,0 +1,94 @@ +#pragma once + +#include +#include +#include + + +/////////////////////////////////////////////////////////////////////////////// +// C++ named requirements +namespace cruft::concepts::named { + /// Corresponds to the "Container" named requirement. + template + concept container = + std::default_initializable and + std::copy_constructible and + std::move_constructible and + std::destructible and + std::equality_comparable and + 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 } -> std::same_as; + { a = std::move (b) } -> std::same_as; + + { a.begin () } -> std::same_as; + { a.end () } -> std::same_as; + { a.cbegin () } -> std::same_as; + { a.cend () } -> std::same_as; + + { a.swap (b) } -> std::same_as; + { std::swap (a, b) } -> std::same_as; + + { a.size () } -> std::same_as; + { a.max_size () } -> std::same_as; + { a.empty () } -> std::same_as; + }; + + + template + concept move_assignable = + requires (T a, T b) + { + { a = std::move (b) } -> std::same_as; + }; + + + template + concept copy_assignable = + move_assignable && + requires (T a, T b) + { + { a = b } -> std::same_as; + }; + + + template + concept legacy_iterator = + std::copy_constructible && + copy_assignable && + std::destructible && + std::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 } -> std::same_as; + }; + + template + concept legacy_input_iterator = + legacy_iterator && + std::equality_comparable && + requires (T a, T b) + { + typename std::iterator_traits::reference; + typename std::iterator_traits::value_type; + { a != b } -> std::same_as; + + { ++a } -> std::same_as; + { a++ }; + }; +} diff --git a/concepts/string.hpp b/concepts/string.hpp new file mode 100644 index 00000000..a7c2da35 --- /dev/null +++ b/concepts/string.hpp @@ -0,0 +1,23 @@ +/* + * 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 2022, Danny Robson + */ + +#pragma once + +#include +#include +#include + +namespace cruft::concepts { + /// A type that behaves like a string. ie, a sequence of characters + template + concept stringy = + std::same_as or + std::same_as or + std::same_as or + std::same_as; +} diff --git a/concepts/traits.hpp b/concepts/traits.hpp new file mode 100644 index 00000000..5a9d8350 --- /dev/null +++ b/concepts/traits.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + + +/////////////////////////////////////////////////////////////////////////////// +// Trivial wrappers around traits +namespace cruft::concepts::traits { + template + concept arithmetic = std::is_arithmetic_v; + + template + concept scalar = std::is_scalar_v; + + template + concept enumeration = std::is_enum_v; + + template + concept pointer = std::is_pointer_v; + + template + concept reference = std::is_reference_v; +} diff --git a/maths.hpp b/maths.hpp index 90a4b37e..99622184 100644 --- a/maths.hpp +++ b/maths.hpp @@ -12,7 +12,10 @@ // it triggers a circular dependency; debug -> format -> maths -> debug // instead, just use cassert +#include "concepts/traits.hpp" +#include "concepts/named.hpp" #include "concepts.hpp" + #include "types/traits.hpp" #include "float.hpp" @@ -36,7 +39,7 @@ /////////////////////////////////////////////////////////////////////////////// namespace cruft { /////////////////////////////////////////////////////////////////////////// - template + template constexpr T abs [[gnu::const]] (T t) { @@ -151,7 +154,7 @@ namespace cruft { template < typename BaseT, - concepts::integral ExponentT + std::integral ExponentT > constexpr BaseT pow [[gnu::const]] (BaseT base, ExponentT exponent) @@ -174,7 +177,7 @@ namespace cruft { //------------------------------------------------------------------------- - template + template constexpr bool is_pow2 [[gnu::const]] (T value) { @@ -188,7 +191,7 @@ namespace cruft { /// /// `val` must be strictly greater than zero, otherwise the results are /// undefined. - template + template constexpr T log2 (T val) { @@ -219,7 +222,7 @@ namespace cruft { /// with runtime performance given the simplistic construction. /// /// It's useful for sizing temporary arrays. - template + template consteval T ilog (T val, T base) { @@ -232,7 +235,7 @@ namespace cruft { /////////////////////////////////////////////////////////////////////////////// /// round T up to the nearest multiple of U - template + template inline std::common_type_t round_up (T value, U size) @@ -247,7 +250,7 @@ namespace cruft { ///---------------------------------------------------------------------------- /// round T up to the nearest power-of-2 - template + template constexpr auto round_pow2 (T value) { @@ -265,8 +268,8 @@ namespace cruft { ///---------------------------------------------------------------------------- /// round T up to the nearest multiple of U and return the quotient. template < - concepts::integral T, - concepts::integral U + std::integral T, + std::integral U > constexpr auto divup (T const a, U const b) @@ -277,7 +280,7 @@ namespace cruft { /////////////////////////////////////////////////////////////////////////////// // Properties - template + template constexpr bool is_integer (T) { @@ -285,7 +288,7 @@ namespace cruft { } - template + template constexpr bool is_integer (T t) { @@ -295,7 +298,7 @@ namespace cruft { //------------------------------------------------------------------------- - template + template constexpr auto digits10 (NumericT v) noexcept { @@ -325,7 +328,7 @@ namespace cruft { } - template + template constexpr int digits (ValueT value, BaseT base) noexcept { @@ -344,7 +347,7 @@ namespace cruft { ///---------------------------------------------------------------------------- /// return positive or negative unit value corresponding to the input. - template + template constexpr T sign (T t) { @@ -355,7 +358,7 @@ namespace cruft { /// return positive or negative unit value corresponding to the input. /// guaranteed to give correct results for signed zeroes, use another /// method if extreme speed is important. - template + template constexpr T sign (T t) { @@ -386,7 +389,7 @@ namespace cruft { // Modulus/etc // namespaced wrapper for `man 3 fmod` - template + template constexpr T mod (T x, T y) { @@ -394,7 +397,7 @@ namespace cruft { } - template + template constexpr T mod (T x, T y) { @@ -402,7 +405,7 @@ namespace cruft { } - template + template ValueT frac (ValueT val) { @@ -515,8 +518,8 @@ namespace cruft { template requires - concepts::legacy_input_iterator && - concepts::floating_point::value_type> + concepts::named::legacy_input_iterator && + std::floating_point::value_type> typename std::iterator_traits::value_type sum (InputT first, InputT last) { @@ -545,8 +548,8 @@ namespace cruft { //------------------------------------------------------------------------- template requires - concepts::legacy_input_iterator && - concepts::integral::value_type> + concepts::named::legacy_input_iterator && + std::integral::value_type> typename std::iterator_traits::value_type sum (InputT first, InputT last) { @@ -575,7 +578,7 @@ namespace cruft { /// parameter packs. /// /// eg, `max (sizeof (T)...)` will otherwise fail with a single type. - template + template constexpr decltype(auto) max (ValueT &&val) { @@ -598,7 +601,7 @@ namespace cruft { //------------------------------------------------------------------------- - template + template typename ContainerT::value_type const& max (ContainerT const &vals) { @@ -606,13 +609,13 @@ namespace cruft { } - template + template typename ValueT::value_type & max (ValueT &&) = delete; //------------------------------------------------------------------------- - template + template typename ContainerT::value_type const& min (ContainerT const &vals) { @@ -620,7 +623,7 @@ namespace cruft { } - template + template typename ContainerT::value_type& min (ContainerT &&) = delete; @@ -643,9 +646,9 @@ namespace cruft { // min/max clamping template < - concepts::scalar T, - concepts::scalar U, - concepts::scalar V + concepts::traits::scalar T, + concepts::traits::scalar U, + concepts::traits::scalar V > constexpr std::common_type_t clamp (T const val, U const lo, V const hi) @@ -697,8 +700,8 @@ namespace cruft { // uint -> float template < - concepts::unsigned_integral T, - concepts::floating_point U + std::unsigned_integral T, + std::floating_point U > constexpr U renormalise (T t) @@ -710,8 +713,8 @@ namespace cruft { //------------------------------------------------------------------------- // float -> uint template < - concepts::floating_point T, - concepts::unsigned_integral U + std::floating_point T, + std::unsigned_integral U > constexpr U renormalise (T t) @@ -745,8 +748,8 @@ namespace cruft { // ambiguous overloads template requires - concepts::floating_point && - concepts::floating_point && + std::floating_point && + std::floating_point && (!std::is_same_v) constexpr U renormalise (T t) @@ -759,8 +762,8 @@ namespace cruft { // hi_uint -> lo_uint template requires - concepts::unsigned_integral && - concepts::unsigned_integral && + std::unsigned_integral && + std::unsigned_integral && (sizeof (T) > sizeof (U)) constexpr U renormalise (T t) @@ -781,8 +784,8 @@ namespace cruft { typename DstT > requires - concepts::unsigned_integral && - concepts::unsigned_integral && + std::unsigned_integral && + std::unsigned_integral && (sizeof (SrcT) < sizeof (DstT)) constexpr DstT renormalise (SrcT src) @@ -834,7 +837,7 @@ namespace cruft { // anything-to-sint template requires - concepts::signed_integral && + std::signed_integral && (!std::is_same::value) constexpr U renormalise (T t) @@ -851,7 +854,7 @@ namespace cruft { // sint-to-anything template requires - concepts::signed_integral && + std::signed_integral && (!std::is_same::value) constexpr U renormalise (T sint) diff --git a/test/concepts.cpp b/test/concepts.cpp index b1bf955b..ea5e129e 100644 --- a/test/concepts.cpp +++ b/test/concepts.cpp @@ -1,12 +1,12 @@ #include -#include +#include int main () { cruft::TAP::logger tap; - tap.expect (cruft::concepts::container>, "vector is a container"); - tap.expect (!cruft::concepts::container, "int is not a container"); + tap.expect (cruft::concepts::named::container>, "vector is a container"); + tap.expect (!cruft::concepts::named::container, "int is not a container"); tap.expect (cruft::concepts::tuple>, "tuple<> is a tuple"); tap.expect (cruft::concepts::tuple>, "tuple is a tuple"); diff --git a/types/tagged.hpp b/types/tagged.hpp index f14ce306..b65701e0 100644 --- a/types/tagged.hpp +++ b/types/tagged.hpp @@ -14,6 +14,7 @@ #include "../maths.hpp" #include "../tuple/type.hpp" +#include #include @@ -54,7 +55,7 @@ namespace cruft { //--------------------------------------------------------------------- template - requires (cruft::concepts::same_as, ValueT> || ...) + requires (std::same_as, ValueT> || ...) tagged (InitialT &&initial) { set (std::forward (initial));