From 0dc4929c5e3f5b6cd7ff767993f2c86eb2f531a5 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Tue, 18 Feb 2020 11:23:21 +1100 Subject: [PATCH] concepts: add initial testing header --- CMakeLists.txt | 2 + concepts.hpp | 168 ++++++++++++++++++++++++++++++++++++++++++++++ test/concepts.cpp | 12 ++++ 3 files changed, 182 insertions(+) create mode 100644 concepts.hpp create mode 100644 test/concepts.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e43cd359..a08c8632 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/concepts.hpp b/concepts.hpp new file mode 100644 index 00000000..acb0db19 --- /dev/null +++ b/concepts.hpp @@ -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 + */ + +#pragma once + +#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 default_constructible = std::is_default_constructible_v; + + template + concept copy_constructible = std::is_copy_constructible_v; + + template + concept move_constructible = std::is_move_constructible_v; + + template + concept destructible = std::is_nothrow_destructible_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) { 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; + }; +} + + +/////////////////////////////////////////////////////////////////////////////// +// 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; + { swap (a, b) } -> same_as; + + { a.size () } -> same_as; + { a.max_size () } -> same_as; + { a.empty () } -> boolean; + }; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Some handy non-standard concepts +namespace cruft::concepts { + template + concept arithmetic = std::is_arithmetic_v; +} diff --git a/test/concepts.cpp b/test/concepts.cpp new file mode 100644 index 00000000..5dd4d72a --- /dev/null +++ b/test/concepts.cpp @@ -0,0 +1,12 @@ +#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"); + + return tap.status (); +} \ No newline at end of file