From 0a04a480f39ee3e0779018d0f24517f8698db04b Mon Sep 17 00:00:00 2001 From: Christos Choutouridis Date: Wed, 22 Sep 2021 19:30:39 +0300 Subject: [PATCH] DEV: update deque functionality --- include/cont/deque.h | 68 +++- include/cont/range.h | 64 ++++ include/cont/span.h | 607 +++++++++++++++++++++++++++++++++++ include/core/ring_iterator.h | 214 +++++++++++- test/tests/deque.cpp | 217 +++++++++++++ test/tests/ring_iterator.cpp | 193 +++++++++++ test/tests/span.cpp | 274 ++++++++++++++++ 7 files changed, 1621 insertions(+), 16 deletions(-) create mode 100644 include/cont/range.h create mode 100644 include/cont/span.h create mode 100644 test/tests/span.cpp diff --git a/include/cont/deque.h b/include/cont/deque.h index 7563bef..c7771c0 100644 --- a/include/cont/deque.h +++ b/include/cont/deque.h @@ -33,8 +33,10 @@ #include #include +#include #include +#include namespace tbx { @@ -50,16 +52,21 @@ namespace tbx { * We use a ring buffer of size \c N+1. We start the front iterator at the last location of the buffer * and the rear on the first. This way when the queue is full the iterators are pointing to the same location. * - * \tparam Data_t The char-like queued item type. Usually \c char - * \tparam N The size of deque + * \tparam Data_t The char-like queued item type. Usually \c char + * \tparam N The size of deque + * \tparam SemiAtomic True for semi-atomic operation. In that case the \c ring_iterator is also atomic. + * \note + * SemiAtomic means it is safe to access different ends from different threads. For example one thread can + * push only from front and another can pop from back to implement a queue. */ -template +template class deque { public: // meta-identity type using type = deque; using buffer_t = std::array; // We need N+1 spaces ring buffer for N spaces deque - using iterator_t = ring_iterator; + using iterator_t = ring_iterator; + using range_t = range; // STL using value_type = Data_t; @@ -103,9 +110,9 @@ class deque { //! \name Iterators //! @{ public: - constexpr iterator begin() noexcept { return f+1; } - constexpr const_iterator begin() const noexcept { return f+1; } - constexpr const_iterator cbegin() const noexcept { return f+1; } + constexpr iterator begin() noexcept { iterator ret = f; return ++ret; } + constexpr const_iterator begin() const noexcept { iterator ret = f; return ++ret; } + constexpr const_iterator cbegin() const noexcept { iterator ret = f; return ++ret; } constexpr iterator end() noexcept { return r; } constexpr const_iterator end() const noexcept { return r; } @@ -115,9 +122,9 @@ class deque { constexpr const_reverse_iterator rbegin() const noexcept { return r; } constexpr const_reverse_iterator crbegin() const noexcept { return r; } - constexpr reverse_iterator rend() noexcept { return f+1; } - constexpr const_reverse_iterator rend() const noexcept { return f+1; } - constexpr const_reverse_iterator crend() const noexcept { return f+1; } + constexpr reverse_iterator rend() noexcept { reverse_iterator ret = f; return ++ret; } + constexpr const_reverse_iterator rend() const noexcept { reverse_iterator ret = f; return ++ret; } + constexpr const_reverse_iterator crend() const noexcept { reverse_iterator ret = f; return ++ret; } //! @} //! \name Capacity @@ -127,6 +134,9 @@ class deque { constexpr size_t size() noexcept { return full() ? N: (r - f) -1; } + constexpr size_t size() const noexcept { + return full() ? N: (r - f) -1; + } //! \return The maximum size of the deque. The items the queue can hold. constexpr size_t max_size() noexcept { return N; } //! \return The capacity of the deque. The items the queue can hold. @@ -134,7 +144,11 @@ class deque { //! \return True if the deque is empty constexpr bool empty() noexcept { return size() == 0 ? true : false; } //! \return True if the deque is full - constexpr bool full() noexcept { return (r == f) ? true : false; } + constexpr bool full() noexcept { + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_acquire); + return (r == f) ? true : false; + } //! @} //! \name Member access @@ -145,41 +159,65 @@ class deque { constexpr void clear() noexcept { f = iterator_t(data_.data(), N); r = iterator_t(data_.data()); + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_release); } //! \brief Push an item in the front of the deque //! \param it The item to push constexpr void push_front (const Data_t& it) { if (full()) return; + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_acquire); *f-- = it; + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_release); } //! \brief Extract an item from the front of the deque and remove it from the deque //! \param it The item to push constexpr Data_t pop_front () { if (empty()) return Data_t{}; + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_acquire); return *++f; } //! \brief Push an item in the back of the deque //! \param it The item to push constexpr void push_back (const Data_t& it) { if (full()) return; + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_acquire); *r++ = it; + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_release); } //! \brief Extract an item from the back of the deque and remove it from the deque //! \param it The item to push constexpr Data_t pop_back () { if (empty()) return Data_t{}; + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_acquire); return *--r; } //! \brief Get a reference to the item in the front of the deque without extracting it. //! \return Reference to the item - constexpr Data_t& front() noexcept { return *(f+1); } - constexpr const Data_t& front() const noexcept { return *(f+1); } + constexpr Data_t& front() noexcept { iterator_t it = f; return *++it; } + constexpr const Data_t& front() const noexcept { iterator_t it = f; return *++it; } //! \brief Get a reference to the item in the front of the deque without extracting it. //! \return Reference to the item - constexpr Data_t& back() noexcept { return *(r-1); } - constexpr const Data_t& back() const noexcept { return *(r-1); } + constexpr Data_t& back() noexcept { iterator_t it = r; return *--it; } + constexpr const Data_t& back() const noexcept { iterator_t it = r; return *--it; } + + //! \brief Get a pointer to the begin of the items on the deque + //! \return + constexpr Data_t* data() noexcept { return &front(); } + constexpr const Data_t* data() const noexcept { return &front(); } + + //! \brief Get a range for the data in queue + //! \return A begin-end iterator pair struct + constexpr range_t contents () noexcept { iterator_t b = f; return {++b, r}; } + constexpr const range_t contents () const noexcept { iterator_t b = f; return {++b, r}; } //! @} private: diff --git a/include/cont/range.h b/include/cont/range.h new file mode 100644 index 0000000..a544056 --- /dev/null +++ b/include/cont/range.h @@ -0,0 +1,64 @@ +/*! + * \file cont/range.h + * \brief + * A plain definition of a range struct with agregate initialization + * and begin-end pairs. + * + * \copyright Copyright (C) 2021 Christos Choutouridis + * + *
License
+ * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *
+ */ + +#ifndef TBX_CONT_RANGE_H_ +#define TBX_CONT_RANGE_H_ + +#include + +namespace tbx { + +/*! + * \brief + * A plain definition of a range struct with begin-end pairs. + * + * \tparam Iter_t The iterator type of the range + */ +template +struct range { + Iter_t b{}, e{}; + +// range () = default; +// range (const Iter_t& first, const Iter_t& last) noexcept : +// b(first), e(last) { } +// range (Iter_t first, Iter_t last) noexcept : +// b(first), e(last) { } + + Iter_t begin() { return b; } + const Iter_t begin() const { return b; } + const Iter_t cbegin() const { return b; } + Iter_t end() { return e; } + const Iter_t end() const { return e; } + const Iter_t cend() const { return e; } +}; + +} +#endif /* TBX_CONT_RANGE_H_ */ diff --git a/include/cont/span.h b/include/cont/span.h new file mode 100644 index 0000000..a3e2a1c --- /dev/null +++ b/include/cont/span.h @@ -0,0 +1,607 @@ +/* + * This is an implementation of C++20's std::span + * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4820.pdf + */ + +// Copyright Tristan Brindle 2018. +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef TBX_CONT_SPAN_H_ +#define TBX_CONT_SPAN_H_ + +#include +#include +#include +#include + +#ifndef TBX_SPAN_NO_EXCEPTIONS +// Attempt to discover whether we're being compiled with exception support +#if !(defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) +#define TBX_SPAN_NO_EXCEPTIONS +#endif +#endif + +#ifndef TBX_SPAN_NO_EXCEPTIONS +#include +#include +#endif + +// Various feature test macros + +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define TBX_SPAN_HAVE_CPP17 +#endif + +#if __cplusplus >= 201402L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +#define TBX_SPAN_HAVE_CPP14 +#endif + +namespace tbx { + +// Establish default contract checking behavior +#if !defined(TBX_SPAN_THROW_ON_CONTRACT_VIOLATION) && \ + !defined(TBX_SPAN_TERMINATE_ON_CONTRACT_VIOLATION) && \ + !defined(TBX_SPAN_NO_CONTRACT_CHECKING) +#if defined(NDEBUG) || !defined(TBX_SPAN_HAVE_CPP14) +#define TBX_SPAN_NO_CONTRACT_CHECKING +#else +#define TBX_SPAN_TERMINATE_ON_CONTRACT_VIOLATION +#endif +#endif + +#if defined(TBX_SPAN_THROW_ON_CONTRACT_VIOLATION) +struct contract_violation_error : std::logic_error { + explicit contract_violation_error(const char* msg) : std::logic_error(msg) + {} +}; + +inline void contract_violation(const char* msg) +{ + throw contract_violation_error(msg); +} + +#elif defined(TBX_SPAN_TERMINATE_ON_CONTRACT_VIOLATION) +[[noreturn]] inline void contract_violation(const char* /*unused*/) +{ + std::terminate(); +} +#endif + +#if !defined(TBX_SPAN_NO_CONTRACT_CHECKING) +#define TBX_SPAN_STRINGIFY(cond) #cond +#define TBX_SPAN_EXPECT(cond) \ + cond ? (void) 0 : contract_violation("Expected " TBX_SPAN_STRINGIFY(cond)) +#else +#define TBX_SPAN_EXPECT(cond) +#endif + +#if defined(TBX_SPAN_HAVE_CPP17) || defined(__cpp_inline_variables) +#define TBX_SPAN_INLINE_VAR inline +#else +#define TBX_SPAN_INLINE_VAR +#endif + +#if defined(TBX_SPAN_HAVE_CPP14) || \ + (defined(__cpp_constexpr) && __cpp_constexpr >= 201304) +#define TBX_SPAN_HAVE_CPP14_CONSTEXPR +#endif + +#if defined(TBX_SPAN_HAVE_CPP14_CONSTEXPR) +#define TBX_SPAN_CONSTEXPR14 constexpr +#else +#define TBX_SPAN_CONSTEXPR14 +#endif + +#if defined(TBX_SPAN_HAVE_CPP14_CONSTEXPR) && \ + (!defined(_MSC_VER) || _MSC_VER > 1900) +#define TBX_SPAN_CONSTEXPR_ASSIGN constexpr +#else +#define TBX_SPAN_CONSTEXPR_ASSIGN +#endif + +#if defined(TBX_SPAN_NO_CONTRACT_CHECKING) +#define TBX_SPAN_CONSTEXPR11 constexpr +#else +#define TBX_SPAN_CONSTEXPR11 TBX_SPAN_CONSTEXPR14 +#endif + +#if defined(TBX_SPAN_HAVE_CPP17) || defined(__cpp_deduction_guides) +#define TBX_SPAN_HAVE_DEDUCTION_GUIDES +#endif + +#if defined(TBX_SPAN_HAVE_CPP17) || defined(__cpp_lib_byte) +#define TBX_SPAN_HAVE_STD_BYTE +#endif + +#if defined(TBX_SPAN_HAVE_CPP17) || defined(__cpp_lib_array_constexpr) +#define TBX_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC +#endif + +#if defined(TBX_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC) +#define TBX_SPAN_ARRAY_CONSTEXPR constexpr +#else +#define TBX_SPAN_ARRAY_CONSTEXPR +#endif + +#ifdef TBX_SPAN_HAVE_STD_BYTE +using byte = std::byte; +#else +using byte = unsigned char; +#endif + +#if defined(TBX_SPAN_HAVE_CPP17) +#define TBX_SPAN_NODISCARD [[nodiscard]] +#else +#define TBX_SPAN_NODISCARD +#endif + +TBX_SPAN_INLINE_VAR constexpr std::size_t dynamic_extent = SIZE_MAX; + +template +class span; + +namespace detail { + +template +struct span_storage { + constexpr span_storage() noexcept = default; + + constexpr span_storage(E* p_ptr, std::size_t /*unused*/) noexcept + : ptr(p_ptr) + {} + + E* ptr = nullptr; + static constexpr std::size_t size = S; +}; + +template +struct span_storage { + constexpr span_storage() noexcept = default; + + constexpr span_storage(E* p_ptr, std::size_t p_size) noexcept + : ptr(p_ptr), size(p_size) + {} + + E* ptr = nullptr; + std::size_t size = 0; +}; + +// Reimplementation of C++17 std::size() and std::data() +#if defined(TBX_SPAN_HAVE_CPP17) || \ + defined(__cpp_lib_nonmember_container_access) +using std::data; +using std::size; +#else +template +constexpr auto size(const C& c) -> decltype(c.size()) +{ + return c.size(); +} + +template +constexpr std::size_t size(const T (&)[N]) noexcept +{ + return N; +} + +template +constexpr auto data(C& c) -> decltype(c.data()) +{ + return c.data(); +} + +template +constexpr auto data(const C& c) -> decltype(c.data()) +{ + return c.data(); +} + +template +constexpr T* data(T (&array)[N]) noexcept +{ + return array; +} + +template +constexpr const E* data(std::initializer_list il) noexcept +{ + return il.begin(); +} +#endif // TBX_SPAN_HAVE_CPP17 + +#if defined(TBX_SPAN_HAVE_CPP17) || defined(__cpp_lib_void_t) +using std::void_t; +#else +template +using void_t = void; +#endif + +template +using uncvref_t = + typename std::remove_cv::type>::type; + +template +struct is_span : std::false_type {}; + +template +struct is_span> : std::true_type {}; + +template +struct is_std_array : std::false_type {}; + +template +struct is_std_array> : std::true_type {}; + +template +struct has_size_and_data : std::false_type {}; + +template +struct has_size_and_data())), + decltype(detail::data(std::declval()))>> + : std::true_type {}; + +template > +struct is_container { + static constexpr bool value = + !is_span::value && !is_std_array::value && + !std::is_array::value && has_size_and_data::value; +}; + +template +using remove_pointer_t = typename std::remove_pointer::type; + +template +struct is_container_element_type_compatible : std::false_type {}; + +template +struct is_container_element_type_compatible< + T, E, + typename std::enable_if< + !std::is_same()))>::type, + void>::value>::type> + : std::is_convertible< + remove_pointer_t()))> (*)[], + E (*)[]> {}; + +template +struct is_complete : std::false_type {}; + +template +struct is_complete : std::true_type {}; + +} // namespace detail + +template +class span { + static_assert(std::is_object::value, + "A span's ElementType must be an object type (not a " + "reference type or void)"); + static_assert(detail::is_complete::value, + "A span's ElementType must be a complete type (not a forward " + "declaration)"); + static_assert(!std::is_abstract::value, + "A span's ElementType cannot be an abstract class type"); + + using storage_type = detail::span_storage; + +public: + // constants and types + using element_type = ElementType; + using value_type = typename std::remove_cv::type; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = element_type*; + using const_pointer = const element_type*; + using reference = element_type&; + using const_reference = const element_type&; + using iterator = pointer; + using reverse_iterator = std::reverse_iterator; + + static constexpr size_type extent = Extent; + + // [span.cons], span constructors, copy, assignment, and destructor + template < + std::size_t E = Extent, + typename std::enable_if<(E == dynamic_extent || E <= 0), int>::type = 0> + constexpr span() noexcept + {} + + TBX_SPAN_CONSTEXPR11 span(pointer ptr, size_type count) + : storage_(ptr, count) + { + TBX_SPAN_EXPECT(extent == dynamic_extent || count == extent); + } + + TBX_SPAN_CONSTEXPR11 span(pointer first_elem, pointer last_elem) + : storage_(first_elem, last_elem - first_elem) + { + TBX_SPAN_EXPECT(extent == dynamic_extent || + last_elem - first_elem == + static_cast(extent)); + } + + template ::value, + int>::type = 0> + constexpr span(element_type (&arr)[N]) noexcept : storage_(arr, N) + {} + + template &, ElementType>::value, + int>::type = 0> + TBX_SPAN_ARRAY_CONSTEXPR span(std::array& arr) noexcept + : storage_(arr.data(), N) + {} + + template &, ElementType>::value, + int>::type = 0> + TBX_SPAN_ARRAY_CONSTEXPR span(const std::array& arr) noexcept + : storage_(arr.data(), N) + {} + + template < + typename Container, std::size_t E = Extent, + typename std::enable_if< + E == dynamic_extent && detail::is_container::value && + detail::is_container_element_type_compatible< + Container&, ElementType>::value, + int>::type = 0> + constexpr span(Container& cont) +// : storage_(detail::data(cont), detail::size(cont)) + : storage_(cont.data(), cont.size()) + {} + + template < + typename Container, std::size_t E = Extent, + typename std::enable_if< + E == dynamic_extent && detail::is_container::value && + detail::is_container_element_type_compatible< + const Container&, ElementType>::value, + int>::type = 0> + constexpr span(const Container& cont) + : storage_(detail::data(cont), detail::size(cont)) + {} + + constexpr span(const span& other) noexcept = default; + + template ::value, + int>::type = 0> + constexpr span(const span& other) noexcept + : storage_(other.data(), other.size()) + {} + + ~span() noexcept = default; + + TBX_SPAN_CONSTEXPR_ASSIGN span& + operator=(const span& other) noexcept = default; + + // [span.sub], span subviews + template + TBX_SPAN_CONSTEXPR11 span first() const + { + TBX_SPAN_EXPECT(Count <= size()); + return {data(), Count}; + } + + template + TBX_SPAN_CONSTEXPR11 span last() const + { + TBX_SPAN_EXPECT(Count <= size()); + return {data() + (size() - Count), Count}; + } + + template + using subspan_return_t = + span; + + template + TBX_SPAN_CONSTEXPR11 subspan_return_t subspan() const + { + TBX_SPAN_EXPECT(Offset <= size() && + (Count == dynamic_extent || Offset + Count <= size())); + return {data() + Offset, + Count != dynamic_extent ? Count : size() - Offset}; + } + + TBX_SPAN_CONSTEXPR11 span + first(size_type count) const + { + TBX_SPAN_EXPECT(count <= size()); + return {data(), count}; + } + + TBX_SPAN_CONSTEXPR11 span + last(size_type count) const + { + TBX_SPAN_EXPECT(count <= size()); + return {data() + (size() - count), count}; + } + + TBX_SPAN_CONSTEXPR11 span + subspan(size_type offset, size_type count = dynamic_extent) const + { + TBX_SPAN_EXPECT(offset <= size() && + (count == dynamic_extent || offset + count <= size())); + return {data() + offset, + count == dynamic_extent ? size() - offset : count}; + } + + // [span.obs], span observers + constexpr size_type size() const noexcept { return storage_.size; } + + constexpr size_type size_bytes() const noexcept + { + return size() * sizeof(element_type); + } + + TBX_SPAN_NODISCARD constexpr bool empty() const noexcept + { + return size() == 0; + } + + // [span.elem], span element access + TBX_SPAN_CONSTEXPR11 reference operator[](size_type idx) const + { + TBX_SPAN_EXPECT(idx < size()); + return *(data() + idx); + } + + TBX_SPAN_CONSTEXPR11 reference front() const + { + TBX_SPAN_EXPECT(!empty()); + return *data(); + } + + TBX_SPAN_CONSTEXPR11 reference back() const + { + TBX_SPAN_EXPECT(!empty()); + return *(data() + (size() - 1)); + } + + constexpr pointer data() const noexcept { return storage_.ptr; } + + // [span.iterators], span iterator support + constexpr iterator begin() const noexcept { return data(); } + + constexpr iterator end() const noexcept { return data() + size(); } + + TBX_SPAN_ARRAY_CONSTEXPR reverse_iterator rbegin() const noexcept + { + return reverse_iterator(end()); + } + + TBX_SPAN_ARRAY_CONSTEXPR reverse_iterator rend() const noexcept + { + return reverse_iterator(begin()); + } + +private: + storage_type storage_{}; +}; + +#ifdef TBX_SPAN_HAVE_DEDUCTION_GUIDES + +/* Deduction Guides */ +template +span(T (&)[N])->span; + +template +span(std::array&)->span; + +template +span(const std::array&)->span; + +template +span(Container&)->span; + +template +span(const Container&)->span; + +#endif // TCB_HAVE_DEDUCTION_GUIDES + +template +constexpr span +make_span(span s) noexcept +{ + return s; +} + +template +constexpr span make_span(T (&arr)[N]) noexcept +{ + return {arr}; +} + +template +TBX_SPAN_ARRAY_CONSTEXPR span make_span(std::array& arr) noexcept +{ + return {arr}; +} + +template +TBX_SPAN_ARRAY_CONSTEXPR span +make_span(const std::array& arr) noexcept +{ + return {arr}; +} + +template +constexpr span make_span(Container& cont) +{ + return {cont}; +} + +template +constexpr span +make_span(const Container& cont) +{ + return {cont}; +} + +template +span +as_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template < + class ElementType, size_t Extent, + typename std::enable_if::value, int>::type = 0> +span +as_writable_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template +constexpr auto get(span s) -> decltype(s[N]) +{ + return s[N]; +} + +} // namespace tbx + +namespace std { + +template +class tuple_size> + : public integral_constant {}; + +template +class tuple_size>; // not defined + +template +class tuple_element> { +public: + static_assert(Extent != tbx::dynamic_extent && + I < Extent, + ""); + using type = ElementType; +}; + +} // end namespace std + +#endif // TBX_CONT_SPAN_H_ diff --git a/include/core/ring_iterator.h b/include/core/ring_iterator.h index 95d4e78..987cefe 100644 --- a/include/core/ring_iterator.h +++ b/include/core/ring_iterator.h @@ -35,10 +35,11 @@ #include #include +#include namespace tbx { -template +template class ring_iterator { //! \name STL iterator traits "forwarding" //! @{ @@ -164,6 +165,9 @@ class ring_iterator { return N; } + constexpr operator Iter_t() noexcept { return iter_; } + constexpr operator const Iter_t() const noexcept { return iter_; } + protected: Iter_t base_; Iter_t iter_; @@ -226,6 +230,214 @@ noexcept { } + + + + +template +class ring_iterator { + //! \name STL iterator traits "forwarding" + //! @{ + protected: + using traits_type = std::iterator_traits; + + public: + using iterator_type = Iter_t; + using iterator_category = typename traits_type::iterator_category; + using value_type = typename traits_type::value_type; + using difference_type = typename traits_type::difference_type; + using reference = typename traits_type::reference; + using pointer = typename traits_type::pointer; + //! @} + + + //! \name Constructor / Destructor + //! @{ + public: + constexpr ring_iterator(const Iter_t base =nullptr) noexcept : + base_(base), iter_(base) { } + + constexpr ring_iterator(const Iter_t base, size_t elem) noexcept : + base_(base), iter_(base + elem) { } + + constexpr ring_iterator(const ring_iterator& it) noexcept : + base_(it.base_) { + iter_ = it.iter_.load(std::memory_order_acquire); + } + + constexpr ring_iterator& operator= (const ring_iterator& it) noexcept { + base_ = it.base_; + iter_ = it.iter_.load(std::memory_order_acquire); + return *this; + } + //! @} + + //! \name Forward iterator requirements + //! @{ + public: + constexpr reference operator*() const noexcept { + return *iter_.load(std::memory_order_acquire); + } + + constexpr pointer operator->() const noexcept { + return iter_.load(std::memory_order_acquire); + } + + constexpr ring_iterator& operator++() noexcept { + Iter_t itnew, it = iter_.load(std::memory_order_acquire); + do { + itnew = it; + if (static_cast(++itnew - base_) >= N) + itnew = base_; + } while (!iter_.compare_exchange_weak(it, itnew, std::memory_order_acq_rel)); + return *this; + } + constexpr ring_iterator operator++(int) noexcept { + ring_iterator it = *this; + this->operator ++(); + return it; + } + //! @} + + //! \name Bidirectional iterator requirements + //! @{ + public: + constexpr ring_iterator& operator--() noexcept { + Iter_t itnew, it = iter_.load(std::memory_order_acquire); + do { + itnew = it; + if (--itnew < base_) + itnew = base_ + N -1; + } while (!iter_.compare_exchange_weak(it, itnew, std::memory_order_acq_rel)); + return *this; + } + constexpr ring_iterator operator--(int) noexcept { + ring_iterator it = *this; + this->operator --(); + return it; + } + //! @} + + //! \name Random access iterator requirements + //! @{ + constexpr reference operator[](difference_type n) const noexcept { + difference_type k = iter_.load(std::memory_order_acquire) - base_; // ptrdiff from base_ + return (static_cast(k + n) < N) ? + base_[k + n] : // on range + base_[k + n - N]; // out of range, loop + } + constexpr ring_iterator& operator+=(difference_type n) noexcept { + Iter_t itnew, it = iter_.load(std::memory_order_acquire); + do { + itnew = it; + difference_type k = it - base_; // ptrdiff from base_ + itnew += (static_cast(k + n) < N) ? + n : // on range + n - N; // out of range, loop + } while (!iter_.compare_exchange_weak(it, itnew, std::memory_order_acquire)); + return *this; + } + constexpr ring_iterator operator+(difference_type n) const noexcept { + difference_type k = iter_.load(std::memory_order_acquire) - base_; // ptrdiff from base_ + return (static_cast(k + n) < N) ? + ring_iterator(base_, k + n) : // on range + ring_iterator(base_, k + n - N); // out of range, loop + } + constexpr ring_iterator& operator-=(difference_type n) noexcept { + Iter_t itnew, it = iter_.load(std::memory_order_acquire); + do { + itnew = it; + difference_type k = it - base_; // ptrdiff from base_ + itnew -= ((k - n) < 0)? + n - N: // out of range, loop + n; // on range + } while (!iter_.compare_exchange_weak(it, itnew, std::memory_order_acquire)); + return *this; + } + constexpr ring_iterator operator-(difference_type n) const noexcept { + difference_type k = iter_.load(std::memory_order_acquire) - base_; // ptrdiff from base_ + return ((k - n) < 0) ? + ring_iterator(base_, k - n + N) : // out of range, loop + ring_iterator(base_, k - n); // on range + } + //! @} + + //! \name Data members and access + //! @{ + constexpr const Iter_t& base() const noexcept { + return base_; + } + constexpr const Iter_t iter() const noexcept { + return iter_.load(std::memory_order_acquire); + } + constexpr size_t size() noexcept { + return N; + } + + constexpr operator Iter_t() noexcept { return iter_.load(std::memory_order_acquire); } + constexpr operator const Iter_t() const noexcept { return iter_.load(std::memory_order_acquire); } + + protected: + Iter_t base_; + std::atomic iter_; + //! @} +}; + +// Forward iterator requirements +template +inline bool operator==(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() == rhs.iter(); +} + +template +inline bool operator!=(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() != rhs.iter(); +} + +// Random access iterator requirements +template +inline bool operator<(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() < rhs.iter(); +} + +template +inline bool operator<=(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() <= rhs.iter(); +} + +template +inline bool operator>(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() > rhs.iter(); +} + +template +inline bool operator>=(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() >= rhs.iter(); +} + +template +inline auto operator-(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept +-> decltype(lhs.iter() - rhs.iter()) { + auto diff = lhs.iter() - rhs.iter(); + return diff < 0 ? + diff + N : // loop + diff; // no loop +} + +template +inline ring_iterator operator+(std::ptrdiff_t lhs, const ring_iterator& rhs) +noexcept { + ring_iterator it(rhs.iter()); + return it += lhs; +} + } //namespace tbx; #endif /* TBX_CORE_RING_ITERATOR_H_ */ diff --git a/test/tests/deque.cpp b/test/tests/deque.cpp index 1b1f566..bf7310a 100644 --- a/test/tests/deque.cpp +++ b/test/tests/deque.cpp @@ -29,12 +29,44 @@ * */ #include +#include #include +#include +#include + namespace Tdeque { using namespace tbx; + template + struct is_span : std::false_type {}; + + template + struct is_span> : std::true_type {}; + + template + struct is_std_array : std::false_type {}; + + template + struct is_std_array> : std::true_type {}; + + template + struct has_size_and_data : std::false_type {}; + + template + struct has_size_and_data().size()), + decltype(std::declval().data())>> + : std::true_type {}; + // Concept + TEST(Tdeque, concept) { + using deque_t = deque; + + EXPECT_EQ (true, !is_span::value); + EXPECT_EQ (true, !is_std_array::value); + EXPECT_EQ (true, !std::is_array::value); + EXPECT_EQ (true, has_size_and_data::value); + } // Test construction TEST(Tdeque, contruct) { deque q1; @@ -192,5 +224,190 @@ namespace Tdeque { EXPECT_EQ(6, check_it); // run through all } + TEST (Tdeque, range) { + deque q1{1, 2, 3, 4, 5, 6, 7, 8}; + int check_it=1; + + for (auto& it : q1.contents()) + EXPECT_EQ(it, check_it++); + + EXPECT_EQ(9, check_it); // run through all + } + + + // Concept + TEST(Tdeque, concept_atomic) { + using deque_t = deque; + + EXPECT_EQ (true, !is_span::value); + EXPECT_EQ (true, !is_std_array::value); + EXPECT_EQ (true, !std::is_array::value); + EXPECT_EQ (true, has_size_and_data::value); + } + // Test construction + TEST(Tdeque, contruct_atomic) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + deque q3{1, 2, 3, 4, 5}; + + EXPECT_EQ (8UL, q1.capacity()); + EXPECT_EQ (0UL, q1.size()); + EXPECT_EQ (8UL, q2.capacity()); + EXPECT_EQ (8UL, q2.size()); + EXPECT_EQ (8UL, q3.capacity()); + EXPECT_EQ (5UL, q3.size()); + } + + // simple push-pop functionality + TEST(Tdeque, push_pop_atomic) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + + q1.push_front(1); + q1.push_front(2); + EXPECT_EQ (1, q1.pop_back()); + EXPECT_EQ (2, q1.pop_back()); + + q1.push_back(1); + q1.push_back(2); + EXPECT_EQ (1, q1.pop_front()); + EXPECT_EQ (2, q1.pop_front()); + + q1.push_front(2); + q1.push_back(3); + q1.push_front(1); + q1.push_back(4); + + for (int i=1 ; i<= 4 ; ++i) + EXPECT_EQ ((int)i, q1.pop_front()); + } + + // front-back + TEST(Tdeque, front_back_atomic) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + + q1.push_front(2); + q1.push_front(1); + q1.push_back(3); + q1.push_back(4); + + EXPECT_EQ (1, q1.front()); + EXPECT_EQ (4, q1.back()); + EXPECT_EQ (1, q2.front()); + EXPECT_EQ (8, q2.back()); + } + + // capacity + TEST(Tdeque, capacity_atomic) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + + q1.push_back(1); + q1.clear(); + EXPECT_EQ (true, q1.empty()); + EXPECT_EQ (true, q2.full()); + + EXPECT_EQ (8UL, q1.capacity()); + EXPECT_EQ (8UL, q2.capacity()); + + EXPECT_EQ (0UL, q1.size()); + EXPECT_EQ (8UL, q2.size()); + + q1.push_back(2); + EXPECT_EQ (1UL, q1.size()); + q1.push_front(1); + EXPECT_EQ (2UL, q1.size()); + + q1.pop_back(); + EXPECT_EQ (1UL, q1.size()); + q1.pop_front(); + EXPECT_EQ (0UL, q1.size()); + } + + // push-pop limits + TEST (Tdeque, push_pop_limits_atomic) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + + EXPECT_EQ (int{}, q1.pop_back()); + EXPECT_EQ (0UL, q1.size()); + EXPECT_EQ (true, q1.empty()); + EXPECT_EQ (false, q1.full()); + + EXPECT_EQ (int{}, q1.pop_front()); + EXPECT_EQ (0UL, q1.size()); + EXPECT_EQ (true, q1.empty()); + EXPECT_EQ (false, q1.full()); + + q2.push_front(0); + EXPECT_EQ (1, q2.front()); + EXPECT_EQ (8, q2.back()); + EXPECT_EQ (8UL, q2.size()); + EXPECT_EQ (false, q2.empty()); + EXPECT_EQ (true, q2.full()); + + q2.push_back(9); + EXPECT_EQ (1, q2.front()); + EXPECT_EQ (8, q2.back()); + EXPECT_EQ (8UL, q2.size()); + EXPECT_EQ (false, q2.empty()); + EXPECT_EQ (true, q2.full()); + } + + // iterators + TEST (Tdeque, iterators_atomic) { + deque q1{1, 2, 3, 4, 5, 6, 7, 8}; + int check_it=1; + + EXPECT_EQ (q1.begin().base(), q1.end().base()); + EXPECT_NE (q1.begin().iter(), q1.end().iter()); + EXPECT_EQ (1, *q1.begin()); + EXPECT_EQ (true, (q1.begin() == ++q1.end())); // loop edge iterators + + for (auto it = q1.begin() ; it != q1.end() ; ++it) + EXPECT_EQ(*it, check_it++); + EXPECT_EQ(9, check_it); // run through all + + EXPECT_EQ (1, q1.front()); // queue stays intact + EXPECT_EQ (8, q1.back()); + EXPECT_EQ (8UL, q1.size()); + EXPECT_EQ (false, q1.empty()); + EXPECT_EQ (true, q1.full()); + + q1.pop_front(); + q1.pop_back(); + + check_it=2; + for (auto& it : q1) + EXPECT_EQ(it, check_it++); + EXPECT_EQ(8, check_it); // run through all + + EXPECT_EQ (2, q1.front()); // queue stays intact + EXPECT_EQ (7, q1.back()); + EXPECT_EQ (6UL, q1.size()); + EXPECT_EQ (false, q1.empty()); + EXPECT_EQ (false, q1.full()); + + deque q2; + q2.push_front(2); + q2.push_front(1); + q2.push_back(3); + q2.push_back(4); + q2.push_back(5); + check_it =1; + for (auto& it : q2) + EXPECT_EQ(it, check_it++); + EXPECT_EQ(6, check_it); // run through all + + } + TEST (Tdeque, range_atomic) { + deque q1{1, 2, 3, 4, 5, 6, 7, 8}; + int check_it=1; + + for (auto& it : q1.contents()) + EXPECT_EQ(it, check_it++); + EXPECT_EQ(9, check_it); // run through all + } } diff --git a/test/tests/ring_iterator.cpp b/test/tests/ring_iterator.cpp index 55b1617..85b34b4 100644 --- a/test/tests/ring_iterator.cpp +++ b/test/tests/ring_iterator.cpp @@ -228,4 +228,197 @@ namespace Tring_iterator { EXPECT_EQ (1, (i2 - i1)); // loop } + + // Test construction atomic + TEST(Tring_iterator, construct_atomic) { + int A[10]; + + //default constructor + ring_iterator i1; + EXPECT_EQ(nullptr, i1.base()); + EXPECT_EQ(nullptr, i1.iter()); + EXPECT_EQ(10UL, i1.size()); + + // implementation specific (you can remove it freely) + EXPECT_EQ(2*sizeof(int*), sizeof(i1)); + + // basic + ring_iterator i2(A); + EXPECT_EQ(A, i2.base()); + EXPECT_EQ(A, i2.iter()); + EXPECT_EQ(10UL, i2.size()); + + // basic from assignment + ring_iterator i3 = A; + EXPECT_EQ(A, i3.base()); + EXPECT_EQ(A, i3.iter()); + EXPECT_EQ(10UL, i3.size()); + + // basic with offset + ring_iterator i4(A, 5); + EXPECT_EQ(A, i4.base()); + EXPECT_EQ(&A[5], i4.iter()); + EXPECT_EQ(10UL, i4.size()); + + // copy (Legacy iterator) + auto i5 = i2; + EXPECT_EQ(A, i5.base()); + EXPECT_EQ(A, i5.iter()); + EXPECT_EQ(10UL, i5.size()); + + // arbitrary type + struct TT { int a,b,c; }; + std::array t; + ring_iterator it(t.data(), 2); + EXPECT_EQ(t.begin(), it.base()); + EXPECT_EQ(&t[2], it.iter()); + EXPECT_EQ(10UL, it.size()); + } + + // Legacy iterator atomic + TEST(Tring_iterator, LegacyIterator_atomic) { + + EXPECT_EQ(true, (std::is_same::value_type>::value)); + EXPECT_EQ(true, (std::is_same::difference_type>::value)); + EXPECT_EQ(true, (std::is_same::reference>::value)); + EXPECT_EQ(true, (std::is_same::pointer>::value)); + EXPECT_EQ(true, (std::is_same::iterator_category>::value)); + + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator i1(A); + + // copy constructible/assignable + auto i2 = i1; + EXPECT_EQ(A, i2.base()); + EXPECT_EQ(A, i2.iter()); + EXPECT_EQ(10UL, i2.size()); + + // dereferenceable - incrementable + ring_iterator i3(A); + EXPECT_EQ(true, (std::is_reference::value)); + EXPECT_EQ(true, (std::is_same&, decltype(++i3)>::value)); + EXPECT_EQ(true, (std::is_reference::value)); + + // more practical + ring_iterator i4(A); + ring_iterator i5(A, 9); + EXPECT_EQ(A[0], *i4); + EXPECT_EQ(&A[1], (++i4).iter()); + // check loop + EXPECT_EQ(A[9], *i5); + EXPECT_EQ(&A[0], (++i5).iter()); + } + + // Legacy input iterator atomic + TEST(Tring_iterator, LegacyInputIterator_atomic) { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator i1(A), i2(A), i3(A, 1); + + struct T { int m; }; + T B[5] { {0}, {1}, {2}, {3}, {4}}; + ring_iterator it(B); + + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_samem)>::value)); + EXPECT_EQ (true, (std::is_same&, decltype(++i1)>::value)); + EXPECT_EQ (true, (std::is_same::value)); + + // more practical + EXPECT_EQ (true, i1 == i2); + EXPECT_EQ (true, i1 != i3); + EXPECT_EQ (0, *i1); + EXPECT_EQ (0, it->m); + EXPECT_EQ (true, (++i1 == i3)); + EXPECT_EQ (1, *i1++); + EXPECT_EQ (2, *i1); + } + + // Legacy input iterator atomic + TEST(Tring_iterator, LegacyOutputIterator_atomic) { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator it(A); + + EXPECT_EQ (true, (std::is_assignable::value)); + EXPECT_EQ (true, (std::is_assignable::value)); + + // more practical + *it = 42; + EXPECT_EQ (42, A[0]); + *it++ = 7; + EXPECT_EQ (7, A[0]); + EXPECT_EQ (&A[1], it.iter()); + } + + // Legacy forward iterator atomic + TEST(Tring_iterator, LegacyForwardIterator_atomic) + { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator it(A); + + EXPECT_EQ (0, *it++); + EXPECT_EQ (1, *it); + } + + // Legacy bidirectional iterator atomic + TEST(Tring_iterator, LegacyBidirectionalIterator_atomic) { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator it(A); + + EXPECT_EQ (true, (std::is_same&, decltype(--it)>::value)); + EXPECT_EQ (true, (std::is_same, decltype(it--)>::value)); + EXPECT_EQ (true, (std::is_same::value)); + + // more practical + ring_iterator i1(A), i2(A, 9); + EXPECT_EQ (9, *i2--); // check loop also + EXPECT_EQ (8, *i2); + EXPECT_EQ (0, *i1--); // check loop also + EXPECT_EQ (9, *i1); + } + + // Legacy random access iterator atomic + TEST(Tring_iterator, LegacyRandomAccessIterator_atomic) { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator it1(A), it2(A, 7); + + EXPECT_EQ (true, (std::is_same&, decltype(it1 += 7)>::value)); + EXPECT_EQ (true, (std::is_same, decltype(it1 + 7)>::value)); + EXPECT_EQ (true, (std::is_same, decltype(7 + it1)>::value)); + + EXPECT_EQ (true, (std::is_same&, decltype(it1 -= 7)>::value)); + EXPECT_EQ (true, (std::is_same, decltype(it1 - 7)>::value)); + + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same::value)); + + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same it2)>::value)); + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same= it2)>::value)); + + // more practical + ring_iterator i1(A), i2(A); + i1 += 7; + EXPECT_EQ (7, *i1); + i1 -= 7; + EXPECT_EQ (0, *i1); + i1 += 11; + EXPECT_EQ (1, *i1); + i1 -= 2; + EXPECT_EQ (9, *i1); + + EXPECT_EQ (7, *(i2+7)); + EXPECT_EQ (7, *(7+i2)); + EXPECT_EQ (1, *(i2+11)); + EXPECT_EQ (1, *(11+i2)); + EXPECT_EQ (7, *(i1-2)); + EXPECT_EQ (8, *(i2-2)); + + EXPECT_EQ (9, (i1 - i2)); + EXPECT_EQ (1, (i2 - i1)); // loop + + } + } diff --git a/test/tests/span.cpp b/test/tests/span.cpp new file mode 100644 index 0000000..5c965b4 --- /dev/null +++ b/test/tests/span.cpp @@ -0,0 +1,274 @@ +/*! + * \file span.cpp + * \brief + * Unit tests for span + * + * \copyright Copyright (C) 2020 Christos Choutouridis + * + *
License
+ * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *
+ * + */ +#include +#include + +#include +#include +#include + +// tests from https://github.com/tcbrindle/span/blob/master/test/test_span.cpp + +namespace test_span { + using namespace tbx; + + TEST (Tspan, default_constructors) { + EXPECT_EQ (true, ( std::is_nothrow_default_constructible>::value)); + EXPECT_EQ (true, ( std::is_nothrow_default_constructible>::value)); + EXPECT_EQ (true, (!std::is_nothrow_default_constructible>::value)); + + constexpr span s1{}; + + EXPECT_EQ (0UL, s1.size()); + EXPECT_EQ (nullptr, s1.data()); + EXPECT_EQ (s1.begin(), s1.end()); + + constexpr span s2{}; + + EXPECT_EQ (0UL, s2.size()); + EXPECT_EQ (nullptr, s2.data()); + EXPECT_EQ (s2.begin(), s2.end()); + } + + TEST (Tspan, pointer_constructors) { + // pointer length + EXPECT_EQ (true, (std::is_constructible, int*, int>::value)); + EXPECT_EQ (true, (std::is_constructible, int*, int>::value)); + EXPECT_EQ (true, (std::is_constructible, const int*, int>::value)); + EXPECT_EQ (true, (std::is_constructible, int*, int>::value)); + EXPECT_EQ (true, (std::is_constructible, int*, int>::value)); + EXPECT_EQ (true, (std::is_constructible, const int*, int>::value)); + + int arr[] = {1, 2, 3}; + + // dynamic size + span s1(arr, 3); + EXPECT_EQ (3UL, s1.size()); + EXPECT_EQ (arr, s1.data()); + EXPECT_EQ (std::begin(arr), s1.begin()); + EXPECT_EQ (std::end(arr), s1.end()); + + // fixed size + span s2(arr, 3); + EXPECT_EQ (3UL, s2.size()); + EXPECT_EQ (arr, s2.data()); + EXPECT_EQ (std::begin(arr), s2.begin()); + EXPECT_EQ (std::end(arr), s2.end()); + + // pointer pointer + EXPECT_EQ (true, (std::is_constructible, int*, int*>::value)); + EXPECT_EQ (true, (std::is_constructible, float*, float*>::value)); + EXPECT_EQ (true, (std::is_constructible, int*, int*>::value)); + EXPECT_EQ (true, (std::is_constructible, float*, float*>::value)); + + // dynamic size + span s3(arr, arr + 3); + EXPECT_EQ (3UL, s3.size()); + EXPECT_EQ (arr, s3.data()); + EXPECT_EQ (std::begin(arr), s3.begin()); + EXPECT_EQ (std::end(arr), s3.end()); + + // fixed size + span s4(arr, arr + 3); + EXPECT_EQ (3UL, s4.size()); + EXPECT_EQ (arr, s4.data()); + EXPECT_EQ (std::begin(arr), s4.begin()); + EXPECT_EQ (std::end(arr), s4.end()); + } + + TEST (Tspan, C_array_constructors) { + using int_array_t = int[3]; + using float_array_t = float[3]; + + EXPECT_EQ (true, ( std::is_nothrow_constructible, int_array_t&>::value)); + EXPECT_EQ (true, (!std::is_constructible, int_array_t const&>::value)); + EXPECT_EQ (true, (!std::is_constructible, float_array_t>::value)); + + EXPECT_EQ (true, ( std::is_nothrow_constructible, int_array_t&>::value)); + EXPECT_EQ (true, ( std::is_nothrow_constructible, int_array_t const&>::value)); + EXPECT_EQ (true, (!std::is_constructible, float_array_t>::value)); + + EXPECT_EQ (true, ( std::is_nothrow_constructible, int_array_t&>::value)); + EXPECT_EQ (true, (!std::is_constructible, int_array_t const&>::value)); + EXPECT_EQ (true, (!std::is_constructible, float_array_t&>::value)); + + EXPECT_EQ (true, ( std::is_nothrow_constructible, int_array_t&>::value)); + EXPECT_EQ (true, ( std::is_nothrow_constructible, int_array_t const&>::value)); + EXPECT_EQ (true, (!std::is_constructible, float_array_t>::value)); + + EXPECT_EQ (true, (!std::is_constructible, int_array_t&>::value)); + EXPECT_EQ (true, (!std::is_constructible, int_array_t const&>::value)); + EXPECT_EQ (true, (!std::is_constructible, float_array_t&>::value)); + + EXPECT_EQ (true, (!std::is_constructible, int_array_t&>::value)); + EXPECT_EQ (true, (!std::is_constructible, int_array_t const&>::value)); + EXPECT_EQ (true, (!std::is_constructible, float_array_t&>::value)); + + int arr[] = {1, 2, 3}; + + // non-const dynamic size + span s1{arr}; + EXPECT_EQ (s1.size(), 3UL); + EXPECT_EQ (s1.data(), arr); + EXPECT_EQ (s1.begin(), std::begin(arr)); + EXPECT_EQ (s1.end(), std::end(arr)); + + // non-const dynamic size + span s2{arr}; + EXPECT_EQ (s2.size(), 3UL); + EXPECT_EQ (s2.data(), arr); + EXPECT_EQ (s2.begin(), std::begin(arr)); + EXPECT_EQ (s2.end(), std::end(arr)); + + // non-const fixed size + span s3{arr}; + EXPECT_EQ (s3.size(), 3UL); + EXPECT_EQ (s3.data(), arr); + EXPECT_EQ (s3.begin(), std::begin(arr)); + EXPECT_EQ (s3.end(), std::end(arr)); + + // non-const fixed size + span s4{arr}; + EXPECT_EQ (s4.size(), 3UL); + EXPECT_EQ (s4.data(), arr); + EXPECT_EQ (s4.begin(), std::begin(arr)); + EXPECT_EQ (s4.end(), std::end(arr)); + } + + TEST (Tspan, array_constructors) { + using int_array_t = std::array; + using float_array_t = std::array; + using zero_array_t = std::array; + + EXPECT_EQ (true, ( std::is_nothrow_constructible, int_array_t&>::value)); + EXPECT_EQ (true, (!std::is_constructible, int_array_t const&>::value)); + EXPECT_EQ (true, (!std::is_constructible, float_array_t>::value)); + + EXPECT_EQ (true, ( std::is_nothrow_constructible, int_array_t&>::value)); + EXPECT_EQ (true, ( std::is_nothrow_constructible, int_array_t const&>::value)); + EXPECT_EQ (true, (!std::is_constructible, float_array_t const&>::value)); + + EXPECT_EQ (true, ( std::is_nothrow_constructible, int_array_t&>::value)); + EXPECT_EQ (true, (!std::is_constructible, int_array_t const&>::value)); + EXPECT_EQ (true, (!std::is_constructible, float_array_t>::value)); + + EXPECT_EQ (true, ( std::is_nothrow_constructible, int_array_t&>::value)); + EXPECT_EQ (true, ( std::is_nothrow_constructible, int_array_t const&>::value)); + EXPECT_EQ (true, (!std::is_constructible, float_array_t const&>::value)); + + EXPECT_EQ (true, (!std::is_constructible, int_array_t&>::value)); + EXPECT_EQ (true, (!std::is_constructible, int_array_t const&>::value)); + EXPECT_EQ (true, (!std::is_constructible, float_array_t const&>::value)); + + EXPECT_EQ (true, (!std::is_constructible, int_array_t&>::value)); + EXPECT_EQ (true, (!std::is_constructible, int_array_t const&>::value)); + EXPECT_EQ (true, (!std::is_constructible, float_array_t&>::value)); + + EXPECT_EQ (true, ( std::is_constructible, zero_array_t&>::value)); + EXPECT_EQ (true, (!std::is_constructible, const zero_array_t&>::value)); + EXPECT_EQ (true, ( std::is_constructible, zero_array_t&>::value)); + EXPECT_EQ (true, ( std::is_constructible, const zero_array_t&>::value)); + + EXPECT_EQ (true, ( std::is_constructible, zero_array_t&>::value)); + EXPECT_EQ (true, (!std::is_constructible, const zero_array_t&>::value)); + EXPECT_EQ (true, ( std::is_constructible, zero_array_t&>::value)); + EXPECT_EQ (true, ( std::is_constructible, const zero_array_t&>::value)); + + + int_array_t arr = {1, 2, 3}; + + // non-const, dynamic size + span s1{arr}; + EXPECT_EQ(s1.size(), 3UL); + EXPECT_EQ(s1.data(), arr.data()); + EXPECT_EQ(s1.begin(), arr.data()); + EXPECT_EQ(s1.end(), arr.data() + 3); + + + //const, dynamic size + span s2{arr}; + EXPECT_EQ(s2.size(), 3UL); + EXPECT_EQ(s2.data(), arr.data()); + EXPECT_EQ(s2.begin(), arr.data()); + EXPECT_EQ(s2.end(), arr.data() + 3); + + + // non-const, static size + span s3{arr}; + EXPECT_EQ(s3.size(), 3UL); + EXPECT_EQ(s3.data(), arr.data()); + EXPECT_EQ(s3.begin(), arr.data()); + EXPECT_EQ(s3.end(), arr.data() + 3); + + // const, dynamic size + span s4{arr}; + EXPECT_EQ(s4.size(), 3UL); + EXPECT_EQ(s4.data(), arr.data()); + EXPECT_EQ(s4.begin(), arr.data()); + EXPECT_EQ(s4.end(), arr.data() + 3); + } + +// TEST (Tspan, containter_constructors) { +// using container_t = tbx::deque; +// +// EXPECT_EQ (true, ( std::is_constructible, container_t&>::value)); +// EXPECT_EQ (true, (!std::is_constructible, const container_t&>::value)); +// +// EXPECT_EQ (true, ( std::is_constructible, container_t&>::value)); +// EXPECT_EQ (true, ( std::is_constructible, const container_t&>::value)); +// +// EXPECT_EQ (true, (!std::is_constructible, container_t&>::value)); +// EXPECT_EQ (true, (!std::is_constructible, const container_t&>::value)); +// +// EXPECT_EQ (true, (!std::is_constructible, container_t&>::value)); +// EXPECT_EQ (true, (!std::is_constructible, const container_t&>::value)); +// +// container_t cont = {1, 2, 3}; +// const container_t ccont = {1, 2, 3}; +// +// // non-const, dynamic size +// span s1(cont); +// EXPECT_EQ(s1.size(), 3UL); +// EXPECT_EQ(s1.data(), cont.data()); +// EXPECT_EQ(s1.begin(), cont.data()); +// EXPECT_EQ(s1.end(), cont.data() + 3); +// +// +// //const, dynamic size +// span s2(cont); +// EXPECT_EQ(s2.size(), 3UL); +// EXPECT_EQ(s2.data(), cont.data()); +// EXPECT_EQ(s2.begin(), cont.data()); +// EXPECT_EQ(s2.end(), cont.data() + 3); +// +// } + +}