Skip to content

Instantly share code, notes, and snippets.

@compnerd
Last active December 18, 2015 00:09
Show Gist options
  • Save compnerd/5694186 to your computer and use it in GitHub Desktop.
Save compnerd/5694186 to your computer and use it in GitHub Desktop.
An implementation of a reverse iteration adaptor for range based for-loops in C++11.
/* vim: set et ft=cpp.doxygen sts=2 sw=2 ts=8 : */
/**
* Copyright © 2013 Saleem Abdulrasool <compnerd@compnerd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**/
/*!
* \note § 6.5.4 The range-based for statement [stmt.ranged]
*
* A range-based for statement is equivalent to:
*
* \code{c++}
* {
* auto && __range = range-init;
* for ( auto __begin = begin-expr,
* __end = end-expr;
* __begin != __end;
* ++__begin ) {
* for-range-declaration = *__begin;
* statement
* }
* }
* \endcode
*
* where \c __range, \c __begin, and \c __end are variables defined for
* exposition only, and \c _RangeT is the type of the expression, and
* \c begin-expr and \c end-expr are determined as follows:
*
* - if \c _RangeT is an array type, \c begin-expr and \c end-expr are
* \c __range and \c __range \c + \c __bound, respectively, where \c __bound
* is the array bound. If \c _RangeT is an array of unknown size or an array
* of incomplete type, the program is ill-formed.
* - otherwise, \c begin-expr and \c end-expr are \c begin(__range) and
* \c end(__range), respectively, where \c begin and \c end are looked up
* with argument-dependent lookup. For the purposes of this name lookup,
* namespace \c std is an associated namespace.
*/
#include <iterator>
#include <type_traits>
template <typename type_, int>
struct reverse_iteration_proxy;
template <typename type_, size_t bound_>
class reverse_iteration_proxy<type_[bound_], 0> {
private:
type_ (&range_)[bound_];
public:
typedef std::reverse_iterator<type_ *> iterator;
reverse_iteration_proxy(type_ (&range)[bound_]) : range_(range) { }
reverse_iteration_proxy(type_ (&&)[bound_]) = delete;
iterator begin() const noexcept { return iterator(range_ + bound_); }
iterator end() const noexcept { return iterator(range_); }
};
template <typename type_>
class reverse_iteration_proxy<type_, 1> {
private:
type_ & range_;
public:
typedef decltype(range_.rbegin()) iterator;
reverse_iteration_proxy(type_ & range) : range_(range) { }
reverse_iteration_proxy(type_ &&) = delete;
iterator begin() const noexcept { return range_.rbegin(); }
iterator end() const noexcept { return range_.rend(); }
};
template <typename type_>
class reverse_iteration_proxy<type_, 2> {
private:
type_ & range_;
public:
typedef std::reverse_iterator<decltype(std::end(range_))> iterator;
reverse_iteration_proxy(type_ & range) : range_(range) { }
reverse_iteration_proxy(type_ &&) = delete;
iterator begin() const noexcept { return iterator(std::end(range_)); }
iterator end() const noexcept { return iterator(std::begin(range_)); }
};
template <typename type_, size_t bound_>
inline constexpr reverse_iteration_proxy<type_[bound_], 0>
select_reverse_iteration_proxy(type_ (&range)[bound_], int) {
return reverse_iteration_proxy<type_[bound_], 0>{ range };
}
template <typename type_, typename = decltype(std::declval<type_>().rbegin())>
inline constexpr reverse_iteration_proxy<type_, 1>
select_reverse_iteration_proxy(type_ & range, int) {
static_assert(std::is_same<decltype(std::declval<type_>().rbegin()),
decltype(std::declval<type_>().rend())>::value,
"iterator type mismatch");
return reverse_iteration_proxy<type_, 1>{ range };
}
template <typename type_>
inline constexpr reverse_iteration_proxy<type_, 2>
select_reverse_iteration_proxy(type_ & range, long) {
return reverse_iteration_proxy<type_, 2>{ range };
}
template <typename type_>
inline constexpr auto reverse(type_ & range) ->
decltype(select_reverse_iteration_proxy(range, 0)) {
return select_reverse_iteration_proxy(range, 0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment