concepts: add initial testing header
This commit is contained in:
parent
8cdbbcb26e
commit
0dc4929c5e
@ -288,6 +288,7 @@ list (
|
||||
cmdopt.hpp
|
||||
colour.cpp
|
||||
colour.hpp
|
||||
concepts.hpp
|
||||
container.hpp
|
||||
coord.hpp
|
||||
coord/fwd.hpp
|
||||
@ -660,6 +661,7 @@ if (TESTS)
|
||||
buffer/simple
|
||||
cmdopt
|
||||
colour
|
||||
concepts
|
||||
comparator
|
||||
coord
|
||||
encode/number
|
||||
|
168
concepts.hpp
Normal file
168
concepts.hpp
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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 <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// 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 <class T>
|
||||
concept default_constructible = std::is_default_constructible_v<T>;
|
||||
|
||||
template <class T>
|
||||
concept copy_constructible = std::is_copy_constructible_v<T>;
|
||||
|
||||
template <class T>
|
||||
concept move_constructible = std::is_move_constructible_v<T>;
|
||||
|
||||
template <typename T>
|
||||
concept destructible = std::is_nothrow_destructible_v<T>;
|
||||
|
||||
|
||||
template <typename A, typename B>
|
||||
concept same_as = std::is_same_v<A, B> and std::is_same_v<B, A>;
|
||||
|
||||
|
||||
template <typename From, typename To>
|
||||
concept convertible_to =
|
||||
std::is_convertible_v<From, To> &&
|
||||
requires (From (&f)())
|
||||
{
|
||||
static_cast<To> (f());
|
||||
};
|
||||
|
||||
|
||||
template <
|
||||
typename LHS,
|
||||
typename RHS
|
||||
>
|
||||
concept assignable_from =
|
||||
std::is_lvalue_reference_v<LHS> &&
|
||||
// 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<LHS> const&,
|
||||
// std::remove_reference_t<RHS> const&
|
||||
// > &&
|
||||
requires(LHS lhs, RHS&& rhs)
|
||||
{
|
||||
{ lhs = std::forward<RHS>(rhs) } -> same_as<LHS>;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept swappable = requires (T &&a, T &&b) { swap (a, b); };
|
||||
|
||||
|
||||
template < class T >
|
||||
concept movable =
|
||||
std::is_object_v<T> &&
|
||||
move_constructible<T> &&
|
||||
assignable_from<T&, T> &&
|
||||
swappable<T>;
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept boolean =
|
||||
movable<std::remove_cvref_t<T>> &&
|
||||
requires (
|
||||
std::remove_reference_t<T> const& b1,
|
||||
std::remove_reference_t<T> const& b2,
|
||||
bool const a
|
||||
)
|
||||
{
|
||||
{ b1 } -> convertible_to<bool>;
|
||||
{ !b1 } -> convertible_to<bool>;
|
||||
|
||||
{ b1 && b2 } -> same_as<bool>;
|
||||
{ b1 && a } -> same_as<bool>;
|
||||
{ a && b2 } -> same_as<bool>;
|
||||
{ b1 || b2 } -> same_as<bool>;
|
||||
{ b1 || a } -> same_as<bool>;
|
||||
{ a || b2 } -> same_as<bool>;
|
||||
|
||||
{ b1 == b2 } -> convertible_to<bool>;
|
||||
{ b1 == a } -> convertible_to<bool>;
|
||||
{ a == b2 } -> convertible_to<bool>;
|
||||
{ b1 != b2 } -> convertible_to<bool>;
|
||||
{ b1 != a } -> convertible_to<bool>;
|
||||
{ a != b2 } -> convertible_to<bool>;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept equality_comparable =
|
||||
requires (
|
||||
std::remove_reference_t<T> const &a,
|
||||
std::remove_reference_t<T> const &b
|
||||
)
|
||||
{
|
||||
{ a == b } -> boolean;
|
||||
{ a != b } -> boolean;
|
||||
{ b == a } -> boolean;
|
||||
{ b != a } -> boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// C++ named requirements
|
||||
namespace cruft::concepts {
|
||||
/// Corresponds to the "Container" named requirement.
|
||||
template <class T>
|
||||
concept container =
|
||||
default_constructible<T> &&
|
||||
copy_constructible<T> &&
|
||||
move_constructible<T> &&
|
||||
destructible<T> &&
|
||||
equality_comparable<T> &&
|
||||
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<T&>;
|
||||
{ a = std::move (b) } -> same_as<T&>;
|
||||
|
||||
{ a.begin () } -> same_as<typename T::iterator>;
|
||||
{ a.end () } -> same_as<typename T::iterator>;
|
||||
{ a.cbegin () } -> same_as<typename T::const_iterator>;
|
||||
{ a.cend () } -> same_as<typename T::const_iterator>;
|
||||
|
||||
{ a.swap (b) } -> same_as<void>;
|
||||
{ swap (a, b) } -> same_as<void>;
|
||||
|
||||
{ a.size () } -> same_as<typename T::size_type>;
|
||||
{ a.max_size () } -> same_as<typename T::size_type>;
|
||||
{ a.empty () } -> boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Some handy non-standard concepts
|
||||
namespace cruft::concepts {
|
||||
template <class T>
|
||||
concept arithmetic = std::is_arithmetic_v<T>;
|
||||
}
|
12
test/concepts.cpp
Normal file
12
test/concepts.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include <cruft/util/tap.hpp>
|
||||
#include <cruft/util/concepts.hpp>
|
||||
|
||||
int main ()
|
||||
{
|
||||
cruft::TAP::logger tap;
|
||||
|
||||
tap.expect (cruft::concepts::container<std::vector<int>>, "vector is a container");
|
||||
tap.expect (!cruft::concepts::container<int>, "int is not a container");
|
||||
|
||||
return tap.status ();
|
||||
}
|
Loading…
Reference in New Issue
Block a user