/*
* This file is part of the DXX-Rebirth project .
* It is copyright by its individual contributors, as recorded in the
* project's Git history. See COPYING.txt at the top level for license
* terms and a link to the Git history.
*/
#pragma once
#include
#include
template
class xrange_endpoint
{
public:
const T value;
constexpr xrange_endpoint(T v) :
value(v)
{
}
constexpr operator T() const
{
return value;
}
};
template
class xrange_endpoint, begin> : public std::integral_constant
{
public:
constexpr xrange_endpoint() = default;
constexpr xrange_endpoint(const std::integral_constant &)
{
}
};
template
class xrange_iterator
{
index_type m_idx;
public:
constexpr xrange_iterator(const index_type i) :
m_idx(i)
{
}
index_type operator*() const
{
return m_idx;
}
xrange_iterator &operator++()
{
++ m_idx;
return *this;
}
constexpr bool operator!=(const xrange_iterator &i) const
{
return m_idx != i.m_idx;
}
};
/* This provides an approximation of the functionality of the Python2
* xrange object. Python3 renamed it to `range`. The older name is
* kept because it is easier to find with grep.
*/
template
class xrange_extent :
public xrange_endpoint,
public xrange_endpoint
{
using begin_type = xrange_endpoint;
using end_type = xrange_endpoint;
using iterator = xrange_iterator;
static begin_type init_begin(B b, E e, std::true_type /* is_same */)
{
return (
#ifdef DXX_CONSTANT_TRUE
/* Compile-time only check. Runtime handles (b > e)
* correctly, and it can happen in a correct program. If it
* is guaranteed to happen, then the range is always empty,
* which likely indicates a bug.
*/
(DXX_CONSTANT_TRUE(!(b < e)) && (DXX_ALWAYS_ERROR_FUNCTION(xrange_is_always_empty, "begin never less than end"), 0)),
#endif
b < e ? std::move(b) : e
);
}
static begin_type init_begin(B b, E, std::false_type /* is_same */)
{
return begin_type(b);
}
public:
xrange_extent(B b, E e) :
begin_type(init_begin(std::move(b), e, std::is_same())), end_type(std::move(e))
{
}
iterator begin() const
{
return iterator(static_cast(*this));
}
iterator end() const
{
return iterator(static_cast(*this));
}
};
/* Disallow building an `xrange` with a reference to mutable `e` as the
* end term. When `e` is mutable, the loop might change `e` during
* iteration, so using `xrange` could be wrong. If the loop does not
* change `e`, store it in a const qualified variable, which will select
* the next overload down instead.
*/
template
typename std::enable_if::value, xrange_extent>::type xrange(Tb &&, Te &) = delete; // mutable `e` might change during range
template
static xrange_extent<
typename std::common_type::type,
typename std::remove_const::type>::type,
typename std::remove_const::type>::type
> xrange(Tb &&b, Te &&e)
{
return {std::forward(b), std::forward(e)};
}
/* Disallow building an `xrange` with a default-constructed start term
* and a non-unsigned end term. This overload only restricts the
* one-argument form of `xrange`. Callers that need a non-unsigned end
* term can use the two-argument form to bypass this check.
*/
template <
typename Te,
typename Te_rr_rc = typename std::remove_const<
typename std::remove_reference::type
>::type,
typename B = typename std::enable_if<
std::is_unsigned::value,
std::integral_constant::type, 0u>
>::type
>
static typename std::enable_if::value, xrange_extent>::type xrange(Te &&e)
{
return {B(), std::forward(e)};
}
template
typename std::enable_if>::type xrange(std::integral_constant) = delete; // range is invalid or always empty due to value of `e`
template , typename E = std::integral_constant>
static typename std::enable_if<(Te(0) < e), xrange_extent>::type xrange(std::integral_constant)
{
return {B(), E()};
}