/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright 2015-2017 Danny Robson */ #ifndef CRUFT_UTIL_VIEW_HPP #define CRUFT_UTIL_VIEW_HPP #include "cast.hpp" #include "debug.hpp" #include "types/traits.hpp" #include #include #include #include #include #include namespace util { template struct view { public: //--------------------------------------------------------------------- using value_type = typename std::iterator_traits< remove_restrict_t >::value_type; //--------------------------------------------------------------------- constexpr view (const IteratorA &first, const IteratorB &last) noexcept: m_begin (first), m_end (last) { ; } //--------------------------------------------------------------------- // explicitly cater for the char array case so that we don't // accidentally include the trailing null in the data. template view (const char (&value)[N]): view (std::begin (value), std::begin (value) + N - 1) { static_assert (N > 0); } //--------------------------------------------------------------------- template view (const ValueT(&value)[N]): view (std::begin (value), std::end (value)) { ; } //--------------------------------------------------------------------- constexpr view (const view &rhs) noexcept: view (rhs.m_begin, rhs.m_end) { ; } //--------------------------------------------------------------------- // technically we could get away without explicitly defining a move // constructor here, but by nulling rhs we can more easily use this // class as a base for unique owning pointers without exposing the // begin/end data members to them directly. constexpr view (view &&rhs) noexcept: view (std::move (rhs.m_begin), std::move (rhs.m_end)) { ; } //--------------------------------------------------------------------- // allow null construction of views where IteratorT is constructible // from nullptr_t // // ideally we would avoid exposing this as it promotes use of nulls but // it simplifies construction of views that are data members of classes // when we may not immediately know the values we should contain. constexpr view (std::nullptr_t) noexcept: view {nullptr,nullptr} { ; } //--------------------------------------------------------------------- template constexpr explicit view (ContainerT &klass): view (std::begin (klass), std::end (klass)) { ; } //--------------------------------------------------------------------- template constexpr explicit view (const ContainerT &klass): view (std::begin (klass), std::end (klass)) { ; } //--------------------------------------------------------------------- view& operator= (const view &rhs) noexcept { m_begin = rhs.m_begin; m_end = rhs.m_end; return *this; } //--------------------------------------------------------------------- view& operator= (view &&rhs) noexcept { m_begin = rhs.m_begin; m_end = rhs.m_end; rhs.m_begin = IteratorA{}; rhs.m_end = IteratorB{}; return *this; }; /////////////////////////////////////////////////////////////////////// constexpr IteratorA begin (void) noexcept { return m_begin; } constexpr IteratorB end (void) noexcept { return m_end; } //--------------------------------------------------------------------- constexpr const IteratorA begin (void) const noexcept { return cbegin (); } constexpr const IteratorB end (void) const noexcept { return cend (); } //--------------------------------------------------------------------- constexpr const IteratorA cbegin (void) const noexcept { return m_begin; } constexpr const IteratorB cend (void) const noexcept { return m_end; } //--------------------------------------------------------------------- auto data (void) { return begin (); } auto data (void) const { return begin (); } /////////////////////////////////////////////////////////////////////// constexpr bool empty (void) const noexcept { return m_begin == m_end; } //--------------------------------------------------------------------- constexpr auto size (void) const noexcept { return sign_cast (std::distance (m_begin, m_end)); } //--------------------------------------------------------------------- [[gnu::warn_unused_result]] constexpr auto redim (int count) const { CHECK_GT (count, 0); if (count > size ()) throw std::invalid_argument ("redim to higher size not allowed"); return view { m_begin, m_begin + count }; }; //--------------------------------------------------------------------- // returns a view that has the same end iterator, but the provided // begin iterator. // // the new begin iterator must be reachable by incrementing the current // begin iterator but lie before reaching the end iterator. ie, in the // middle. // // useful for resizing views after options which incrementally consume // the referenced data. [[gnu::warn_unused_result]] constexpr auto increment (IteratorA _begin) const { return view { _begin, m_end }; } //--------------------------------------------------------------------- [[gnu::warn_unused_result]] constexpr auto increment (int count) const { IteratorA next; std::advance (next, count); return increment (next); } //--------------------------------------------------------------------- template [[gnu::warn_unused_result]] constexpr auto decrement (IteratorC _end) { return view { m_begin, _end }; }; /////////////////////////////////////////////////////////////////////// constexpr value_type& operator[] (size_t idx)& noexcept { auto it = begin (); std::advance (it, idx); return *it; } //--------------------------------------------------------------------- constexpr const value_type& operator[] (size_t idx) const& noexcept { auto it = begin (); std::advance (it, idx); return *it; } private: /////////////////////////////////////////////////////////////////////// IteratorA m_begin; IteratorB m_end; }; /////////////////////////////////////////////////////////////////////////// template auto make_view (const ValueT (&arr)[N]) { return util::view (arr + 0, arr + N); } //------------------------------------------------------------------------- template auto make_view (ContainerT &t) { return util::view { std::begin (t), std::end (t) }; } //------------------------------------------------------------------------- template auto make_view (const ContainerT &t) { return util::view { std::cbegin (t), std::cend (t) }; } //------------------------------------------------------------------------- // disable the possibility of creating a view to a temporary. note that // this only works if an lval version has already been defined otherwise // universal reference rules will capture both lval and rval here. template auto make_view (ContainerT&&) = delete; /////////////////////////////////////////////////////////////////////////// template auto make_cview (const ContainerT &t) { return util::view { std::cbegin (t), std::cend (t) }; } //------------------------------------------------------------------------- template auto make_view (IteratorA first, IteratorB last) { return view {first, last}; } //------------------------------------------------------------------------- template auto make_cview (ValueT *first, ValueT *last) { return view {first, last}; } /////////////////////////////////////////////////////////////////////////// inline view make_view (const char *str) { return { str, str + strlen (str) }; } //------------------------------------------------------------------------- inline view make_view (char *str) { return { str, str + strlen (str) }; } //------------------------------------------------------------------------- template view make_view (const std::basic_string &str) { return { std::data (str), std::data (str) + std::size (str) }; } //------------------------------------------------------------------------- template view make_view (std::basic_string &str) { return { std::data (str), std::data (str) + std::size (str) }; } //------------------------------------------------------------------------- template view make_view (const std::basic_string&&) = delete; //------------------------------------------------------------------------- template view make_view (std::basic_string&&) = delete; /////////////////////////////////////////////////////////////////////////// template constexpr bool operator== (const view &a, const view &b) { return a.size () == b.size () && std::equal (std::begin (a), std::end (a), std::begin (b)); } //------------------------------------------------------------------------- // defer equality to the view/view operator by way of make_view template constexpr bool operator== (const view &a, const ValueT &b) { return a == make_view (b); } //------------------------------------------------------------------------- // reverse the arguments and forward to the above operator. we formumlate // equality this way to avoid implementing the operator twice for each // weird case. template constexpr bool operator== (const ValueT &a, const view &b) { return b == a; } /////////////////////////////////////////////////////////////////////////// template constexpr bool operator!= (const util::view &a, const ValueT &b) { return !(a == b); } //------------------------------------------------------------------------- template constexpr bool operator!= (const ValueT &a, const util::view &b) { return !(a == b); } /////////////////////////////////////////////////////////////////////////// template std::ostream& operator<< (std::ostream &os, view val) { std::copy ( std::cbegin (val), std::cend (val), std::ostream_iterator (os) ); return os; } /////////////////////////////////////////////////////////////////////////// /// a basic stringlike comparison operator that behaves as /// std::string::compare would. /// /// provided so that the common case of stringlike views can be used in a /// std::map and similar without a great deal of work. inline bool operator< (util::view a, util::view b) { const auto la = std::size (a); const auto lb = std::size (b); const auto res = strncmp ( std::data (a), std::data (b), util::min (la, lb) ); return res < 0 || (res == 0 && la < lb); } } #endif