Split and simplify partial_range check
Rely on T.size() instead of distance(T.begin(), T.end()) to get the container size. Add a special case for C-style arrays with known size. In some cases, gcc-4.9 fails to optimize distance(...) on an array<T, N> of unknown base address, leading to a bogus compile failure. Switching the object to have a known address prevents the problem, but requires changing the calling code. Switch to using T.size(), which gcc seems to get right.
This commit is contained in:
parent
36cd5404d4
commit
98afca20a9
|
@ -23,7 +23,9 @@ struct partial_range_t
|
||||||
m_begin(b), m_end(e)
|
m_begin(b), m_end(e)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
__attribute_warn_unused_result
|
||||||
iterator begin() const { return m_begin; }
|
iterator begin() const { return m_begin; }
|
||||||
|
__attribute_warn_unused_result
|
||||||
iterator end() const { return m_end; }
|
iterator end() const { return m_end; }
|
||||||
bool empty() const __attribute_warn_unused_result
|
bool empty() const __attribute_warn_unused_result
|
||||||
{
|
{
|
||||||
|
@ -74,6 +76,9 @@ struct base_partial_range_error_t : std::out_of_range
|
||||||
__attribute_warn_unused_result
|
__attribute_warn_unused_result
|
||||||
static inline std::string prepare(const char (&file)[NF], unsigned line, const char (&estr)[NE], const char (&desc)[ND], unsigned long expr, const void *t, unsigned long d)
|
static inline std::string prepare(const char (&file)[NF], unsigned line, const char (&estr)[NE], const char (&desc)[ND], unsigned long expr, const void *t, unsigned long d)
|
||||||
{
|
{
|
||||||
|
/* Round reporting into large buckets. Code size is more
|
||||||
|
* important than stack space.
|
||||||
|
*/
|
||||||
return prepare<((NF + NE + ND) | 0xff) + 1>(file + 0, line, estr, desc, expr, t, d);
|
return prepare<((NF + NE + ND) | 0xff) + 1>(file + 0, line, estr, desc, expr, t, d);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -90,11 +95,36 @@ struct partial_range_error_t : base_partial_range_error_t
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename I, std::size_t NF, std::size_t NE>
|
namespace partial_range_detail
|
||||||
static void check_partial_range(const char (&file)[NF], unsigned line, const char (&estr)[NE], T &t, I range_begin, I container_end, const std::size_t o, const std::size_t l)
|
{
|
||||||
|
|
||||||
|
template <typename T, std::size_t NF, std::size_t NE>
|
||||||
|
static inline void check_range_bounds(const char (&file)[NF], unsigned line, const char (&estr)[NE], T &t, const std::size_t o, const std::size_t l, const std::size_t d)
|
||||||
{
|
{
|
||||||
using std::distance;
|
|
||||||
#ifdef DXX_HAVE_BUILTIN_CONSTANT_P
|
#ifdef DXX_HAVE_BUILTIN_CONSTANT_P
|
||||||
|
/*
|
||||||
|
* If EXPR and d are compile-time constant, and the (EXPR > d)
|
||||||
|
* branch is optimized out, then the expansion of
|
||||||
|
* PARTIAL_RANGE_COMPILE_CHECK_BOUND is optimized out, preventing
|
||||||
|
* the compile error.
|
||||||
|
*
|
||||||
|
* If EXPR and d are compile-time constant, and the (EXPR > d)
|
||||||
|
* branch is not optimized out, then this function is guaranteed to
|
||||||
|
* throw if it is ever called. In that case, the compile fails,
|
||||||
|
* since the program is guaranteed not to work as the programmer
|
||||||
|
* intends.
|
||||||
|
*
|
||||||
|
* If they are not compile-time constant, but the compiler can
|
||||||
|
* optimize based on constants, then it will optimize out the
|
||||||
|
* expansion of PARTIAL_RANGE_COMPILE_CHECK_BOUND, preventing the
|
||||||
|
* compile error. The function might throw on invalid inputs,
|
||||||
|
* including constant inputs that the compiler failed to recognize
|
||||||
|
* as compile-time constant.
|
||||||
|
*
|
||||||
|
* If the compiler cannot optimize based on the result of
|
||||||
|
* __builtin_constant_p (such as at -O0), then configure tests set
|
||||||
|
* !DXX_HAVE_BUILTIN_CONSTANT_P and the macro expands to nothing.
|
||||||
|
*/
|
||||||
#define PARTIAL_RANGE_COMPILE_CHECK_BOUND(EXPR,S) \
|
#define PARTIAL_RANGE_COMPILE_CHECK_BOUND(EXPR,S) \
|
||||||
(__builtin_constant_p(EXPR) && __builtin_constant_p(d) && (DXX_ALWAYS_ERROR_FUNCTION(partial_range_will_always_throw, S " will always throw"), 0))
|
(__builtin_constant_p(EXPR) && __builtin_constant_p(d) && (DXX_ALWAYS_ERROR_FUNCTION(partial_range_will_always_throw, S " will always throw"), 0))
|
||||||
#else
|
#else
|
||||||
|
@ -104,22 +134,44 @@ static void check_partial_range(const char (&file)[NF], unsigned line, const cha
|
||||||
if (EXPR > d) \
|
if (EXPR > d) \
|
||||||
((void)(PARTIAL_RANGE_COMPILE_CHECK_BOUND(EXPR,S))), \
|
((void)(PARTIAL_RANGE_COMPILE_CHECK_BOUND(EXPR,S))), \
|
||||||
partial_range_error_t<const T>::report(file, line, estr, S, EXPR, t, d)
|
partial_range_error_t<const T>::report(file, line, estr, S, EXPR, t, d)
|
||||||
size_t d = distance(range_begin, container_end);
|
|
||||||
PARTIAL_RANGE_CHECK_BOUND(o, "begin");
|
PARTIAL_RANGE_CHECK_BOUND(o, "begin");
|
||||||
PARTIAL_RANGE_CHECK_BOUND(l, "end");
|
PARTIAL_RANGE_CHECK_BOUND(l, "end");
|
||||||
#undef PARTIAL_RANGE_CHECK_BOUND
|
#undef PARTIAL_RANGE_CHECK_BOUND
|
||||||
#undef PARTIAL_RANGE_COMPILE_CHECK_BOUND
|
#undef PARTIAL_RANGE_COMPILE_CHECK_BOUND
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* C arrays lack a size method, but have a constant size */
|
||||||
|
template <typename T, std::size_t NF, std::size_t NE, std::size_t d>
|
||||||
|
static inline void check_partial_range(const char (&file)[NF], unsigned line, const char (&estr)[NE], T (&t)[d], const std::size_t o, const std::size_t l)
|
||||||
|
{
|
||||||
|
check_range_bounds(file, line, estr, t, o, l, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, std::size_t NF, std::size_t NE>
|
||||||
|
static inline void check_partial_range(const char (&file)[NF], unsigned line, const char (&estr)[NE], T &t, const std::size_t o, const std::size_t l)
|
||||||
|
{
|
||||||
|
check_range_bounds(file, line, estr, t, o, l, t.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
template <typename I>
|
template <typename I>
|
||||||
__attribute_warn_unused_result
|
__attribute_warn_unused_result
|
||||||
static inline partial_range_t<I> unchecked_partial_range(I range_begin, const std::size_t o, const std::size_t l, tt::true_type)
|
static inline partial_range_t<I> unchecked_partial_range(I range_begin, const std::size_t o, const std::size_t l, tt::true_type)
|
||||||
{
|
{
|
||||||
#ifdef DXX_HAVE_BUILTIN_CONSTANT_P
|
#ifdef DXX_HAVE_BUILTIN_CONSTANT_P
|
||||||
|
/* Compile-time only check. Runtime handles (o > l) 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.
|
||||||
|
*/
|
||||||
if (__builtin_constant_p(!(o < l)) && !(o < l))
|
if (__builtin_constant_p(!(o < l)) && !(o < l))
|
||||||
DXX_ALWAYS_ERROR_FUNCTION(partial_range_is_always_empty, "offset never less than length");
|
DXX_ALWAYS_ERROR_FUNCTION(partial_range_is_always_empty, "offset never less than length");
|
||||||
#endif
|
#endif
|
||||||
auto range_end = range_begin;
|
auto range_end = range_begin;
|
||||||
|
/* Use <= so that (o == 0) makes the expression always-true, so the
|
||||||
|
* compiler will optimize out the test.
|
||||||
|
*/
|
||||||
if (o <= l)
|
if (o <= l)
|
||||||
{
|
{
|
||||||
using std::advance;
|
using std::advance;
|
||||||
|
@ -152,8 +204,8 @@ template <typename T, typename UO, typename UL, std::size_t NF, std::size_t NE,
|
||||||
__attribute_warn_unused_result
|
__attribute_warn_unused_result
|
||||||
static inline partial_range_t<I> partial_range(const char (&file)[NF], unsigned line, const char (&estr)[NE], T &t, const UO &o, const UL &l)
|
static inline partial_range_t<I> partial_range(const char (&file)[NF], unsigned line, const char (&estr)[NE], T &t, const UO &o, const UL &l)
|
||||||
{
|
{
|
||||||
|
partial_range_detail::check_partial_range(file, line, estr, t, o, l);
|
||||||
auto range_begin = begin(t);
|
auto range_begin = begin(t);
|
||||||
check_partial_range(file, line, estr, t, range_begin, end(t), o, l);
|
|
||||||
return unchecked_partial_range<I, UO, UL>(range_begin, o, l);
|
return unchecked_partial_range<I, UO, UL>(range_begin, o, l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue