/* * 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 BeginT &first, const EndT &last) noexcept: m_begin (first), m_end (last) { ; } //--------------------------------------------------------------------- template ,void>> view ( const BeginT &_begin, CountT _size ): view (_begin, _begin + _size) { ; } //--------------------------------------------------------------------- template < typename ValueT, typename = std::enable_if_t< std::is_same_v && std::is_same_v > > view (const view &rhs): m_begin (rhs.begin ()), m_end (rhs.end ()) { ; } //--------------------------------------------------------------------- // 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 view (std::basic_string &val): view (std::data (val), std::data (val) + std::size (val)) { ; } //--------------------------------------------------------------------- // non-contigous containers should use their begin/end iterators // directly template < typename ContainerT, typename std::enable_if_t,ContainerT*> = nullptr > constexpr explicit view ( ContainerT &klass ): view (std::begin (klass), std::end (klass)) { ; } //--------------------------------------------------------------------- // contiguous containers are often used with pointers to their contents // for other operations so we directly construct them using pointers to // their data for user convenience. template < typename ContainerT, typename std::enable_if_t,ContainerT*> = nullptr > constexpr explicit view ( ContainerT &klass ): view (std::data (klass), std::data (klass) + std::size (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 = BeginT{}; rhs.m_end = EndT{}; return *this; }; /////////////////////////////////////////////////////////////////////// constexpr BeginT begin (void) noexcept { return m_begin; } constexpr EndT end (void) noexcept { return m_end; } //--------------------------------------------------------------------- constexpr const BeginT begin (void) const noexcept { return cbegin (); } constexpr const EndT end (void) const noexcept { return cend (); } //--------------------------------------------------------------------- constexpr const BeginT cbegin (void) const noexcept { return m_begin; } constexpr const EndT 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 }; }; //--------------------------------------------------------------------- util::view operator- (util::view prefix) const { CHECK_EQ (prefix.begin (), begin ()); return { prefix.end (), end () }; } /////////////////////////////////////////////////////////////////////// template < typename ValueT, typename = std::enable_if_t< std::is_pointer_v && sizeof (*std::declval ()) == sizeof (ValueT) , void > > view cast (void) const { return { reinterpret_cast (m_begin), reinterpret_cast (m_end) }; } /////////////////////////////////////////////////////////////////////// constexpr auto& operator[] (size_t idx)& noexcept { auto it = begin (); std::advance (it, idx); return *it; } //--------------------------------------------------------------------- constexpr auto& operator[] (size_t idx) const& noexcept { auto it = begin (); std::advance (it, idx); return *it; } private: /////////////////////////////////////////////////////////////////////// BeginT m_begin; EndT m_end; }; //------------------------------------------------------------------------- template view (const ValueT(&)[N]) -> view; //------------------------------------------------------------------------- template < typename IteratorT, typename SizeT, typename = std::enable_if_t< std::is_integral_v,void > > view (IteratorT, SizeT) -> view; //------------------------------------------------------------------------- template < typename ContainerT, typename = std::enable_if_t, void> > view (ContainerT&) -> view< typename ContainerT::value_type*, typename ContainerT::value_type* >; //------------------------------------------------------------------------- template < typename ContainerT, typename = std::enable_if_t, void> > view (ContainerT&) -> view< decltype (std::begin (std::declval ())), decltype (std::end (std::declval ())) >; /////////////////////////////////////////////////////////////////////////// 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 make_view (t); //return util::view { std::cbegin (t), std::cend (t) }; } //------------------------------------------------------------------------- template auto make_view (BeginT first, EndT 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