From c29537a341ecf7051362795050320a73095273a7 Mon Sep 17 00:00:00 2001 From: Treb Connell Date: Mon, 5 Oct 2015 14:04:25 -0700 Subject: [PATCH] Fix #119 express preconditions with Expects --- include/array_view.h | 85 +++++++++++++++++++++----------------------- include/gsl.h | 13 +++---- include/gslassert.h | 81 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 53 deletions(-) create mode 100644 include/gslassert.h diff --git a/include/array_view.h b/include/array_view.h index 046cbf8..92b4c04 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -29,7 +29,7 @@ #include #include #include -#include "fail_fast.h" +#include "gslassert.h" #ifdef _MSC_VER @@ -108,7 +108,7 @@ namespace details constexpr coordinate_facade(std::initializer_list il) { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - fail_fast_assert(il.size() == rank, "The size of the initializer list must match the rank of the array"); + Expects(il.size() == rank); for (size_t i = 0; i < rank; ++i) { elems[i] = begin(il)[i]; @@ -131,13 +131,13 @@ namespace details // Preconditions: component_idx < rank constexpr reference operator[](size_t component_idx) { - fail_fast_assert(component_idx < rank, "Component index must be less than rank"); + Expects(component_idx < rank); return elems[component_idx]; } // Preconditions: component_idx < rank constexpr const_reference operator[](size_t component_idx) const { - fail_fast_assert(component_idx < rank, "Component index must be less than rank"); + Expects(component_idx < rank); return elems[component_idx]; } constexpr bool operator==(const ConcreteType& rhs) const noexcept @@ -335,7 +335,7 @@ public: // Preconditions: il.size() == rank constexpr index(std::initializer_list il) { - fail_fast_assert(il.size() == rank, "Size of the initializer list must match the rank of the array"); + Expects(il.size() == rank); value = begin(il)[0]; } @@ -344,7 +344,7 @@ public: template constexpr index(const index<1, OtherValueType> & other) { - fail_fast_assert(other.value <= details::SizeTypeTraits::max_value); + Expects(other.value <= details::SizeTypeTraits::max_value); value = static_cast(other.value); } @@ -355,14 +355,14 @@ public: // Preconditions: component_idx < rank constexpr reference operator[](size_type component_idx) noexcept { - fail_fast_assert(component_idx == 0, "Component index must be less than rank"); + Expects(component_idx == 0); (void)(component_idx); return value; } // Preconditions: component_idx < rank constexpr const_reference operator[](size_type component_idx) const noexcept { - fail_fast_assert(component_idx == 0, "Component index must be less than rank"); + Expects(component_idx == 0); (void)(component_idx); return value; } @@ -582,8 +582,8 @@ namespace details BoundsRanges (const BoundsRanges &) = default; BoundsRanges(const SizeType * const arr) : Base(arr + 1), m_bound(static_cast(*arr * this->Base::totalSize())) { - fail_fast_assert(0 <= *arr); - fail_fast_assert(*arr * this->Base::totalSize() <= details::SizeTypeTraits::max_value); + Expects(0 <= *arr); + Expects(*arr * this->Base::totalSize() <= details::SizeTypeTraits::max_value); } BoundsRanges() : m_bound(0) {} @@ -600,9 +600,8 @@ namespace details } template SizeType linearize(const T & arr) const { - const size_t index = this->Base::totalSize() * arr[Dim]; - fail_fast_assert(index < static_cast(m_bound)); - return static_cast(index) + this->Base::template linearize(arr); + Expects(this->Base::totalSize() * arr[Dim] < static_cast(m_bound)); + return static_cast(this->Base::totalSize() * arr[Dim]) + this->Base::template linearize(arr); } template @@ -651,7 +650,7 @@ namespace details template BoundsRanges(const BoundsRanges &other, bool firstLevel = true) : Base(static_cast&>(other), false) { - fail_fast_assert((firstLevel && totalSize() <= other.totalSize()) || totalSize() == other.totalSize()); + Expects((firstLevel && totalSize() <= other.totalSize()) || totalSize() == other.totalSize()); } template @@ -662,7 +661,7 @@ namespace details template SizeType linearize(const T & arr) const { - fail_fast_assert(arr[Dim] < CurrentRange, "Index is out of range"); + Expects(arr[Dim] < CurrentRange); return static_cast(this->Base::totalSize()) * arr[Dim] + this->Base::template linearize(arr); } @@ -799,8 +798,8 @@ public: constexpr static_bounds(std::initializer_list il) : m_ranges(il.begin()) { - fail_fast_assert(MyRanges::DynamicNum == il.size(), "Size of the initializer list must match the rank of the array"); - fail_fast_assert(m_ranges.totalSize() <= details::SizeTypeTraits::max_value, "Size of the range is larger than the max element of the size type"); + Expects(MyRanges::DynamicNum == il.size()); + Expects(il.size() <= details::SizeTypeTraits::max_value); } constexpr static_bounds() = default; @@ -1340,7 +1339,7 @@ namespace details static_assert(is_bounds::value && is_bounds::value, "The src type and dest type must be bounds"); static_assert(std::is_same::value, "The source type must be a contiguous bounds"); static_assert(BoundsDest::static_size == dynamic_range || BoundsSrc::static_size == dynamic_range || BoundsDest::static_size == BoundsSrc::static_size, "The source bounds must have same size as dest bounds"); - fail_fast_assert(src.size() == dest.size()); + Expects(src.size() == dest.size()); } @@ -1399,11 +1398,9 @@ public: template 1), typename Ret = std::enable_if_t> constexpr Ret operator[](size_type idx) const { - fail_fast_assert(idx < m_bounds.size(), "index is out of bounds of the array"); - const size_type ridx = idx * m_bounds.stride(); - - fail_fast_assert(ridx < m_bounds.total_size(), "index is out of bounds of the underlying data"); - return Ret {m_pdata + ridx, m_bounds.slice()}; + Expects(idx < m_bounds.size()); + Expects(idx * m_bounds.stride() < m_bounds.total_size()); + return Ret {m_pdata + idx * m_bounds.stride(), m_bounds.slice()}; } constexpr operator bool () const noexcept @@ -1496,14 +1493,14 @@ protected: : m_pdata(data) , m_bounds(std::move(bound)) { - fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); + Expects((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); } template constexpr basic_array_view(T *data, std::enable_if_t>::value, bounds_type> bound) noexcept : m_pdata(reinterpret_cast(data)) , m_bounds(std::move(bound)) { - fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); + Expects((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); } template constexpr basic_array_view as_array_view(const DestBounds &bounds) @@ -1567,13 +1564,13 @@ namespace details template BoundsType newBoundsHelperImpl(size_t totalSize, std::true_type) // dynamic size { - fail_fast_assert(totalSize <= details::SizeTypeTraits::max_value); + Expects(totalSize <= details::SizeTypeTraits::max_value); return BoundsType{static_cast(totalSize)}; } template BoundsType newBoundsHelperImpl(size_t totalSize, std::false_type) // static size { - fail_fast_assert(BoundsType::static_size == totalSize); + Expects(BoundsType::static_size == totalSize); return {}; } template @@ -1665,7 +1662,7 @@ public: constexpr array_view(std::nullptr_t, size_type size) : Base(nullptr, bounds_type{}) { - fail_fast_assert(size == 0); + Expects(size == 0); } // default @@ -1696,7 +1693,7 @@ public: && std::is_convertible::value >> constexpr array_view(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{ size }) { - fail_fast_assert(size <= N); + Expects(size <= N); } // from std array @@ -1776,7 +1773,7 @@ public: { static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), "Target type must be standard layout and its size must match the byte array size"); - fail_fast_assert((this->bytes() % sizeof(U)) == 0); + Expects((this->bytes() % sizeof(U)) == 0); return { reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; } @@ -1785,7 +1782,7 @@ public: { static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), "Target type must be standard layout and its size must match the byte array size"); - fail_fast_assert((this->bytes() % sizeof(U)) == 0); + Expects((this->bytes() % sizeof(U)) == 0); return { reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; } @@ -1794,13 +1791,13 @@ public: constexpr array_view first() const noexcept { static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); - fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); // ensures we only check condition when needed + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); // ensures we only check condition when needed return { this->data(), Count }; } constexpr array_view first(size_type count) const noexcept { - fail_fast_assert(count <= this->size()); + Expects(count <= this->size()); return { this->data(), count }; } @@ -1808,13 +1805,13 @@ public: constexpr array_view last() const noexcept { static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); - fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); return { this->data() + this->size() - Count, Count }; } constexpr array_view last(size_type count) const noexcept { - fail_fast_assert(count <= this->size()); + Expects(count <= this->size()); return { this->data() + this->size() - count, count }; } @@ -1822,13 +1819,13 @@ public: constexpr array_view sub() const noexcept { static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset <= bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); - fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset <= this->size()) && Offset + Count <= this->size())); + Expects(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset <= this->size()) && Offset + Count <= this->size())); return { this->data() + Offset, Count }; } constexpr array_view sub(size_type offset, size_type count = dynamic_range) const noexcept { - fail_fast_assert((offset == 0 || offset <= this->size()) && (count == dynamic_range || (offset + count) <= this->size())); + Expects((offset == 0 || offset <= this->size()) && (count == dynamic_range || (offset + count) <= this->size())); return { this->data() + offset, count == dynamic_range ? this->length() - offset : count }; } @@ -1949,20 +1946,20 @@ public: template strided_array_view(value_type(&values)[N], bounds_type bounds) : Base(values, std::move(bounds)) { - fail_fast_assert(this->bounds().total_size() <= N, "Bounds cross data boundaries"); + Expects(this->bounds().total_size() <= N); } // from raw data strided_array_view(pointer ptr, size_type size, bounds_type bounds): Base(ptr, std::move(bounds)) { - fail_fast_assert(this->bounds().total_size() <= size, "Bounds cross data boundaries"); + Expects(this->bounds().total_size() <= size); } // from array view template > strided_array_view(array_view av, bounds_type bounds) : Base(av.data(), std::move(bounds)) { - fail_fast_assert(this->bounds().total_size() <= av.bounds().total_size(), "Bounds cross data boundaries"); + Expects(this->bounds().total_size() <= av.bounds().total_size()); } // convertible @@ -2007,7 +2004,7 @@ public: private: static index_type resize_extent(const index_type& extent, size_t d) { - fail_fast_assert(extent[rank - 1] >= d && (extent[rank-1] % d == 0), "The last dimension of the array needs to contain a multiple of new type elements"); + Expects(extent[rank - 1] >= d && (extent[rank-1] % d == 0)); index_type ret = extent; ret[rank - 1] /= d; @@ -2018,7 +2015,7 @@ private: template > static index_type resize_stride(const index_type& strides, size_t , void * = 0) { - fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); + Expects(strides[rank - 1] == 1); return strides; } @@ -2026,8 +2023,8 @@ private: template 1), typename Dummy = std::enable_if_t> static index_type resize_stride(const index_type& strides, size_t d) { - fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); - fail_fast_assert(strides[rank - 2] >= d && (strides[rank - 2] % d == 0), "The strides must have contiguous chunks of memory that can contain a multiple of new type elements"); + Expects(strides[rank - 1] == 1); + Expects(strides[rank - 2] >= d && (strides[rank - 2] % d == 0)); for (size_t i = rank - 1; i > 0; --i) fail_fast_assert((strides[i-1] >= strides[i]) && (strides[i-1] % strides[i] == 0), "Only strided arrays with regular strides can be resized"); diff --git a/include/gsl.h b/include/gsl.h index 824ca6a..f56f350 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -21,6 +21,7 @@ #include "array_view.h" // array_view, strided_array_view... #include "string_view.h" // zstring, string_view, zstring_builder... +#include "gslassert.h" #include #ifdef _MSC_VER @@ -63,12 +64,6 @@ using std::shared_ptr; template using owner = T; -// -// GSL.assert: assertions -// -#define Expects(x) gsl::fail_fast_assert((x)) -#define Ensures(x) gsl::fail_fast_assert((x)) - // // GSL.util: utilities // @@ -111,13 +106,13 @@ T narrow(U u) { T t = narrow_cast(u); if (static_cast(t) != u) throw narro // at() - Bounds-checked way of accessing static arrays, std::array, std::vector // template -T& at(T(&arr)[N], size_t index) { fail_fast_assert(index < N); return arr[index]; } +T& at(T(&arr)[N], size_t index) { Expects(index < N); return arr[index]; } template -T& at(std::array& arr, size_t index) { fail_fast_assert(index < N); return arr[index]; } +T& at(std::array& arr, size_t index) { Expects(index < N); return arr[index]; } template -typename Cont::value_type& at(Cont& cont, size_t index) { fail_fast_assert(index < cont.size()); return cont[index]; } +typename Cont::value_type& at(Cont& cont, size_t index) { Expects(index < cont.size()); return cont[index]; } // diff --git a/include/gslassert.h b/include/gslassert.h new file mode 100644 index 0000000..88b8484 --- /dev/null +++ b/include/gslassert.h @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifndef GSL_UTIL_H +#define GSL_UTIL_H + +#include "fail_fast.h" + +#ifdef _MSC_VER + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr /* nothing */ + +// MSVC 2013 workarounds +#if _MSC_VER <= 1800 + +// noexcept is not understood +#ifndef GSL_THROWS_FOR_TESTING +#define noexcept /* nothing */ +#endif + +// turn off some misguided warnings +#pragma warning(push) +#pragma warning(disable: 4351) // warns about newly introduced aggregate initializer behavior + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +// In order to test the library, we need it to throw exceptions that we can catch +#ifdef GSL_THROWS_FOR_TESTING +#define noexcept /* nothing */ +#endif // GSL_THROWS_FOR_TESTING + +namespace gsl { + +// +// GSL.assert: assertions +// +#define Expects(x) gsl::fail_fast_assert((x)) +#define Ensures(x) gsl::fail_fast_assert((x)) + +} // namespace gsl + +#ifdef _MSC_VER + +#undef constexpr +#pragma pop_macro("constexpr") + +#if _MSC_VER <= 1800 +#pragma warning(pop) + +#ifndef GSL_THROWS_FOR_TESTING +#pragma undef noexcept +#endif // GSL_THROWS_FOR_TESTING + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#if defined(GSL_THROWS_FOR_TESTING) +#undef noexcept +#endif // GSL_THROWS_FOR_TESTING + +#endif // GSL_UTIL_H