[llvm-commits] [lld] r172512 - in /lld/trunk: include/lld/Core/range.h unittests/CMakeLists.txt unittests/RangeTest.cpp
Michael J. Spencer
bigcheesegs at gmail.com
Mon Jan 14 22:55:26 PST 2013
Author: mspencer
Date: Tue Jan 15 00:55:25 2013
New Revision: 172512
URL: http://llvm.org/viewvc/llvm-project?rev=172512&view=rev
Log:
[Core] Add iterator ranges.
This is based on code by Jeffrey Yasskin. It has been modified to compile
with MSVC and reformated to LLVM style.
Added:
lld/trunk/include/lld/Core/range.h
lld/trunk/unittests/RangeTest.cpp
Modified:
lld/trunk/unittests/CMakeLists.txt
Added: lld/trunk/include/lld/Core/range.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/include/lld/Core/range.h?rev=172512&view=auto
==============================================================================
--- lld/trunk/include/lld/Core/range.h (added)
+++ lld/trunk/include/lld/Core/range.h Tue Jan 15 00:55:25 2013
@@ -0,0 +1,739 @@
+//===-- lld/Core/range.h - Iterator ranges ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Iterator range type based on c++1y range proposal.
+///
+/// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3350.html
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ADT_RANGE_H
+#define LLD_ADT_RANGE_H
+
+#include "llvm/Support/Compiler.h"
+
+#include <cassert>
+#include <array>
+#include <iterator>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+namespace lld {
+// Nothing in this namespace is part of the exported interface.
+namespace detail {
+using std::begin;
+using std::end;
+/// Used as the result type of undefined functions.
+struct undefined {};
+
+template <typename R> class begin_result {
+ template <typename T> static auto check(T &&t) -> decltype(begin(t));
+ static undefined check(...);
+public:
+ typedef decltype(check(std::declval<R>())) type;
+};
+
+template <typename R> class end_result {
+ template <typename T> static auto check(T &&t) -> decltype(end(t));
+ static undefined check(...);
+public:
+ typedef decltype(check(std::declval<R>())) type;
+};
+
+// Things that begin and end work on, in compatible ways, are
+// ranges. [stmt.ranged]
+template <typename R>
+struct is_range : std::is_same<typename detail::begin_result<R>::type,
+ typename detail::end_result<R>::type> {};
+
+// This currently requires specialization and doesn't work for
+// detecting \c range<>s or iterators. We should add
+// \c contiguous_iterator_tag to fix that.
+template <typename R> struct is_contiguous_range : std::false_type {};
+template <typename R>
+struct is_contiguous_range<R &> : is_contiguous_range<R> {};
+template <typename R>
+struct is_contiguous_range <R &&> : is_contiguous_range<R> {};
+template <typename R>
+struct is_contiguous_range<const R> : is_contiguous_range<R> {};
+
+template <typename T, size_t N>
+struct is_contiguous_range<T[N]> : std::true_type {};
+template <typename T, size_t N>
+struct is_contiguous_range<const T[N]> : std::true_type {};
+template <typename T, size_t N>
+struct is_contiguous_range<std::array<T, N> > : std::true_type {};
+template <typename charT, typename traits, typename Allocator>
+struct is_contiguous_range<
+ std::basic_string<charT, traits, Allocator> > : std::true_type {};
+template <typename T, typename Allocator>
+struct is_contiguous_range<std::vector<T, Allocator> > : std::true_type {};
+
+// Removes cv qualifiers from all levels of a multi-level pointer
+// type, not just the type level.
+template <typename T> struct remove_all_cv_ptr {
+ typedef T type;
+};
+template <typename T> struct remove_all_cv_ptr<T *> {
+ typedef typename remove_all_cv_ptr<T>::type *type;
+};
+template <typename T> struct remove_all_cv_ptr<const T> {
+ typedef typename remove_all_cv_ptr<T>::type type;
+};
+template <typename T> struct remove_all_cv_ptr<volatile T> {
+ typedef typename remove_all_cv_ptr<T>::type type;
+};
+template <typename T> struct remove_all_cv_ptr<const volatile T> {
+ typedef typename remove_all_cv_ptr<T>::type type;
+};
+
+template <typename From, typename To>
+struct conversion_preserves_array_indexing : std::false_type {};
+
+template <typename FromVal, typename ToVal>
+struct conversion_preserves_array_indexing<FromVal *,
+ ToVal *> : std::integral_constant<
+ bool, std::is_convertible<FromVal *, ToVal *>::value &&
+ std::is_same<typename remove_all_cv_ptr<FromVal>::type,
+ typename remove_all_cv_ptr<ToVal>::type>::value> {};
+
+template <typename T>
+LLVM_CONSTEXPR auto adl_begin(T &&t) -> decltype(begin(t)) {
+ return begin(std::forward<T>(t));
+}
+
+template <typename T> LLVM_CONSTEXPR auto adl_end(T &&t) -> decltype(end(t)) {
+ return end(std::forward<T>(t));
+}
+} // end namespace detail
+
+/// A \c std::range<Iterator> represents a half-open iterator range
+/// built from two iterators, \c 'begin', and \c 'end'. If \c end is
+/// not reachable from \c begin, the behavior is undefined.
+///
+/// The mutability of elements of the range is controlled by the
+/// Iterator argument. Instantiate
+/// <code>range<<var>Foo</var>::iterator></code> or
+/// <code>range<<var>T</var>*></code>, or call
+/// <code>make_range(<var>non_const_container</var>)</code>, and you
+/// get a mutable range. Instantiate
+/// <code>range<<var>Foo</var>::const_iterator></code> or
+/// <code>range<const <var>T</var>*></code>, or call
+/// <code>make_range(<var>const_container</var>)</code>, and you get a
+/// constant range.
+///
+/// \todo Inherit from std::pair<Iterator, Iterator>?
+///
+/// \todo This interface contains some functions that could be
+/// provided as free algorithms rather than member functions, and all
+/// of the <code>pop_*()</code> functions could be replaced by \c
+/// slice() at the cost of some extra iterator copies. This makes
+/// them more awkward to use, but makes it easier for users to write
+/// their own types that follow the same interface. On the other hand,
+/// a \c range_facade could be provided to help users write new
+/// ranges, and it could provide the members. Such functions are
+/// marked with a note in their documentation. (Of course, all of
+/// these member functions could be provided as free functions using
+/// the iterator access methods, but one goal here is to allow people
+/// to program without touching iterators at all.)
+template <typename Iterator> class range {
+ Iterator begin_, end_;
+public:
+ /// \name types
+ /// @{
+
+ /// The iterator category of \c Iterator.
+ /// \todo Consider defining range categories. If they don't add
+ /// anything over the corresponding iterator categories, then
+ /// they're probably not worth defining.
+ typedef typename std::iterator_traits<
+ Iterator>::iterator_category iterator_category;
+ /// The type of elements of the range. Not cv-qualified.
+ typedef typename std::iterator_traits<Iterator>::value_type value_type;
+ /// The type of the size of the range and offsets within the range.
+ typedef typename std::iterator_traits<
+ Iterator>::difference_type difference_type;
+ /// The return type of element access methods: \c front(), \c back(), etc.
+ typedef typename std::iterator_traits<Iterator>::reference reference;
+ typedef typename std::iterator_traits<Iterator>::pointer pointer;
+ /// @}
+
+ /// \name constructors
+ /// @{
+
+ /// Creates a range of default-constructed (<em>not</em>
+ /// value-initialized) iterators. For most \c Iterator types, this
+ /// will be an invalid range.
+ range() : begin_(), end_() {}
+
+ /// \pre \c end is reachable from \c begin.
+ /// \post <code>this->begin() == begin && this->end() == end</code>
+ LLVM_CONSTEXPR range(Iterator begin, Iterator end)
+ : begin_(begin), end_(end) {}
+
+ /// \par Participates in overload resolution if:
+ /// - \c Iterator is not a pointer type,
+ /// - \c begin(r) and \c end(r) return the same type, and
+ /// - that type is convertible to \c Iterator.
+ ///
+ /// \todo std::begin and std::end are overloaded between T& and
+ /// const T&, which means that if a container has only a non-const
+ /// begin or end method, then it's ill-formed to pass an rvalue to
+ /// the free function. To avoid that problem, we don't use
+ /// std::forward<> here, so begin() and end() are always called with
+ /// an lvalue. Another option would be to insist that rvalue
+ /// arguments to range() must have const begin() and end() methods.
+ template <typename R> LLVM_CONSTEXPR range(
+ R &&r,
+ typename std::enable_if<
+ !std::is_pointer<Iterator>::value &&
+ detail::is_range<R>::value &&
+ std::is_convertible<typename detail::begin_result<R>::type,
+ Iterator>::value>::type* = 0)
+ : begin_(detail::adl_begin(r)), end_(detail::adl_end(r)) {}
+
+ /// This constructor creates a \c range<T*> from any range with
+ /// contiguous iterators. Because dereferencing a past-the-end
+ /// iterator can be undefined behavior, empty ranges get initialized
+ /// with \c nullptr rather than \c &*begin().
+ ///
+ /// \par Participates in overload resolution if:
+ /// - \c Iterator is a pointer type \c T*,
+ /// - \c begin(r) and \c end(r) return the same type,
+ /// - elements \c i of that type satisfy the invariant
+ /// <code>&*(i + N) == (&*i) + N</code>, and
+ /// - The result of <code>&*begin()</code> is convertible to \c T*
+ /// using only qualification conversions [conv.qual] (since
+ /// pointer conversions stop the pointer from pointing to an
+ /// array element).
+ ///
+ /// \todo The <code>&*(i + N) == (&*i) + N</code> invariant is
+ /// currently impossible to check for user-defined types. We need a
+ /// \c contiguous_iterator_tag to let users assert it.
+ template <typename R> LLVM_CONSTEXPR range(
+ R &&r,
+ typename std::enable_if<
+ std::is_pointer<Iterator>::value &&
+ detail::is_contiguous_range<R>::value
+ // MSVC returns false for this in this context, but not if we lift it out of the
+ // constructor.
+#ifndef _MSC_VER
+ && detail::conversion_preserves_array_indexing<
+ decltype(&*detail::adl_begin(r)), Iterator>::value
+#endif
+ >::type* = 0)
+ : begin_((detail::adl_begin(r) == detail::adl_end(r) &&
+ !std::is_pointer<decltype(detail::adl_begin(r))>::value)
+ // For non-pointers, &*begin(r) is only defined behavior
+ // if there's an element there. Otherwise, use nullptr
+ // since the user can't dereference it anyway. This _is_
+ // detectable.
+ ? nullptr : &*detail::adl_begin(r)),
+ end_(begin_ + (detail::adl_end(r) - detail::adl_begin(r))) {}
+
+ /// @}
+
+ /// \name iterator access
+ /// @{
+ LLVM_CONSTEXPR Iterator begin() const { return begin_; }
+ LLVM_CONSTEXPR Iterator end() const { return end_; }
+ /// @}
+
+ /// \name element access
+ /// @{
+
+ /// \par Complexity:
+ /// O(1)
+ /// \pre \c !empty()
+ /// \returns a reference to the element at the front of the range.
+ LLVM_CONSTEXPR reference front() const { return *begin(); }
+
+ /// \par Ill-formed unless:
+ /// \c iterator_category is convertible to \c
+ /// std::bidirectional_iterator_tag.
+ ///
+ /// \par Complexity:
+ /// O(2) (Involves copying and decrementing an iterator, so not
+ /// quite as cheap as \c front())
+ ///
+ /// \pre \c !empty()
+ /// \returns a reference to the element at the front of the range.
+ LLVM_CONSTEXPR reference back() const {
+ static_assert(
+ std::is_convertible<iterator_category,
+ std::bidirectional_iterator_tag>::value,
+ "Can only retrieve the last element of a bidirectional range.");
+ using std::prev;
+ return *prev(end());
+ }
+
+ /// This method is drawn from scripting language indexing. It
+ /// indexes std::forward from the beginning of the range if the argument
+ /// is positive, or backwards from the end of the array if the
+ /// argument is negative.
+ ///
+ /// \par Ill-formed unless:
+ /// \c iterator_category is convertible to \c
+ /// std::random_access_iterator_tag.
+ ///
+ /// \par Complexity:
+ /// O(1)
+ ///
+ /// \pre <code>abs(index) < size() || index == -size()</code>
+ ///
+ /// \returns if <code>index >= 0</code>, a reference to the
+ /// <code>index</code>'th element in the range. Otherwise, a
+ /// reference to the <code>size()+index</code>'th element.
+ LLVM_CONSTEXPR reference operator[](difference_type index) const {
+ static_assert(std::is_convertible<iterator_category,
+ std::random_access_iterator_tag>::value,
+ "Can only index into a random-access range.");
+ // Less readable construction for constexpr support.
+ return index < 0 ? end()[index]
+ : begin()[index];
+ }
+ /// @}
+
+ /// \name size
+ /// @{
+
+ /// \par Complexity:
+ /// O(1)
+ /// \returns \c true if the range contains no elements.
+ LLVM_CONSTEXPR bool empty() const { return begin() == end(); }
+
+ /// \par Ill-formed unless:
+ /// \c iterator_category is convertible to
+ /// \c std::forward_iterator_tag.
+ ///
+ /// \par Complexity:
+ /// O(1) if \c iterator_category is convertible to \c
+ /// std::random_access_iterator_tag. O(<code>size()</code>)
+ /// otherwise.
+ ///
+ /// \returns the number of times \c pop_front() can be called before
+ /// \c empty() becomes true.
+ LLVM_CONSTEXPR difference_type size() const {
+ static_assert(std::is_convertible<iterator_category,
+ std::forward_iterator_tag>::value,
+ "Calling size on an input range would destroy the range.");
+ return dispatch_size(iterator_category());
+ }
+ /// @}
+
+ /// \name traversal from the beginning of the range
+ /// @{
+
+ /// Advances the beginning of the range by one element.
+ /// \pre \c !empty()
+ void pop_front() { ++begin_; }
+
+ /// Advances the beginning of the range by \c n elements.
+ ///
+ /// \par Complexity:
+ /// O(1) if \c iterator_category is convertible to \c
+ /// std::random_access_iterator_tag, O(<code>n</code>) otherwise.
+ ///
+ /// \pre <code>n >= 0</code>, and there must be at least \c n
+ /// elements in the range.
+ void pop_front(difference_type n) { advance(begin_, n); }
+
+ /// Advances the beginning of the range by at most \c n elements,
+ /// stopping if the range becomes empty. A negative argument causes
+ /// no change.
+ ///
+ /// \par Complexity:
+ /// O(1) if \c iterator_category is convertible to \c
+ /// std::random_access_iterator_tag, O(<code>min(n,
+ /// <var>#-elements-in-range</var>)</code>) otherwise.
+ ///
+ /// \note Could be provided as a free function with little-to-no
+ /// loss in efficiency.
+ void pop_front_upto(difference_type n) {
+ advance_upto(begin_, std::max<difference_type>(0, n), end_,
+ iterator_category());
+ }
+
+ /// @}
+
+ /// \name traversal from the end of the range
+ /// @{
+
+ /// Moves the end of the range earlier by one element.
+ ///
+ /// \par Ill-formed unless:
+ /// \c iterator_category is convertible to
+ /// \c std::bidirectional_iterator_tag.
+ ///
+ /// \par Complexity:
+ /// O(1)
+ ///
+ /// \pre \c !empty()
+ void pop_back() {
+ static_assert(std::is_convertible<iterator_category,
+ std::bidirectional_iterator_tag>::value,
+ "Can only access the end of a bidirectional range.");
+ --end_;
+ }
+
+ /// Moves the end of the range earlier by \c n elements.
+ ///
+ /// \par Ill-formed unless:
+ /// \c iterator_category is convertible to
+ /// \c std::bidirectional_iterator_tag.
+ ///
+ /// \par Complexity:
+ /// O(1) if \c iterator_category is convertible to \c
+ /// std::random_access_iterator_tag, O(<code>n</code>) otherwise.
+ ///
+ /// \pre <code>n >= 0</code>, and there must be at least \c n
+ /// elements in the range.
+ void pop_back(difference_type n) {
+ static_assert(std::is_convertible<iterator_category,
+ std::bidirectional_iterator_tag>::value,
+ "Can only access the end of a bidirectional range.");
+ advance(end_, -n);
+ }
+
+ /// Moves the end of the range earlier by <code>min(n,
+ /// size())</code> elements. A negative argument causes no change.
+ ///
+ /// \par Ill-formed unless:
+ /// \c iterator_category is convertible to
+ /// \c std::bidirectional_iterator_tag.
+ ///
+ /// \par Complexity:
+ /// O(1) if \c iterator_category is convertible to \c
+ /// std::random_access_iterator_tag, O(<code>min(n,
+ /// <var>#-elements-in-range</var>)</code>) otherwise.
+ ///
+ /// \note Could be provided as a free function with little-to-no
+ /// loss in efficiency.
+ void pop_back_upto(difference_type n) {
+ static_assert(std::is_convertible<iterator_category,
+ std::bidirectional_iterator_tag>::value,
+ "Can only access the end of a bidirectional range.");
+ advance_upto(end_, -std::max<difference_type>(0, n), begin_,
+ iterator_category());
+ }
+
+ /// @}
+
+ /// \name creating derived ranges
+ /// @{
+
+ /// Divides the range into two pieces at \c index, where a positive
+ /// \c index represents an offset from the beginning of the range
+ /// and a negative \c index represents an offset from the end.
+ /// <code>range[index]</code> is the first element in the second
+ /// piece. If <code>index >= size()</code>, the second piece
+ /// will be empty. If <code>index < -size()</code>, the first
+ /// piece will be empty.
+ ///
+ /// \par Ill-formed unless:
+ /// \c iterator_category is convertible to
+ /// \c std::forward_iterator_tag.
+ ///
+ /// \par Complexity:
+ /// - If \c iterator_category is convertible to \c
+ /// std::random_access_iterator_tag: O(1)
+ /// - Otherwise, if \c iterator_category is convertible to \c
+ /// std::bidirectional_iterator_tag, \c abs(index) iterator increments
+ /// or decrements
+ /// - Otherwise, if <code>index >= 0</code>, \c index iterator
+ /// increments
+ /// - Otherwise, <code>size() + (size() + index)</code>
+ /// iterator increments.
+ ///
+ /// \returns a pair of adjacent ranges.
+ ///
+ /// \post
+ /// - <code>result.first.size() == min(index, this->size())</code>
+ /// - <code>result.first.end() == result.second.begin()</code>
+ /// - <code>result.first.size() + result.second.size()</code> <code>==
+ /// this->size()</code>
+ ///
+ /// \todo split() could take an arbitrary number of indices and
+ /// return an <code>N+1</code>-element \c tuple<>. This is tricky to
+ /// implement with negative indices in the optimal number of
+ /// increments or decrements for a bidirectional iterator, but it
+ /// should be possible. Do we want it?
+ std::pair<range, range> split(difference_type index) const {
+ static_assert(
+ std::is_convertible<iterator_category,
+ std::forward_iterator_tag>::value,
+ "Calling split on a non-std::forward range would return a useless "
+ "first result.");
+ if (index >= 0) {
+ range second = *this;
+ second.pop_front_upto(index);
+ return make_pair(range(begin(), second.begin()), second);
+ } else {
+ return dispatch_split_neg(index, iterator_category());
+ }
+ }
+
+ /// \returns A sub-range from \c start to \c stop (not including \c
+ /// stop, as usual). \c start and \c stop are interpreted as for
+ /// <code>operator[]</code>, with negative values offsetting from
+ /// the end of the range. Omitting the \c stop argument makes the
+ /// sub-range continue to the end of the original range. Positive
+ /// arguments saturate to the end of the range, and negative
+ /// arguments saturate to the beginning. If \c stop is before \c
+ /// start, returns an empty range beginning and ending at \c start.
+ ///
+ /// \par Ill-formed unless:
+ /// \c iterator_category is convertible to
+ /// \c std::forward_iterator_tag.
+ ///
+ /// \par Complexity:
+ /// - If \c iterator_category is convertible to \c
+ /// std::random_access_iterator_tag: O(1)
+ /// - Otherwise, if \c iterator_category is convertible to \c
+ /// std::bidirectional_iterator_tag, at most <code>min(abs(start),
+ /// size()) + min(abs(stop), size())</code> iterator
+ /// increments or decrements
+ /// - Otherwise, if <code>start >= 0 && stop >= 0</code>,
+ /// <code>max(start, stop)</code> iterator increments
+ /// - Otherwise, <code>size() + max(start', stop')</code>
+ /// iterator increments, where \c start' and \c stop' are the
+ /// offsets of the elements \c start and \c stop refer to.
+ ///
+ /// \note \c slice(start) should be implemented with a different
+ /// overload, rather than defaulting \c stop to
+ /// <code>numeric_limits<difference_type>::max()</code>, because
+ /// using a default would force non-random-access ranges to use an
+ /// O(<code>size()</code>) algorithm to compute the end rather
+ /// than the O(1) they're capable of.
+ range slice(difference_type start, difference_type stop) const {
+ static_assert(
+ std::is_convertible<iterator_category,
+ std::forward_iterator_tag>::value,
+ "Calling slice on a non-std::forward range would destroy the original "
+ "range.");
+ return dispatch_slice(start, stop, iterator_category());
+ }
+
+ range slice(difference_type start) const {
+ static_assert(
+ std::is_convertible<iterator_category,
+ std::forward_iterator_tag>::value,
+ "Calling slice on a non-std::forward range would destroy the original "
+ "range.");
+ return split(start).second;
+ }
+
+ /// @}
+
+private:
+ // advance_upto: should be added to <algorithm>, but I'll use it as
+ // a helper function here.
+ //
+ // These return the number of increments that weren't applied
+ // because we ran into 'limit' (or 0 if we didn't run into limit).
+ static difference_type advance_upto(Iterator &it, difference_type n,
+ Iterator limit, std::input_iterator_tag) {
+ if (n < 0)
+ return 0;
+ while (it != limit && n > 0) {
+ ++it;
+ --n;
+ }
+ return n;
+ }
+
+ static difference_type advance_upto(Iterator &it, difference_type n,
+ Iterator limit,
+ std::bidirectional_iterator_tag) {
+ if (n < 0) {
+ while (it != limit && n < 0) {
+ --it;
+ ++n;
+ }
+ } else {
+ while (it != limit && n > 0) {
+ ++it;
+ --n;
+ }
+ }
+ return n;
+ }
+
+ static difference_type advance_upto(Iterator &it, difference_type n,
+ Iterator limit,
+ std::random_access_iterator_tag) {
+ difference_type distance = limit - it;
+ if (distance < 0)
+ assert(n <= 0);
+ else if (distance > 0)
+ assert(n >= 0);
+
+ if (abs(distance) > abs(n)) {
+ it += n;
+ return 0;
+ } else {
+ it = limit;
+ return n - distance;
+ }
+ }
+
+ // Dispatch functions.
+ difference_type dispatch_size(std::forward_iterator_tag) const {
+ return std::distance(begin(), end());
+ }
+
+ LLVM_CONSTEXPR difference_type dispatch_size(
+ std::random_access_iterator_tag) const {
+ return end() - begin();
+ }
+
+ std::pair<range, range> dispatch_split_neg(difference_type index,
+ std::forward_iterator_tag) const {
+ assert(index < 0);
+ difference_type size = this->size();
+ return split(std::max<difference_type>(0, size + index));
+ }
+
+ std::pair<range, range> dispatch_split_neg(
+ difference_type index, std::bidirectional_iterator_tag) const {
+ assert(index < 0);
+ range first = *this;
+ first.pop_back_upto(-index);
+ return make_pair(first, range(first.end(), end()));
+ }
+
+ range dispatch_slice(difference_type start, difference_type stop,
+ std::forward_iterator_tag) const {
+ if (start < 0 || stop < 0) {
+ difference_type size = this->size();
+ if (start < 0)
+ start = std::max<difference_type>(0, size + start);
+ if (stop < 0)
+ stop = size + stop; // Possibly negative; will be fixed in 2 lines.
+ }
+ stop = std::max<difference_type>(start, stop);
+
+ Iterator first = begin();
+ advance_upto(first, start, end(), iterator_category());
+ Iterator last = first;
+ advance_upto(last, stop - start, end(), iterator_category());
+ return range(first, last);
+ }
+
+ range dispatch_slice(const difference_type start, const difference_type stop,
+ std::bidirectional_iterator_tag) const {
+ Iterator first;
+ if (start < 0) {
+ first = end();
+ advance_upto(first, start, begin(), iterator_category());
+ } else {
+ first = begin();
+ advance_upto(first, start, end(), iterator_category());
+ }
+ Iterator last;
+ if (stop < 0) {
+ last = end();
+ advance_upto(last, stop, first, iterator_category());
+ } else {
+ if (start >= 0) {
+ last = first;
+ if (stop > start)
+ advance_upto(last, stop - start, end(), iterator_category());
+ } else {
+ // Complicated: 'start' walked from the end of the sequence,
+ // but 'stop' needs to walk from the beginning.
+ Iterator dummy = begin();
+ // Walk up to 'stop' increments from begin(), stopping when we
+ // get to 'first', and capturing the remaining number of
+ // increments.
+ difference_type increments_past_start =
+ advance_upto(dummy, stop, first, iterator_category());
+ if (increments_past_start == 0) {
+ // If this is 0, then stop was before start.
+ last = first;
+ } else {
+ // Otherwise, count that many spaces beyond first.
+ last = first;
+ advance_upto(last, increments_past_start, end(), iterator_category());
+ }
+ }
+ }
+ return range(first, last);
+ }
+
+ range dispatch_slice(difference_type start, difference_type stop,
+ std::random_access_iterator_tag) const {
+ const difference_type size = this->size();
+ if (start < 0)
+ start = size + start;
+ if (start < 0)
+ start = 0;
+ if (start > size)
+ start = size;
+
+ if (stop < 0)
+ stop = size + stop;
+ if (stop < start)
+ stop = start;
+ if (stop > size)
+ stop = size;
+
+ return range(begin() + start, begin() + stop);
+ }
+};
+
+/// \name deducing constructor wrappers
+/// \relates std::range
+/// \xmlonly <nonmember/> \endxmlonly
+///
+/// These functions do the same thing as the constructor with the same
+/// signature. They just allow users to avoid writing the iterator
+/// type.
+/// @{
+
+/// \todo I'd like to define a \c make_range taking a single iterator
+/// argument representing the beginning of a range that ends with a
+/// default-constructed \c Iterator. This would help with using
+/// iterators like \c istream_iterator. However, using just \c
+/// make_range() could be confusing and lead to people writing
+/// incorrect ranges of more common iterators. Is there a better name?
+template <typename Iterator>
+LLVM_CONSTEXPR range<Iterator> make_range(Iterator begin, Iterator end) {
+ return range<Iterator>(begin, end);
+}
+
+/// \par Participates in overload resolution if:
+/// \c begin(r) and \c end(r) return the same type.
+template <typename Range> LLVM_CONSTEXPR auto make_range(
+ Range &&r,
+ typename std::enable_if<detail::is_range<Range>::value>::type* = 0)
+ -> range<decltype(detail::adl_begin(r))> {
+ return range<decltype(detail::adl_begin(r))>(r);
+}
+
+/// \par Participates in overload resolution if:
+/// - \c begin(r) and \c end(r) return the same type,
+/// - that type satisfies the invariant that <code>&*(i + N) ==
+/// (&*i) + N</code>, and
+/// - \c &*begin(r) has a pointer type.
+template <typename Range> LLVM_CONSTEXPR auto make_ptr_range(
+ Range &&r,
+ typename std::enable_if<
+ detail::is_contiguous_range<Range>::value &&
+ std::is_pointer<decltype(&*detail::adl_begin(r))>::value>::type* = 0)
+ -> range<decltype(&*detail::adl_begin(r))> {
+ return range<decltype(&*detail::adl_begin(r))>(r);
+}
+/// @}
+} // end namespace lld
+
+#endif
Modified: lld/trunk/unittests/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/unittests/CMakeLists.txt?rev=172512&r1=172511&r2=172512&view=diff
==============================================================================
--- lld/trunk/unittests/CMakeLists.txt (original)
+++ lld/trunk/unittests/CMakeLists.txt Tue Jan 15 00:55:25 2013
@@ -13,4 +13,7 @@
support
)
-add_lld_unittest(CoreTests ErrorOrTest.cpp)
+add_lld_unittest(CoreTests
+ ErrorOrTest.cpp
+ RangeTest.cpp
+ )
Added: lld/trunk/unittests/RangeTest.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/unittests/RangeTest.cpp?rev=172512&view=auto
==============================================================================
--- lld/trunk/unittests/RangeTest.cpp (added)
+++ lld/trunk/unittests/RangeTest.cpp Tue Jan 15 00:55:25 2013
@@ -0,0 +1,245 @@
+//===- lld/unittest/RangeTest.cpp -----------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief range.h unit tests.
+///
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+
+#include "lld/Core/range.h"
+
+#include <assert.h>
+#include <array>
+#include <deque>
+#include <forward_list>
+#include <iterator>
+#include <list>
+#include <numeric>
+#include <sstream>
+#include <vector>
+
+template <typename T, typename U> struct AssertTypesSame;
+template <typename T> struct AssertTypesSame<T, T> {};
+#define ASSERT_TYPES_SAME(T, U) AssertTypesSame<T, U>()
+
+struct no_begin {};
+struct member_begin {
+ int *begin();
+};
+struct free_begin {};
+int *begin(free_begin);
+
+template <typename T>
+auto type_of_forward(T &&t) -> decltype(std::forward<T>(t)) {
+ return std::forward<T>(t);
+}
+
+template <typename To> To implicit_cast(To val) { return val; }
+
+void test_traits() {
+ using namespace lld::detail;
+ ASSERT_TYPES_SAME(begin_result<no_begin>::type, undefined);
+ // This causes clang to segfault.
+#if 0
+ ASSERT_TYPES_SAME(
+ begin_result<decltype(type_of_forward(member_begin()))>::type, int *);
+#endif
+ ASSERT_TYPES_SAME(begin_result<free_begin>::type, int *);
+}
+
+TEST(Range, constructors) {
+ std::vector<int> v(5);
+ std::iota(v.begin(), v.end(), 0);
+ lld::range<std::vector<int>::iterator> r = v;
+ EXPECT_EQ(v.begin(), r.begin());
+ EXPECT_EQ(v.end(), r.end());
+
+ int arr[] = { 1, 2, 3, 4, 5 };
+ std::begin(arr);
+ lld::range<int *> r2 = arr;
+ EXPECT_EQ(5, r2.back());
+}
+
+TEST(Range, conversion_to_pointer_range) {
+ std::vector<int> v(5);
+ std::iota(v.begin(), v.end(), 0);
+ lld::range<int *> r = v;
+ EXPECT_EQ(&*v.begin(), r.begin());
+ EXPECT_EQ(2, r[2]);
+}
+
+template <typename Iter> void takes_range(lld::range<Iter> r) {
+ int expected = 0;
+ for (int val : r) {
+ EXPECT_EQ(expected++, val);
+ }
+}
+
+void takes_ptr_range(lld::range<const int *> r) {
+ int expected = 0;
+ for (int val : r) {
+ EXPECT_EQ(expected++, val);
+ }
+}
+
+TEST(Range, passing) {
+ using lld::make_range;
+ using lld::make_ptr_range;
+ std::list<int> l(5);
+ std::iota(l.begin(), l.end(), 0);
+ takes_range(make_range(l));
+ takes_range(make_range(implicit_cast<const std::list<int> &>(l)));
+ std::deque<int> d(5);
+ std::iota(d.begin(), d.end(), 0);
+ takes_range(make_range(d));
+ takes_range(make_range(implicit_cast<const std::deque<int> &>(d)));
+ std::vector<int> v(5);
+ std::iota(v.begin(), v.end(), 0);
+ takes_range(make_range(v));
+ takes_range(make_range(implicit_cast<const std::vector<int> &>(v)));
+ // MSVC Can't compile make_ptr_range.
+#ifndef _MSC_VER
+ static_assert(
+ std::is_same<decltype(make_ptr_range(v)), lld::range<int *> >::value,
+ "make_ptr_range should return a range of pointers");
+ takes_range(make_ptr_range(v));
+ takes_range(make_ptr_range(implicit_cast<const std::vector<int> &>(v)));
+#endif
+ int arr[] = { 0, 1, 2, 3, 4 };
+ takes_range(make_range(arr));
+ const int carr[] = { 0, 1, 2, 3, 4 };
+ takes_range(make_range(carr));
+
+ takes_ptr_range(v);
+ takes_ptr_range(implicit_cast<const std::vector<int> &>(v));
+ takes_ptr_range(arr);
+ takes_ptr_range(carr);
+}
+
+TEST(Range, access) {
+ std::array<int, 5> a = { { 1, 2, 3, 4, 5 } };
+ lld::range<decltype(a.begin())> r = a;
+ EXPECT_EQ(4, r[3]);
+ EXPECT_EQ(4, r[-2]);
+}
+
+template <bool b> struct CompileAssert;
+template <> struct CompileAssert<true> {};
+
+#if __has_feature(cxx_constexpr)
+constexpr int arr[] = { 1, 2, 3, 4, 5 };
+TEST(Range, constexpr) {
+ constexpr lld::range<const int *> r(arr, arr + 5);
+ CompileAssert<r.front() == 1>();
+ CompileAssert<r.size() == 5>();
+ CompileAssert<r[4] == 5>();
+}
+#endif
+
+template <typename Container> void test_slice() {
+ Container cont(10);
+ std::iota(cont.begin(), cont.end(), 0);
+ lld::range<decltype(cont.begin())> r = cont;
+
+ // One argument.
+ EXPECT_EQ(10, r.slice(0).size());
+ EXPECT_EQ(8, r.slice(2).size());
+ EXPECT_EQ(2, r.slice(2).front());
+ EXPECT_EQ(1, r.slice(-1).size());
+ EXPECT_EQ(9, r.slice(-1).front());
+
+ // Two positive arguments.
+ EXPECT_TRUE(r.slice(5, 2).empty());
+ EXPECT_EQ(next(cont.begin(), 5), r.slice(5, 2).begin());
+ EXPECT_EQ(1, r.slice(1, 2).size());
+ EXPECT_EQ(1, r.slice(1, 2).front());
+
+ // Two negative arguments.
+ EXPECT_TRUE(r.slice(-2, -5).empty());
+ EXPECT_EQ(next(cont.begin(), 8), r.slice(-2, -5).begin());
+ EXPECT_EQ(1, r.slice(-2, -1).size());
+ EXPECT_EQ(8, r.slice(-2, -1).front());
+
+ // Positive start, negative stop.
+ EXPECT_EQ(1, r.slice(6, -3).size());
+ EXPECT_EQ(6, r.slice(6, -3).front());
+ EXPECT_TRUE(r.slice(6, -5).empty());
+ EXPECT_EQ(next(cont.begin(), 6), r.slice(6, -5).begin());
+
+ // Negative start, positive stop.
+ EXPECT_TRUE(r.slice(-3, 6).empty());
+ EXPECT_EQ(next(cont.begin(), 7), r.slice(-3, 6).begin());
+ EXPECT_EQ(1, r.slice(-5, 6).size());
+ EXPECT_EQ(5, r.slice(-5, 6).front());
+}
+
+TEST(Range, slice) {
+ // -fsanitize=undefined complains about this, but only if optimizations are
+ // enabled.
+#if 0
+ test_slice<std::forward_list<int> >();
+#endif
+ test_slice<std::list<int> >();
+ // gcc doesn't like this.
+#if !(defined(__GNUC__) && !defined(__clang__)) || defined(_MSC_VER)
+ test_slice<std::deque<int> >();
+#endif
+}
+
+// This test is flaky and I've yet to pin down why. Changing between
+// EXPECT_EQ(1, input.front()) and EXPECT_TRUE(input.front() == 1) makes it work
+// with VS 2012 in Debug mode. Clang on Linux seems to fail with -03 and -02 -g
+// -fsanitize=undefined.
+#if 0
+TEST(Range, istream_range) {
+ std::istringstream stream("1 2 3 4 5");
+ // MSVC interprets input as a function declaration if you don't declare start
+ // and instead directly pass std::istream_iterator<int>(stream).
+ auto start = std::istream_iterator<int>(stream);
+ lld::range<std::istream_iterator<int> > input(
+ start, std::istream_iterator<int>());
+ EXPECT_TRUE(input.front() == 1);
+ input.pop_front();
+ EXPECT_TRUE(input.front() == 2);
+ input.pop_front(2);
+ EXPECT_TRUE(input.front() == 4);
+ input.pop_front_upto(7);
+ EXPECT_TRUE(input.empty());
+}
+#endif
+
+//! [algorithm using range]
+template <typename T> void partial_sum(T &container) {
+ using lld::make_range;
+ auto range = make_range(container);
+ typename T::value_type sum = 0;
+ // One would actually use a range-based for loop
+ // in this case, but you get the idea:
+ for (; !range.empty(); range.pop_front()) {
+ sum += range.front();
+ range.front() = sum;
+ }
+}
+
+TEST(Range, user1) {
+ std::vector<int> v(5, 2);
+ partial_sum(v);
+ EXPECT_EQ(8, v[3]);
+}
+//! [algorithm using range]
+
+//! [algorithm using ptr_range]
+void my_write(int fd, lld::range<const char *> buffer) {}
+
+TEST(Range, user2) {
+ std::string s("Hello world");
+ my_write(1, s);
+}
More information about the llvm-commits
mailing list