concepts: add initial testing header

This commit is contained in:
Danny Robson 2020-02-18 11:23:21 +11:00
parent 8cdbbcb26e
commit 0dc4929c5e
3 changed files with 182 additions and 0 deletions

View File

@ -288,6 +288,7 @@ list (
cmdopt.hpp cmdopt.hpp
colour.cpp colour.cpp
colour.hpp colour.hpp
concepts.hpp
container.hpp container.hpp
coord.hpp coord.hpp
coord/fwd.hpp coord/fwd.hpp
@ -660,6 +661,7 @@ if (TESTS)
buffer/simple buffer/simple
cmdopt cmdopt
colour colour
concepts
comparator comparator
coord coord
encode/number encode/number

168
concepts.hpp Normal file
View 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
View 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 ();
}