diff --git a/CMakeLists.txt b/CMakeLists.txt index e397830..48f6712 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,9 @@ set_target_properties(gmock_main PROPERTIES FOLDER lib) set_target_properties(gtest PROPERTIES FOLDER lib) set_target_properties(gtest_main PROPERTIES FOLDER lib) +# ... GSL +include_directories(SYSTEM "lib/gsl/include") + # Define executable include_directories("src" "src/audio_input") configure_file(src/appInfo.cpp.in src/appInfo.cpp ESCAPE_QUOTES) diff --git a/LICENSE.md b/LICENSE.md index 9a9e029..00dc916 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -116,4 +116,19 @@ The [Google Test](https://github.com/google/googletest) framework is released un > * 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. > * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. > -> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. \ No newline at end of file +> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +### Guidelines Support Library (GSL) + +The [Guidelines Support Library](https://github.com/Microsoft/GSL) is released under the **MIT License (MIT)**. + +> Copyright (c) 2015 Microsoft Corporation. All rights reserved. +> +> This code is licensed under the MIT License (MIT). +> +> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +> +> 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. +> \ No newline at end of file diff --git a/lib/gsl/.clang-format b/lib/gsl/.clang-format new file mode 100644 index 0000000..b80d2c6 --- /dev/null +++ b/lib/gsl/.clang-format @@ -0,0 +1,21 @@ +ColumnLimit: 100 + +UseTab: Never +IndentWidth: 4 +AccessModifierOffset: -4 +NamespaceIndentation: Inner + +BreakBeforeBraces: Allman +AlwaysBreakTemplateDeclarations: true +BreakConstructorInitializersBeforeComma: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +AllowShortBlocksOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true + +PointerAlignment: Left +AlignConsecutiveAssignments: false +AlignTrailingComments: false + +SpaceAfterCStyleCast: true diff --git a/lib/gsl/.gitignore b/lib/gsl/.gitignore new file mode 100644 index 0000000..ea47eb3 --- /dev/null +++ b/lib/gsl/.gitignore @@ -0,0 +1,14 @@ +tests/unittest-cpp +CMakeFiles +tests/CMakeFiles +tests/Debug +*.opensdf +*.sdf +tests/*tests.dir +*.vcxproj +*.vcxproj.filters +*.sln +*.tlog +Testing/Temporary/*.* +CMakeCache.txt +*.suo diff --git a/lib/gsl/.travis.yml b/lib/gsl/.travis.yml new file mode 100644 index 0000000..3c64230 --- /dev/null +++ b/lib/gsl/.travis.yml @@ -0,0 +1,68 @@ +# Based on https://github.com/ldionne/hana/blob/master/.travis.yml + +language: cpp +sudo: false + +matrix: + include: + - env: COMPILER=clang++-3.6 BUILD_TYPE=Debug CLANG=1 + compiler: clang + addons: &clang36 + apt: + packages: + - clang-3.6 + - cmake + - g++-5 + sources: &sources + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.6 + - kalakris-cmake + - env: COMPILER=clang++-3.6 BUILD_TYPE=Release CLANG=1 + compiler: clang + addons: *clang36 + - env: COMPILER=g++-5 BUILD_TYPE=Debug + compiler: gcc + addons: &gcc5 + apt: + packages: g++-5 + sources: *sources + - env: COMPILER=g++-5 BUILD_TYPE=Release + compiler: gcc + addons: *gcc5 + +install: + - which $COMPILER + - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" + - mkdir ${DEPS_DIR} && cd ${DEPS_DIR} + - | + if [[ "$CLANG" == 1 && "${TRAVIS_OS_NAME}" == "linux" && "${STDLIB}" != "libstdc++" ]]; then + if [[ "${COMPILER}" == "clang++-3.5" ]]; then LLVM_VERSION="3.5.2"; fi + if [[ "${COMPILER}" == "clang++-3.6" ]]; then LLVM_VERSION="3.6.2"; fi + if [[ "${COMPILER}" == "clang++-3.7" ]]; then LLVM_VERSION="3.7.0"; fi + LLVM_URL="http://llvm.org/releases/${LLVM_VERSION}/llvm-${LLVM_VERSION}.src.tar.xz" + LIBCXX_URL="http://llvm.org/releases/${LLVM_VERSION}/libcxx-${LLVM_VERSION}.src.tar.xz" + LIBCXXABI_URL="http://llvm.org/releases/${LLVM_VERSION}/libcxxabi-${LLVM_VERSION}.src.tar.xz" + mkdir -p llvm llvm/build llvm/projects/libcxx llvm/projects/libcxxabi + travis_retry wget --quiet -O - ${LLVM_URL} | tar --strip-components=1 -xJ -C llvm + travis_retry wget --quiet -O - ${LIBCXX_URL} | tar --strip-components=1 -xJ -C llvm/projects/libcxx + travis_retry wget --quiet -O - ${LIBCXXABI_URL} | tar --strip-components=1 -xJ -C llvm/projects/libcxxabi + (cd llvm/build && cmake .. -DCMAKE_INSTALL_PREFIX=${DEPS_DIR}/llvm/install -DCMAKE_CXX_COMPILER=clang++) + (cd llvm/build/projects/libcxx && make install -j2) + (cd llvm/build/projects/libcxxabi && make install -j2) + export CXXFLAGS="-I ${DEPS_DIR}/llvm/install/include/c++/v1" + export LDFLAGS="-L ${DEPS_DIR}/llvm/install/lib -l c++ -l c++abi" + export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${DEPS_DIR}/llvm/install/lib" + fi + +before_script: + - cd ${TRAVIS_BUILD_DIR} + - git clone --depth 1 https://github.com/Microsoft/unittest-cpp tests/unittest-cpp + - cmake -H. -Bb -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_INSTALL_PREFIX=$PWD/o -DCMAKE_BUILD_TYPE=$BUILD_TYPE + - cmake --build b + +script: + - cd b + - ctest + +notifications: + email: false diff --git a/lib/gsl/CMakeLists.txt b/lib/gsl/CMakeLists.txt new file mode 100644 index 0000000..f8145d6 --- /dev/null +++ b/lib/gsl/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8.7) + +project(GSL CXX) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} +) + +enable_testing() + +add_subdirectory(tests) diff --git a/lib/gsl/CONTRIBUTING.md b/lib/gsl/CONTRIBUTING.md new file mode 100644 index 0000000..990b8e1 --- /dev/null +++ b/lib/gsl/CONTRIBUTING.md @@ -0,0 +1,29 @@ +## Contributing to the Guidelines Support Library + +The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the +[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines). GSL design changes are made only as a result of modifications to the Guidelines. + +GSL is accepting contributions that improve or refine any of the types in this library as well as ports to other platforms. Changes should have an issue +tracking the suggestion that has been approved by the maintainers. Your pull request should include a link to the bug that you are fixing. If you've submitted +a PR, please post a comment in the associated issue to avoid duplication of effort. + +## Legal +You will need to complete a Contributor License Agreement (CLA). Briefly, this agreement testifies that you are granting us and the community permission to +use the submitted change according to the terms of the project's license, and that the work being submitted is under appropriate copyright. + +Please submit a Contributor License Agreement (CLA) before submitting a pull request. You may visit https://cla.microsoft.com to sign digitally. + +## Housekeeping +Your pull request should: + +* Include a description of what your change intends to do +* Be a child commit of a reasonably recent commit in the **master** branch + * Requests need not be a single commit, but should be a linear sequence of commits (i.e. no merge commits in your PR) +* It is desirable, but not necessary, for the tests to pass at each commit. Please see [README.md](./README.md) for instructions to build the test suite. +* Have clear commit messages + * e.g. "Fix issue", "Add tests for type", etc. +* Include appropriate tests + * Tests should include reasonable permutations of the target fix/change + * Include baseline changes with your change + * All changed code must have 100% code coverage +* To avoid line ending issues, set `autocrlf = input` and `whitespace = cr-at-eol` in your git configuration diff --git a/lib/gsl/LICENSE b/lib/gsl/LICENSE new file mode 100644 index 0000000..aa58667 --- /dev/null +++ b/lib/gsl/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2015 Microsoft Corporation. All rights reserved. + +This code is licensed under the MIT License (MIT). + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/lib/gsl/README.md b/lib/gsl/README.md new file mode 100644 index 0000000..5743bb3 --- /dev/null +++ b/lib/gsl/README.md @@ -0,0 +1,67 @@ +# GSL: Guidelines Support Library [![Build Status](https://travis-ci.org/Microsoft/GSL.svg?branch=master)](https://travis-ci.org/Microsoft/GSL) [![Build status](https://ci.appveyor.com/api/projects/status/github/Microsoft/GSL?svg=true)](https://ci.appveyor.com/project/neilmacintosh/GSL) + +The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the +[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](https://isocpp.org). +This repo contains Microsoft's implementation of GSL. + +The library includes types like `span`, `string_span`, `owner<>` and others. + +The entire implementation is provided inline in the headers under the [include](./include) directory. The implementation generally assumes a platform that implements C++14 support. There are specific workarounds to support MSVC 2013 and 2015. + +While some types have been broken out into their own headers (e.g. [include/span.h](./include/span.h)), +it is simplest to just include [gsl.h](./include/gsl.h) and gain access to the entire library. + +> NOTE: We encourage contributions that improve or refine any of the types in this library as well as ports to +other platforms. Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about contributing. + +# Quick Start +## Supported Platforms +The test suite that exercises GSL has been built and passes successfully on the following platforms: + +* Windows using Visual Studio 2013 +* Windows using Visual Studio 2015 +* Windows using Clang/LLVM 3.6 +* Windows using GCC 5.1 +* GNU/Linux using Clang/LLVM 3.6 +* GNU/Linux using GCC 5.1 +* OS X Yosemite using Xcode with AppleClang 7.0.0.7000072 +* OS X Yosemite using GCC-5.2.0 +* FreeBSD 10.x with Clang/LLVM 3.6 + +> If you successfully port GSL to another platform, we would love to hear from you. Please submit an issue to let us know. Also please consider +contributing any changes that were necessary back to this project to benefit the wider community. + +## Building the tests +To build the tests, you will require the following: + +* [CMake](http://cmake.org), version 2.8.7 or later to be installed and in your PATH. +* [UnitTest-cpp](https://github.com/Microsoft/unittest-cpp), to be cloned under the [tests/unittest-cpp](./tests/unittest-cpp) directory +of your GSL source. + +These steps assume the source code of this repository has been cloned into a directory named `c:\GSL`. + +1. Create a directory to contain the build outputs for a particular architecture (we name it c:\GSL\vs14-x86 in this example). + + cd GSL + md build-x86 + cd build-x86 + +2. Configure CMake to use the compiler of your choice (you can see a list by running `cmake --help`). + + cmake -G "Visual Studio 14 2015" c:\GSL + +3. Build the test suite (in this case, in the Debug configuration, Release is another good choice). + + cmake --build . --config Debug + +4. Run the test suite. + + ctest -C Debug + +All tests should pass - indicating your platform is fully supported and you are ready to use the GSL types! + +## Using the libraries +As the types are entirely implemented inline in headers, there are no linking requirements. + +Just place the contents of the [include](./include) directory within your source tree so it is available +to your compiler, then include the appropriate headers in your program, and away you go! diff --git a/lib/gsl/include/gsl.h b/lib/gsl/include/gsl.h new file mode 100644 index 0000000..ad064ba --- /dev/null +++ b/lib/gsl/include/gsl.h @@ -0,0 +1,168 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_GSL_H +#define GSL_GSL_H + +#include "gsl_assert.h" // Ensures/Expects +#include "gsl_util.h" // finally()/narrow()/narrow_cast()... +#include "span.h" // span, strided_span... +#include "string_span.h" // zstring, string_span, zstring_builder... +#include + +#ifdef _MSC_VER + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr + +// MSVC 2013 workarounds +#if _MSC_VER <= 1800 +// noexcept is not understood +#pragma push_macro("noexcept") +#define noexcept + +// 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 + + +namespace gsl +{ + +// +// GSL.owner: ownership pointers +// +using std::unique_ptr; +using std::shared_ptr; + +template +using owner = T; + + +// +// not_null +// +// Restricts a pointer or smart pointer to only hold non-null values. +// +// Has zero size overhead over T. +// +// If T is a pointer (i.e. T == U*) then +// - allow construction from U* or U& +// - disallow construction from nullptr_t +// - disallow default construction +// - ensure construction from U* fails with nullptr +// - allow implicit conversion to U* +// +template +class not_null +{ + static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); +public: + not_null(T t) : ptr_(t) { ensure_invariant(); } + not_null& operator=(const T& t) { ptr_ = t; ensure_invariant(); return *this; } + + not_null(const not_null &other) = default; + not_null& operator=(const not_null &other) = default; + + template ::value>> + not_null(const not_null &other) + { + *this = other; + } + + template ::value>> + not_null& operator=(const not_null &other) + { + ptr_ = other.get(); + return *this; + } + + // prevents compilation when someone attempts to assign a nullptr + not_null(std::nullptr_t) = delete; + not_null(int) = delete; + not_null& operator=(std::nullptr_t) = delete; + not_null& operator=(int) = delete; + + T get() const { +#ifdef _MSC_VER + __assume(ptr_ != nullptr); +#endif + return ptr_; + } // the assume() should help the optimizer + + operator T() const { return get(); } + T operator->() const { return get(); } + + bool operator==(const T& rhs) const { return ptr_ == rhs; } + bool operator!=(const T& rhs) const { return !(*this == rhs); } +private: + T ptr_; + + // we assume that the compiler can hoist/prove away most of the checks inlined from this function + // if not, we could make them optional via conditional compilation + void ensure_invariant() const { Expects(ptr_ != nullptr); } + + // unwanted operators...pointers only point to single objects! + // TODO ensure all arithmetic ops on this type are unavailable + not_null& operator++() = delete; + not_null& operator--() = delete; + not_null operator++(int) = delete; + not_null operator--(int) = delete; + not_null& operator+(size_t) = delete; + not_null& operator+=(size_t) = delete; + not_null& operator-(size_t) = delete; + not_null& operator-=(size_t) = delete; +}; + +} // namespace gsl + +namespace std +{ + template + struct hash> + { + size_t operator()(const gsl::not_null & value) const + { + return hash{}(value); + } + }; + +} // namespace std + +#ifdef _MSC_VER + +#undef constexpr +#pragma pop_macro("constexpr") + +#if _MSC_VER <= 1800 + +#undef noexcept +#pragma pop_macro("noexcept") + +#pragma warning(pop) + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#endif // GSL_GSL_H diff --git a/lib/gsl/include/gsl_assert.h b/lib/gsl/include/gsl_assert.h new file mode 100644 index 0000000..81cfd13 --- /dev/null +++ b/lib/gsl/include/gsl_assert.h @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_CONTRACTS_H +#define GSL_CONTRACTS_H + +#include + +// +// There are three configuration options for this GSL implementation's behavior +// when pre/post conditions on the GSL types are violated: +// +// 1. GSL_TERMINATE_ON_CONTRACT_VIOLATION: std::terminate will be called (default) +// 2. GSL_THROW_ON_CONTRACT_VIOLATION: a gsl::fail_fast exception will be thrown +// 3. GSL_UNENFORCED_ON_CONTRACT_VIOLATION: nothing happens +// +#if !(defined(GSL_THROW_ON_CONTRACT_VIOLATION) ^ defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) ^ defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION)) +#define GSL_TERMINATE_ON_CONTRACT_VIOLATION +#endif + + +#define GSL_STRINGIFY_DETAIL(x) #x +#define GSL_STRINGIFY(x) GSL_STRINGIFY_DETAIL(x) + + +// +// GSL.assert: assertions +// + +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) + +#include + +namespace gsl +{ +struct fail_fast : public std::runtime_error +{ + explicit fail_fast(char const* const message) : std::runtime_error(message) {} +}; +} + +#define Expects(cond) if (!(cond)) \ + throw gsl::fail_fast("GSL: Precondition failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__)); +#define Ensures(cond) if (!(cond)) \ + throw gsl::fail_fast("GSL: Postcondition failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__)); + + +#elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) + + +#define Expects(cond) if (!(cond)) std::terminate(); +#define Ensures(cond) if (!(cond)) std::terminate(); + + +#elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION) + +#define Expects(cond) +#define Ensures(cond) + +#endif + + +#endif // GSL_CONTRACTS_H diff --git a/lib/gsl/include/gsl_util.h b/lib/gsl/include/gsl_util.h new file mode 100644 index 0000000..316f2a1 --- /dev/null +++ b/lib/gsl/include/gsl_util.h @@ -0,0 +1,148 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 "gsl_assert.h" // Ensures/Expects +#include +#include +#include +#include + +#ifdef _MSC_VER + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr + +// MSVC 2013 workarounds +#if _MSC_VER <= 1800 +// noexcept is not understood +#pragma push_macro("noexcept") +#define noexcept + +// turn off some misguided warnings +#pragma warning(push) +#pragma warning(disable: 4351) // warns about newly introduced aggregate initializer behavior +#pragma warning(disable: 4127) // conditional expression is constant + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + + +namespace gsl +{ +// +// GSL.util: utilities +// + +// final_act allows you to ensure something gets run at the end of a scope +template +class final_act +{ +public: + explicit final_act(F f) noexcept + : f_(std::move(f)), invoke_(true) + {} + + final_act(final_act&& other) noexcept + : f_(std::move(other.f_)), invoke_(other.invoke_) + { other.invoke_ = false; } + + final_act(const final_act&) = delete; + final_act& operator=(const final_act&) = delete; + + ~final_act() noexcept { if (invoke_) f_(); } + +private: + F f_; + bool invoke_; +}; + +// finally() - convenience function to generate a final_act +template +inline final_act finally(const F &f) +noexcept { return final_act(f); } + +template +inline final_act finally(F &&f) noexcept +{ return final_act(std::forward(f)); } + +// narrow_cast(): a searchable way to do narrowing casts of values +template +inline constexpr T narrow_cast(U u) noexcept +{ return static_cast(u); } + +struct narrowing_error : public std::exception {}; + +namespace details +{ + template + struct is_same_signedness : public std::integral_constant::value == std::is_signed::value> + {}; +} + +// narrow() : a checked version of narrow_cast() that throws if the cast changed the value +template +inline T narrow(U u) +{ + T t = narrow_cast(u); + if (static_cast(t) != u) + throw narrowing_error(); + if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) + throw narrowing_error(); + return t; +} + +// +// at() - Bounds-checked way of accessing static arrays, std::array, std::vector +// +template +constexpr T& at(T(&arr)[N], size_t index) +{ Expects(index < N); return arr[index]; } + +template +constexpr T& at(std::array& arr, size_t index) +{ Expects(index < N); return arr[index]; } + +template +constexpr typename Cont::value_type& at(Cont& cont, size_t index) +{ Expects(index < cont.size()); return cont[index]; } + +} // namespace gsl + + +#ifdef _MSC_VER + +#undef constexpr +#pragma pop_macro("constexpr") + +#if _MSC_VER <= 1800 + +#undef noexcept +#pragma pop_macro("noexcept") + +#pragma warning(pop) + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#endif // GSL_UTIL_H diff --git a/lib/gsl/include/span.h b/lib/gsl/include/span.h new file mode 100644 index 0000000..31c26ad --- /dev/null +++ b/lib/gsl/include/span.h @@ -0,0 +1,2224 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_SPAN_H +#define GSL_SPAN_H + +#include "gsl_assert.h" +#include "gsl_util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + +// turn off some warnings that are noisy about our Expects statements +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr + +// VS 2013 workarounds +#if _MSC_VER <= 1800 + +#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG +#define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + +// noexcept is not understood +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION +#pragma push_macro("noexcept") +#define noexcept /* nothing */ +#endif + +// turn off some misguided warnings +#pragma warning(push) +#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior +#pragma warning(disable : 4512) // warns that assignment op could not be generated + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#ifdef GSL_THROW_ON_CONTRACT_VIOLATION + +#ifdef _MSC_VER +#pragma push_macro("noexcept") +#endif + +#define noexcept /* nothing */ + +#endif // GSL_THROW_ON_CONTRACT_VIOLATION + +namespace gsl +{ + +/* +** begin definitions of index and bounds +*/ +namespace details +{ + template + struct SizeTypeTraits + { + static const SizeType max_value = std::numeric_limits::max(); + }; + + template + class are_integral : public std::integral_constant + { + }; + + template + class are_integral + : public std::integral_constant::value && are_integral::value> + { + }; +} + +template +class index final +{ + static_assert(Rank > 0, "Rank must be greater than 0!"); + + template + friend class index; + +public: + static const size_t rank = Rank; + using value_type = std::ptrdiff_t; + using size_type = value_type; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; + + constexpr index() noexcept {} + + constexpr index(const value_type (&values)[Rank]) noexcept + { + std::copy(values, values + Rank, elems); + } + +#ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG + template < + typename T, typename... Ts, + typename = std::enable_if_t<((sizeof...(Ts) + 1) == Rank) && std::is_integral::value && + details::are_integral::value>> + constexpr index(T t, Ts... ds) + : index({narrow_cast(t), narrow_cast(ds)...}) + { + } +#else + template ::value>> + constexpr index(Ts... ds) noexcept : elems{narrow_cast(ds)...} + { + } +#endif + + constexpr index(const index& other) noexcept = default; + + constexpr index& operator=(const index& rhs) noexcept = default; + + // Preconditions: component_idx < rank + constexpr reference operator[](size_t component_idx) + { + Expects(component_idx < Rank); // Component index must be less than rank + return elems[component_idx]; + } + + // Preconditions: component_idx < rank + constexpr const_reference operator[](size_t component_idx) const noexcept + { + Expects(component_idx < Rank); // Component index must be less than rank + return elems[component_idx]; + } + + constexpr bool operator==(const index& rhs) const noexcept + { + return std::equal(elems, elems + rank, rhs.elems); + } + + constexpr bool operator!=(const index& rhs) const noexcept { return !(this == rhs); } + + constexpr index operator+() const noexcept { return *this; } + + constexpr index operator-() const noexcept + { + index ret = *this; + std::transform(ret, ret + rank, ret, std::negate{}); + return ret; + } + + constexpr index operator+(const index& rhs) const noexcept + { + index ret = *this; + ret += rhs; + return ret; + } + + constexpr index operator-(const index& rhs) const noexcept + { + index ret = *this; + ret -= rhs; + return ret; + } + + constexpr index& operator+=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); + return *this; + } + + constexpr index& operator-=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); + return *this; + } + + constexpr index operator*(value_type v) const noexcept + { + index ret = *this; + ret *= v; + return ret; + } + + constexpr index operator/(value_type v) const noexcept + { + index ret = *this; + ret /= v; + return ret; + } + + friend constexpr index operator*(value_type v, const index& rhs) noexcept + { + return rhs * v; + } + + constexpr index& operator*=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, + [v](value_type x) { return std::multiplies{}(x, v); }); + return *this; + } + + constexpr index& operator/=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, + [v](value_type x) { return std::divides{}(x, v); }); + return *this; + } + +private: + value_type elems[Rank] = {}; +}; + +#ifndef _MSC_VER + +struct static_bounds_dynamic_range_t +{ + template ::value>> + constexpr operator T() const noexcept + { + return narrow_cast(-1); + } + + template ::value>> + constexpr bool operator==(T other) const noexcept + { + return narrow_cast(-1) == other; + } + + template ::value>> + constexpr bool operator!=(T other) const noexcept + { + return narrow_cast(-1) != other; + } +}; + +template ::value>> +constexpr bool operator==(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right == left; +} + +template ::value>> +constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right != left; +} + +constexpr static_bounds_dynamic_range_t dynamic_range{}; +#else +const std::ptrdiff_t dynamic_range = -1; +#endif + +struct generalized_mapping_tag +{ +}; +struct contiguous_mapping_tag : generalized_mapping_tag +{ +}; + +namespace details +{ + + template + struct LessThan + { + static const bool value = Left < Right; + }; + + template + struct BoundsRanges + { + using size_type = std::ptrdiff_t; + static const size_type Depth = 0; + static const size_type DynamicNum = 0; + static const size_type CurrentRange = 1; + static const size_type TotalSize = 1; + + // TODO : following signature is for work around VS bug + template + BoundsRanges(const OtherRange&, bool /* firstLevel */) + { + } + + BoundsRanges(const BoundsRanges&) = default; + BoundsRanges& operator=(const BoundsRanges&) = default; + BoundsRanges(const std::ptrdiff_t* const) {} + BoundsRanges() = default; + + template + void serialize(T&) const + { + } + + template + size_type linearize(const T&) const + { + return 0; + } + + template + size_type contains(const T&) const + { + return -1; + } + + size_type elementNum(size_t) const noexcept { return 0; } + + size_type totalSize() const noexcept { return TotalSize; } + + bool operator==(const BoundsRanges&) const noexcept { return true; } + }; + + template + struct BoundsRanges : BoundsRanges + { + using Base = BoundsRanges; + using size_type = std::ptrdiff_t; + static const size_t Depth = Base::Depth + 1; + static const size_t DynamicNum = Base::DynamicNum + 1; + static const size_type CurrentRange = dynamic_range; + static const size_type TotalSize = dynamic_range; + const size_type m_bound; + + BoundsRanges(const BoundsRanges&) = default; + + BoundsRanges(const std::ptrdiff_t* const arr) + : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) + { + Expects(0 <= *arr); + } + + BoundsRanges() : m_bound(0) {} + + template + BoundsRanges(const BoundsRanges& other, + bool /* firstLevel */ = true) + : Base(static_cast&>(other), false) + , m_bound(other.totalSize()) + { + } + + template + void serialize(T& arr) const + { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + size_type linearize(const T& arr) const + { + const size_type index = this->Base::totalSize() * arr[Dim]; + Expects(index < m_bound); + return index + this->Base::template linearize(arr); + } + + template + size_type contains(const T& arr) const + { + const ptrdiff_t last = this->Base::template contains(arr); + if (last == -1) return -1; + const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; + return cur < m_bound ? cur + last : -1; + } + + size_type totalSize() const noexcept { return m_bound; } + + size_type elementNum() const noexcept { return totalSize() / this->Base::totalSize(); } + + size_type elementNum(size_t dim) const noexcept + { + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator==(const BoundsRanges& rhs) const noexcept + { + return m_bound == rhs.m_bound && + static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRanges : BoundsRanges + { + using Base = BoundsRanges; + using size_type = std::ptrdiff_t; + static const size_t Depth = Base::Depth + 1; + static const size_t DynamicNum = Base::DynamicNum; + static const size_type CurrentRange = CurRange; + static const size_type TotalSize = + Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; + + BoundsRanges(const BoundsRanges&) = default; + + BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {} + BoundsRanges() = default; + + template + BoundsRanges(const BoundsRanges& other, + bool firstLevel = true) + : Base(static_cast&>(other), false) + { + (void) firstLevel; + } + + template + void serialize(T& arr) const + { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + size_type linearize(const T& arr) const + { + Expects(arr[Dim] < CurrentRange); // Index is out of range + return this->Base::totalSize() * arr[Dim] + + this->Base::template linearize(arr); + } + + template + size_type contains(const T& arr) const + { + if (arr[Dim] >= CurrentRange) return -1; + const size_type last = this->Base::template contains(arr); + if (last == -1) return -1; + return this->Base::totalSize() * arr[Dim] + last; + } + + size_type totalSize() const noexcept { return CurrentRange * this->Base::totalSize(); } + + size_type elementNum() const noexcept { return CurrentRange; } + + size_type elementNum(size_t dim) const noexcept + { + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator==(const BoundsRanges& rhs) const noexcept + { + return static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRangeConvertible + : public std::integral_constant= TargetType::TotalSize || + TargetType::TotalSize == dynamic_range || + SourceType::TotalSize == dynamic_range || + TargetType::TotalSize == 0)> + { + }; + + template + struct TypeListIndexer + { + const TypeChain& obj_; + TypeListIndexer(const TypeChain& obj) : obj_(obj) {} + + template + const TypeChain& getObj(std::true_type) + { + return obj_; + } + + template + auto getObj(std::false_type) + -> decltype(TypeListIndexer(static_cast(obj_)).template get()) + { + return TypeListIndexer(static_cast(obj_)).template get(); + } + + template + auto get() -> decltype(getObj(std::integral_constant())) + { + return getObj(std::integral_constant()); + } + }; + + template + TypeListIndexer createTypeListIndexer(const TypeChain& obj) + { + return TypeListIndexer(obj); + } + + template 1), + typename Ret = std::enable_if_t>> + constexpr Ret shift_left(const index& other) noexcept + { + Ret ret{}; + for (size_t i = 0; i < Rank - 1; ++i) { + ret[i] = other[i + 1]; + } + return ret; + } +} + +template +class bounds_iterator; + +template +class static_bounds +{ +public: + static_bounds(const details::BoundsRanges&) {} +}; + +template +class static_bounds +{ + using MyRanges = details::BoundsRanges; + + MyRanges m_ranges; + constexpr static_bounds(const MyRanges& range) : m_ranges(range) {} + + template + friend class static_bounds; + +public: + static const size_t rank = MyRanges::Depth; + static const size_t dynamic_rank = MyRanges::DynamicNum; + static const std::ptrdiff_t static_size = MyRanges::TotalSize; + + using size_type = std::ptrdiff_t; + using index_type = index; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + using difference_type = std::ptrdiff_t; + using sliced_type = static_bounds; + using mapping_type = contiguous_mapping_tag; + + constexpr static_bounds(const static_bounds&) = default; + + template + struct BoundsRangeConvertible2; + + template > + static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; + + template + static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; + + template + struct BoundsRangeConvertible2 + : decltype(helpBoundsRangeConvertible( + SourceType(), TargetType(), + std::integral_constant())) + { + }; + + template + struct BoundsRangeConvertible2 : std::true_type + { + }; + + template + struct BoundsRangeConvertible + : decltype(helpBoundsRangeConvertible( + SourceType(), TargetType(), + std::integral_constant::value || + TargetType::CurrentRange == dynamic_range || + SourceType::CurrentRange == dynamic_range)>())) + { + }; + + template + struct BoundsRangeConvertible : std::true_type + { + }; + + template , + details::BoundsRanges>::value>> + constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) + { + Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges::DynamicNum == 0) || + MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize()); + } + + constexpr static_bounds(std::initializer_list il) + : m_ranges(static_cast(il.begin())) + { + // Size of the initializer list must match the rank of the array + Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || + MyRanges::DynamicNum == il.size()); + // Size of the range must be less than the max element of the size type + Expects(m_ranges.totalSize() <= PTRDIFF_MAX); + } + + constexpr static_bounds() = default; + + constexpr static_bounds& operator=(const static_bounds& otherBounds) + { + new (&m_ranges) MyRanges(otherBounds.m_ranges); + return *this; + } + + constexpr sliced_type slice() const noexcept + { + return sliced_type{static_cast&>(m_ranges)}; + } + + constexpr size_type stride() const noexcept { return rank > 1 ? slice().size() : 1; } + + constexpr size_type size() const noexcept { return m_ranges.totalSize(); } + + constexpr size_type total_size() const noexcept { return m_ranges.totalSize(); } + + constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); } + + constexpr bool contains(const index_type& idx) const noexcept + { + return m_ranges.contains(idx) != -1; + } + + constexpr size_type operator[](size_t index) const noexcept + { + return m_ranges.elementNum(index); + } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < rank, + "dimension should be less than rank (dimension count starts from 0)"); + return details::createTypeListIndexer(m_ranges).template get().elementNum(); + } + + template + constexpr size_type extent(IntType dim) const noexcept + { + static_assert(std::is_integral::value, + "Dimension parameter must be supplied as an integral type."); + auto real_dim = narrow_cast(dim); + Expects(real_dim < rank); + + return m_ranges.elementNum(real_dim); + } + + constexpr index_type index_bounds() const noexcept + { + size_type extents[rank] = {}; + m_ranges.serialize(extents); + return {extents}; + } + + template + constexpr bool operator==(const static_bounds& rhs) const noexcept + { + return this->size() == rhs.size(); + } + + template + constexpr bool operator!=(const static_bounds& rhs) const noexcept + { + return !(*this == rhs); + } + + constexpr const_iterator begin() const noexcept { return const_iterator(*this, index_type{}); } + + constexpr const_iterator end() const noexcept + { + return const_iterator(*this, this->index_bounds()); + } +}; + +template +class strided_bounds +{ + template + friend class strided_bounds; + +public: + static const size_t rank = Rank; + using value_type = std::ptrdiff_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_const_t; + using size_type = value_type; + using difference_type = value_type; + using index_type = index; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + static const value_type dynamic_rank = rank; + static const value_type static_size = dynamic_range; + using sliced_type = std::conditional_t, void>; + using mapping_type = generalized_mapping_tag; + + constexpr strided_bounds(const strided_bounds&) noexcept = default; + + constexpr strided_bounds& operator=(const strided_bounds&) noexcept = default; + + constexpr strided_bounds(const value_type (&values)[rank], index_type strides) + : m_extents(values), m_strides(std::move(strides)) + { + } + + constexpr strided_bounds(const index_type& extents, const index_type& strides) noexcept + : m_extents(extents), + m_strides(strides) + { + } + + constexpr index_type strides() const noexcept { return m_strides; } + + constexpr size_type total_size() const noexcept + { + size_type ret = 0; + for (size_t i = 0; i < rank; ++i) { + ret += (m_extents[i] - 1) * m_strides[i]; + } + return ret + 1; + } + + constexpr size_type size() const noexcept + { + size_type ret = 1; + for (size_t i = 0; i < rank; ++i) { + ret *= m_extents[i]; + } + return ret; + } + + constexpr bool contains(const index_type& idx) const noexcept + { + for (size_t i = 0; i < rank; ++i) { + if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; + } + return true; + } + + constexpr size_type linearize(const index_type& idx) const noexcept + { + size_type ret = 0; + for (size_t i = 0; i < rank; i++) { + Expects(idx[i] < m_extents[i]); // index is out of bounds of the array + ret += idx[i] * m_strides[i]; + } + return ret; + } + + constexpr size_type stride() const noexcept { return m_strides[0]; } + + template 1), typename Ret = std::enable_if_t> + constexpr sliced_type slice() const + { + return {details::shift_left(m_extents), details::shift_left(m_strides)}; + } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, + "dimension should be less than rank (dimension count starts from 0)"); + return m_extents[Dim]; + } + + constexpr index_type index_bounds() const noexcept { return m_extents; } + constexpr const_iterator begin() const noexcept { return const_iterator{*this, index_type{}}; } + + constexpr const_iterator end() const noexcept { return const_iterator{*this, index_bounds()}; } + +private: + index_type m_extents; + index_type m_strides; +}; + +template +struct is_bounds : std::integral_constant +{ +}; +template +struct is_bounds> : std::integral_constant +{ +}; +template +struct is_bounds> : std::integral_constant +{ +}; + +template +class bounds_iterator : public std::iterator +{ +private: + using Base = std::iterator; + +public: + static const size_t rank = IndexType::rank; + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; + using index_type = value_type; + using index_size_type = typename IndexType::value_type; + template + explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept + : boundary_(bnd.index_bounds()), + curr_(std::move(curr)) + { + static_assert(is_bounds::value, "Bounds type must be provided"); + } + + constexpr reference operator*() const noexcept { return curr_; } + + constexpr pointer operator->() const noexcept { return &curr_; } + + constexpr bounds_iterator& operator++() noexcept + { + for (size_t i = rank; i-- > 0;) { + if (curr_[i] < boundary_[i] - 1) { + curr_[i]++; + return *this; + } + curr_[i] = 0; + } + // If we're here we've wrapped over - set to past-the-end. + curr_ = boundary_; + return *this; + } + + constexpr bounds_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + + constexpr bounds_iterator& operator--() noexcept + { + if (!less(curr_, boundary_)) { + // if at the past-the-end, set to last element + for (size_t i = 0; i < rank; ++i) { + curr_[i] = boundary_[i] - 1; + } + return *this; + } + for (size_t i = rank; i-- > 0;) { + if (curr_[i] >= 1) { + curr_[i]--; + return *this; + } + curr_[i] = boundary_[i] - 1; + } + // If we're here the preconditions were violated + // "pre: there exists s such that r == ++s" + Expects(false); + return *this; + } + + constexpr bounds_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + + constexpr bounds_iterator operator+(difference_type n) const noexcept + { + bounds_iterator ret{*this}; + return ret += n; + } + + constexpr bounds_iterator& operator+=(difference_type n) noexcept + { + auto linear_idx = linearize(curr_) + n; + std::remove_const_t stride = 0; + stride[rank - 1] = 1; + for (size_t i = rank - 1; i-- > 0;) { + stride[i] = stride[i + 1] * boundary_[i + 1]; + } + for (size_t i = 0; i < rank; ++i) { + curr_[i] = linear_idx / stride[i]; + linear_idx = linear_idx % stride[i]; + } + // index is out of bounds of the array + Expects(!less(curr_, index_type{}) && !less(boundary_, curr_)); + return *this; + } + + constexpr bounds_iterator operator-(difference_type n) const noexcept + { + bounds_iterator ret{*this}; + return ret -= n; + } + + constexpr bounds_iterator& operator-=(difference_type n) noexcept { return * this += -n; } + + constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept + { + return linearize(curr_) - linearize(rhs.curr_); + } + + constexpr value_type operator[](difference_type n) const noexcept { return *(*this + n); } + + constexpr bool operator==(const bounds_iterator& rhs) const noexcept + { + return curr_ == rhs.curr_; + } + + constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } + + constexpr bool operator<(const bounds_iterator& rhs) const noexcept + { + return less(curr_, rhs.curr_); + } + + constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } + + constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } + + constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } + + void swap(bounds_iterator& rhs) noexcept + { + std::swap(boundary_, rhs.boundary_); + std::swap(curr_, rhs.curr_); + } + +private: + constexpr bool less(index_type& one, index_type& other) const noexcept + { + for (size_t i = 0; i < rank; ++i) { + if (one[i] < other[i]) return true; + } + return false; + } + + constexpr index_size_type linearize(const value_type& idx) const noexcept + { + // TODO: Smarter impl. + // Check if past-the-end + index_size_type multiplier = 1; + index_size_type res = 0; + if (!less(idx, boundary_)) { + res = 1; + for (size_t i = rank; i-- > 0;) { + res += (idx[i] - 1) * multiplier; + multiplier *= boundary_[i]; + } + } + else + { + for (size_t i = rank; i-- > 0;) { + res += idx[i] * multiplier; + multiplier *= boundary_[i]; + } + } + return res; + } + + value_type boundary_; + std::remove_const_t curr_; +}; + +template +bounds_iterator operator+(typename bounds_iterator::difference_type n, + const bounds_iterator& rhs) noexcept +{ + return rhs + n; +} + +namespace details +{ + template + constexpr std::enable_if_t< + std::is_same::value, + typename Bounds::index_type> + make_stride(const Bounds& bnd) noexcept + { + return bnd.strides(); + } + + // Make a stride vector from bounds, assuming contiguous memory. + template + constexpr std::enable_if_t< + std::is_same::value, + typename Bounds::index_type> + make_stride(const Bounds& bnd) noexcept + { + auto extents = bnd.index_bounds(); + typename Bounds::size_type stride[Bounds::rank] = {}; + + stride[Bounds::rank - 1] = 1; + for (size_t i = 1; i < Bounds::rank; ++i) { + stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; + } + return {stride}; + } + + template + void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest) + { + 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"); + Expects(src.size() == dest.size()); + } + +} // namespace details + +template +class contiguous_span_iterator; +template +class general_span_iterator; +enum class byte : std::uint8_t +{ +}; + +template +struct dim +{ + static const std::ptrdiff_t value = DimSize; +}; +template <> +struct dim +{ + static const std::ptrdiff_t value = dynamic_range; + const std::ptrdiff_t dvalue; + dim(std::ptrdiff_t size) : dvalue(size) {} +}; + +template +class span; + +template +class strided_span; + +namespace details +{ + template + struct SpanTypeTraits + { + using value_type = T; + using size_type = size_t; + }; + + template + struct SpanTypeTraits::type> + { + using value_type = typename Traits::span_traits::value_type; + using size_type = typename Traits::span_traits::size_type; + }; + + template + struct SpanArrayTraits + { + using type = span; + using value_type = T; + using bounds_type = static_bounds; + using pointer = T*; + using reference = T&; + }; + template + struct SpanArrayTraits : SpanArrayTraits + { + }; + + template + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size + { + Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX); + return BoundsType{totalSize}; + } + template + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size + { + Expects(BoundsType::static_size <= totalSize); + return {}; + } + template + BoundsType newBoundsHelper(std::ptrdiff_t totalSize) + { + static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); + return newBoundsHelperImpl( + totalSize, std::integral_constant()); + } + + struct Sep + { + }; + + template + T static_as_span_helper(Sep, Args... args) + { + return T{narrow_cast(args)...}; + } + template + std::enable_if_t< + !std::is_same>::value && !std::is_same::value, T> + static_as_span_helper(Arg, Args... args) + { + return static_as_span_helper(args...); + } + template + T static_as_span_helper(dim val, Args... args) + { + return static_as_span_helper(args..., val.dvalue); + } + + template + struct static_as_span_static_bounds_helper + { + using type = static_bounds<(Dimensions::value)...>; + }; + + template + struct is_span_oracle : std::false_type + { + }; + + template + struct is_span_oracle> : std::true_type + { + }; + + template + struct is_span_oracle> : std::true_type + { + }; + + template + struct is_span : is_span_oracle> + { + }; +} + +template +class span +{ + // TODO do we still need this? + template + friend class span; + +public: + using bounds_type = static_bounds; + static const size_t Rank = bounds_type::rank; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using const_value_type = std::add_const_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using iterator = contiguous_span_iterator; + using const_span = span; + using const_iterator = contiguous_span_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = + std::conditional_t>; + +private: + pointer data_; + bounds_type bounds_; + + friend iterator; + friend const_iterator; + +public: + // default constructor - same as constructing from nullptr_t + constexpr span() noexcept : span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "Default construction of span only possible " + "for dynamic or fixed, zero-length spans."); + } + + // construct from nullptr - get an empty span + constexpr span(std::nullptr_t) noexcept : span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "nullptr_t construction of span only possible " + "for dynamic or fixed, zero-length spans."); + } + + // construct from nullptr with size of 0 (helps with template function calls) + template ::value>> + constexpr span(std::nullptr_t, IntType size) noexcept : span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "nullptr_t construction of span only possible " + "for dynamic or fixed, zero-length spans."); + Expects(size == 0); + } + + // construct from a single element + constexpr span(reference data) noexcept : span(&data, bounds_type{1}) + { + static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 || + bounds_type::static_size == 1, + "Construction from a single element only possible " + "for dynamic or fixed spans of length 0 or 1."); + } + + // prevent constructing from temporaries for single-elements + constexpr span(value_type&&) = delete; + + // construct from pointer + length + constexpr span(pointer ptr, size_type size) noexcept : span(ptr, bounds_type{size}) {} + + // construct from pointer + length - multidimensional + constexpr span(pointer data, bounds_type bounds) noexcept : data_(data), + bounds_(std::move(bounds)) + { + Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); + } + + // construct from begin,end pointer pair + template ::value && + details::LessThan::value>> + constexpr span(pointer begin, Ptr end) + : span(begin, details::newBoundsHelper(static_cast(end) - begin)) + { + Expects(begin != nullptr && end != nullptr && begin <= static_cast(end)); + } + + // construct from n-dimensions static array + template > + constexpr span(T (&arr)[N]) + : span(reinterpret_cast(arr), bounds_type{typename Helper::bounds_type{}}) + { + static_assert( + std::is_convertible::value, + "Cannot convert from source type to target span type."); + static_assert(std::is_convertible::value, + "Cannot construct a span from an array with fewer elements."); + } + + // construct from n-dimensions dynamic array (e.g. new int[m][4]) + // (precedence will be lower than the 1-dimension pointer) + template > + constexpr span(T* const& data, size_type size) + : span(reinterpret_cast(data), typename Helper::bounds_type{size}) + { + static_assert( + std::is_convertible::value, + "Cannot convert from source type to target span type."); + } + + // construct from std::array + template + constexpr span(std::array& arr) : span(arr.data(), bounds_type{static_bounds{}}) + { + static_assert( + std::is_convertible(*) []>::value, + "Cannot convert from source type to target span type."); + static_assert(std::is_convertible, bounds_type>::value, + "You cannot construct a span from a std::array of smaller size."); + } + + // construct from const std::array + template + constexpr span(const std::array, N>& arr) + : span(arr.data(), static_bounds()) + { + static_assert(std::is_convertible>::value, + "Cannot convert from source type to target span type."); + static_assert(std::is_convertible, bounds_type>::value, + "You cannot construct a span from a std::array of smaller size."); + } + + // prevent constructing from temporary std::array + template + constexpr span(std::array&& arr) = delete; + + // construct from containers + // future: could use contiguous_iterator_traits to identify only contiguous containers + // type-requirements: container must have .size(), operator[] which are value_type compatible + template ::value && + std::is_convertible::value && + std::is_same().size(), + *std::declval().data())>, + DataType>::value>> + constexpr span(Cont& cont) + : span(static_cast(cont.data()), + details::newBoundsHelper(narrow_cast(cont.size()))) + { + } + + // prevent constructing from temporary containers + template ::value && + std::is_convertible::value && + std::is_same().size(), + *std::declval().data())>, + DataType>::value>> + explicit constexpr span(Cont&& cont) = delete; + + // construct from a convertible span + template , + typename = std::enable_if_t::value && + std::is_convertible::value>> + constexpr span(span other) noexcept : data_(other.data_), + bounds_(other.bounds_) + { + } + +// trivial copy and move +#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + constexpr span(span&&) = default; +#endif + constexpr span(const span&) = default; + +// trivial assignment +#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + constexpr span& operator=(span&&) = default; +#endif + constexpr span& operator=(const span&) = default; + + // first() - extract the first Count elements into a new span + template + constexpr span first() const noexcept + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + Count <= bounds_type::static_size, + "Count is out of bounds."); + + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); + return {this->data(), Count}; + } + + // first() - extract the first count elements into a new span + constexpr span first(size_type count) const noexcept + { + Expects(count >= 0 && count <= this->size()); + return {this->data(), count}; + } + + // last() - extract the last Count elements into a new span + template + constexpr span last() const noexcept + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + Count <= bounds_type::static_size, + "Count is out of bounds."); + + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); + return {this->data() + this->size() - Count, Count}; + } + + // last() - extract the last count elements into a new span + constexpr span last(size_type count) const noexcept + { + Expects(count >= 0 && count <= this->size()); + return {this->data() + this->size() - count, count}; + } + + // subspan() - create a subview of Count elements starting at Offset + template + constexpr span subspan() const noexcept + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(Offset >= 0, "Offset must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + ((Offset <= bounds_type::static_size) && + Count <= bounds_type::static_size - Offset), + "You must describe a sub-range within bounds of the span."); + + Expects(bounds_type::static_size != dynamic_range || + (Offset <= this->size() && Count <= this->size() - Offset)); + return {this->data() + Offset, Count}; + } + + // subspan() - create a subview of count elements starting at offset + // supplying dynamic_range for count will consume all available elements from offset + constexpr span subspan(size_type offset, + size_type count = dynamic_range) const noexcept + { + Expects((offset >= 0 && offset <= this->size()) && + (count == dynamic_range || (count <= this->size() - offset))); + return {this->data() + offset, count == dynamic_range ? this->length() - offset : count}; + } + + // section - creates a non-contiguous, strided span from a contiguous one + constexpr strided_span section(index_type origin, index_type extents) const + noexcept + { + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + return {&this->operator[](origin), size, + strided_bounds{extents, details::make_stride(bounds())}}; + } + + // length of the span in elements + constexpr size_type size() const noexcept { return bounds_.size(); } + + // length of the span in elements + constexpr size_type length() const noexcept { return this->size(); } + + // length of the span in bytes + constexpr size_type size_bytes() const noexcept { return sizeof(value_type) * this->size(); } + + // length of the span in bytes + constexpr size_type length_bytes() const noexcept { return this->size_bytes(); } + + constexpr bool empty() const noexcept { return this->size() == 0; } + + static constexpr std::size_t rank() { return Rank; } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, + "Dimension should be less than rank (dimension count starts from 0)."); + return bounds_.template extent(); + } + + template + constexpr size_type extent(IntType dim) const noexcept + { + return bounds_.extent(dim); + } + + constexpr bounds_type bounds() const noexcept { return bounds_; } + + constexpr pointer data() const noexcept { return data_; } + + template + constexpr reference operator()(FirstIndex index) + { + return this->operator[](narrow_cast(index)); + } + + template + constexpr reference operator()(FirstIndex index, OtherIndices... indices) + { + index_type idx = {narrow_cast(index), + narrow_cast(indices...)}; + return this->operator[](idx); + } + + constexpr reference operator[](const index_type& idx) const noexcept + { + return data_[bounds_.linearize(idx)]; + } + + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const noexcept + { + Expects(idx < bounds_.size()); // index is out of bounds of the array + const size_type ridx = idx * bounds_.stride(); + + // index is out of bounds of the underlying data + Expects(ridx < bounds_.total_size()); + return Ret{data_ + ridx, bounds_.slice()}; + } + + constexpr iterator begin() const noexcept { return iterator{this, true}; } + + constexpr iterator end() const noexcept { return iterator{this, false}; } + + constexpr const_iterator cbegin() const noexcept + { + return const_iterator{reinterpret_cast(this), true}; + } + + constexpr const_iterator cend() const noexcept + { + return const_iterator{reinterpret_cast(this), false}; + } + + constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } + + constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } + + constexpr const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator{cend()}; + } + + constexpr const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator{cbegin()}; + } + + template , std::remove_cv_t>::value>> + constexpr bool operator==(const span& other) const noexcept + { + return bounds_.size() == other.bounds_.size() && + (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator!=(const span& other) const noexcept + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<(const span& other) const noexcept + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<=(const span& other) const noexcept + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>(const span& other) const noexcept + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>=(const span& other) const noexcept + { + return !(*this < other); + } +}; + +// +// Free functions for manipulating spans +// + +// reshape a span into a different dimensionality +// DimCount and Enabled here are workarounds for a bug in MSVC 2015 +template 0), typename = std::enable_if_t> +constexpr span as_span(SpanType s, + Dimensions2... dims) +{ + static_assert(details::is_span::value, + "Variadic as_span() is for reshaping existing spans."); + using BoundsType = + typename span::bounds_type; + auto tobounds = details::static_as_span_helper(dims..., details::Sep{}); + details::verifyBoundsReshape(s.bounds(), tobounds); + return {s.data(), tobounds}; +} + +// convert a span to a span +template +span as_bytes(span s) noexcept +{ + static_assert(std::is_trivial>::value, + "The value_type of span must be a trivial type."); + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// convert a span to a span (a writeable byte span) +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +span as_writeable_bytes(span s) noexcept +{ + static_assert(std::is_trivial>::value, + "The value_type of span must be a trivial type."); + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// convert a span to a span +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +constexpr auto as_span(span s) noexcept + -> span( + span::bounds_type::static_size != dynamic_range + ? (static_cast( + span::bounds_type::static_size) / + sizeof(U)) + : dynamic_range)> +{ + using ConstByteSpan = span; + static_assert( + std::is_trivial>::value && + (ConstByteSpan::bounds_type::static_size == dynamic_range || + ConstByteSpan::bounds_type::static_size % narrow_cast(sizeof(U)) == 0), + "Target type must be a trivial type and its size must match the byte array size"); + + Expects((s.size_bytes() % sizeof(U)) == 0 && (s.size_bytes() / sizeof(U)) < PTRDIFF_MAX); + return {reinterpret_cast(s.data()), + s.size_bytes() / narrow_cast(sizeof(U))}; +} + +// convert a span to a span +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +constexpr auto as_span(span s) noexcept -> span< + U, narrow_cast( + span::bounds_type::static_size != dynamic_range + ? static_cast(span::bounds_type::static_size) / + sizeof(U) + : dynamic_range)> +{ + using ByteSpan = span; + static_assert( + std::is_trivial>::value && + (ByteSpan::bounds_type::static_size == dynamic_range || + ByteSpan::bounds_type::static_size % static_cast(sizeof(U)) == 0), + "Target type must be a trivial type and its size must match the byte array size"); + + Expects((s.size_bytes() % sizeof(U)) == 0); + return {reinterpret_cast(s.data()), + s.size_bytes() / narrow_cast(sizeof(U))}; +} + +template +constexpr auto as_span(T* const& ptr, dim... args) + -> span, Dimensions...> +{ + return {reinterpret_cast*>(ptr), + details::static_as_span_helper>(args..., details::Sep{})}; +} + +template +constexpr auto as_span(T* arr, std::ptrdiff_t len) -> + typename details::SpanArrayTraits::type +{ + return {reinterpret_cast*>(arr), len}; +} + +template +constexpr auto as_span(T (&arr)[N]) -> typename details::SpanArrayTraits::type +{ + return {arr}; +} + +template +constexpr span as_span(const std::array& arr) +{ + return {arr}; +} + +template +constexpr span as_span(const std::array&&) = delete; + +template +constexpr span as_span(std::array& arr) +{ + return {arr}; +} + +template +constexpr span as_span(T* begin, T* end) +{ + return {begin, end}; +} + +template +constexpr auto as_span(Cont& arr) -> std::enable_if_t< + !details::is_span>::value, + span, dynamic_range>> +{ + Expects(arr.size() < PTRDIFF_MAX); + return {arr.data(), narrow_cast(arr.size())}; +} + +template +constexpr auto as_span(Cont&& arr) -> std::enable_if_t< + !details::is_span>::value, + span, dynamic_range>> = delete; + +// from basic_string which doesn't have nonconst .data() member like other contiguous containers +template +constexpr auto as_span(std::basic_string& str) + -> span +{ + Expects(str.size() < PTRDIFF_MAX); + return {&str[0], narrow_cast(str.size())}; +} + +// strided_span is an extension that is not strictly part of the GSL at this time. +// It is kept here while the multidimensional interface is still being defined. +template +class strided_span +{ +public: + using bounds_type = strided_bounds; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using const_value_type = std::add_const_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using iterator = general_span_iterator; + using const_strided_span = strided_span; + using const_iterator = general_span_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = + std::conditional_t>; + +private: + pointer data_; + bounds_type bounds_; + + friend iterator; + friend const_iterator; + template + friend class strided_span; + +public: + // from raw data + constexpr strided_span(pointer ptr, size_type size, bounds_type bounds) + : data_(ptr), bounds_(std::move(bounds)) + { + Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0); + // Bounds cross data boundaries + Expects(this->bounds().total_size() <= size); + (void) size; + } + + // from static array of size N + template + constexpr strided_span(value_type (&values)[N], bounds_type bounds) + : strided_span(values, N, std::move(bounds)) + { + } + + // from array view + template ::value, + typename Dummy = std::enable_if_t> + constexpr strided_span(span av, bounds_type bounds) + : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) + { + } + + // convertible + template ::value>> + constexpr strided_span(const strided_span& other) + : data_(other.data_), bounds_(other.bounds_) + { + } + + // convert from bytes + template + constexpr strided_span< + typename std::enable_if::value, OtherValueType>::type, + Rank> + as_strided_span() const + { + static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && + (sizeof(OtherValueType) % sizeof(value_type) == 0), + "OtherValueType should have a size to contain a multiple of ValueTypes"); + auto d = narrow_cast(sizeof(OtherValueType) / sizeof(value_type)); + + size_type size = this->bounds().total_size() / d; + return {const_cast(reinterpret_cast(this->data())), size, + bounds_type{resize_extent(this->bounds().index_bounds(), d), + resize_stride(this->bounds().strides(), d)}}; + } + + constexpr strided_span section(index_type origin, index_type extents) const + { + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + return {&this->operator[](origin), size, + bounds_type{extents, details::make_stride(bounds())}}; + } + + constexpr reference operator[](const index_type& idx) const + { + return data_[bounds_.linearize(idx)]; + } + + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const + { + Expects(idx < bounds_.size()); // index is out of bounds of the array + const size_type ridx = idx * bounds_.stride(); + + // index is out of bounds of the underlying data + Expects(ridx < bounds_.total_size()); + return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()}; + } + + constexpr bounds_type bounds() const noexcept { return bounds_; } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, + "dimension should be less than Rank (dimension count starts from 0)"); + return bounds_.template extent(); + } + + constexpr size_type size() const noexcept { return bounds_.size(); } + + constexpr pointer data() const noexcept { return data_; } + + constexpr explicit operator bool() const noexcept { return data_ != nullptr; } + + constexpr iterator begin() const { return iterator{this, true}; } + + constexpr iterator end() const { return iterator{this, false}; } + + constexpr const_iterator cbegin() const + { + return const_iterator{reinterpret_cast(this), true}; + } + + constexpr const_iterator cend() const + { + return const_iterator{reinterpret_cast(this), false}; + } + + constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; } + + constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; } + + constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; } + + constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; } + + template , std::remove_cv_t>::value>> + constexpr bool operator==(const strided_span& other) const noexcept + { + return bounds_.size() == other.bounds_.size() && + (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator!=(const strided_span& other) const noexcept + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<(const strided_span& other) const noexcept + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<=(const strided_span& other) const noexcept + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>(const strided_span& other) const noexcept + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>=(const strided_span& other) const noexcept + { + return !(*this < other); + } + +private: + static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) + { + // 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; + + return ret; + } + + template > + static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = 0) + { + // Only strided arrays with regular strides can be resized + Expects(strides[Rank - 1] == 1); + + return strides; + } + + template 1), typename Dummy = std::enable_if_t> + static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) + { + // Only strided arrays with regular strides can be resized + Expects(strides[Rank - 1] == 1); + // The strides must have contiguous chunks of + // memory that can contain a multiple of new type elements + Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0)); + + for (size_t i = Rank - 1; i > 0; --i) { + // Only strided arrays with regular strides can be resized + Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0)); + } + + index_type ret = strides / d; + ret[Rank - 1] = 1; + + return ret; + } +}; + +template +class contiguous_span_iterator + : public std::iterator +{ + using Base = std::iterator; + +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + +private: + template + friend class span; + + pointer data_; + const Span* m_validator; + void validateThis() const + { + // iterator is out of range of the array + Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size()); + } + contiguous_span_iterator(const Span* container, bool isbegin) + : data_(isbegin ? container->data_ : container->data_ + container->size()) + , m_validator(container) + { + } + +public: + reference operator*() const noexcept + { + validateThis(); + return *data_; + } + pointer operator->() const noexcept + { + validateThis(); + return data_; + } + contiguous_span_iterator& operator++() noexcept + { + ++data_; + return *this; + } + contiguous_span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + contiguous_span_iterator& operator--() noexcept + { + --data_; + return *this; + } + contiguous_span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + contiguous_span_iterator operator+(difference_type n) const noexcept + { + contiguous_span_iterator ret{*this}; + return ret += n; + } + contiguous_span_iterator& operator+=(difference_type n) noexcept + { + data_ += n; + return *this; + } + contiguous_span_iterator operator-(difference_type n) const noexcept + { + contiguous_span_iterator ret{*this}; + return ret -= n; + } + contiguous_span_iterator& operator-=(difference_type n) noexcept { return * this += -n; } + difference_type operator-(const contiguous_span_iterator& rhs) const noexcept + { + Expects(m_validator == rhs.m_validator); + return data_ - rhs.data_; + } + reference operator[](difference_type n) const noexcept { return *(*this + n); } + bool operator==(const contiguous_span_iterator& rhs) const noexcept + { + Expects(m_validator == rhs.m_validator); + return data_ == rhs.data_; + } + bool operator!=(const contiguous_span_iterator& rhs) const noexcept { return !(*this == rhs); } + bool operator<(const contiguous_span_iterator& rhs) const noexcept + { + Expects(m_validator == rhs.m_validator); + return data_ < rhs.data_; + } + bool operator<=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs < *this); } + bool operator>(const contiguous_span_iterator& rhs) const noexcept { return rhs < *this; } + bool operator>=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs > *this); } + void swap(contiguous_span_iterator& rhs) noexcept + { + std::swap(data_, rhs.data_); + std::swap(m_validator, rhs.m_validator); + } +}; + +template +contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, + const contiguous_span_iterator& rhs) noexcept +{ + return rhs + n; +} + +template +class general_span_iterator + : public std::iterator +{ + using Base = std::iterator; + +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; + +private: + template + friend class strided_span; + + const Span* m_container; + typename Span::bounds_type::iterator m_itr; + general_span_iterator(const Span* container, bool isbegin) + : m_container(container) + , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) + { + } + +public: + reference operator*() noexcept { return (*m_container)[*m_itr]; } + pointer operator->() noexcept { return &(*m_container)[*m_itr]; } + general_span_iterator& operator++() noexcept + { + ++m_itr; + return *this; + } + general_span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + general_span_iterator& operator--() noexcept + { + --m_itr; + return *this; + } + general_span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + general_span_iterator operator+(difference_type n) const noexcept + { + general_span_iterator ret{*this}; + return ret += n; + } + general_span_iterator& operator+=(difference_type n) noexcept + { + m_itr += n; + return *this; + } + general_span_iterator operator-(difference_type n) const noexcept + { + general_span_iterator ret{*this}; + return ret -= n; + } + general_span_iterator& operator-=(difference_type n) noexcept { return * this += -n; } + difference_type operator-(const general_span_iterator& rhs) const noexcept + { + Expects(m_container == rhs.m_container); + return m_itr - rhs.m_itr; + } + value_type operator[](difference_type n) const noexcept + { + return (*m_container)[m_itr[n]]; + ; + } + bool operator==(const general_span_iterator& rhs) const noexcept + { + Expects(m_container == rhs.m_container); + return m_itr == rhs.m_itr; + } + bool operator!=(const general_span_iterator& rhs) const noexcept { return !(*this == rhs); } + bool operator<(const general_span_iterator& rhs) const noexcept + { + Expects(m_container == rhs.m_container); + return m_itr < rhs.m_itr; + } + bool operator<=(const general_span_iterator& rhs) const noexcept { return !(rhs < *this); } + bool operator>(const general_span_iterator& rhs) const noexcept { return rhs < *this; } + bool operator>=(const general_span_iterator& rhs) const noexcept { return !(rhs > *this); } + void swap(general_span_iterator& rhs) noexcept + { + std::swap(m_itr, rhs.m_itr); + std::swap(m_container, rhs.m_container); + } +}; + +template +general_span_iterator operator+(typename general_span_iterator::difference_type n, + const general_span_iterator& rhs) noexcept +{ + return rhs + n; +} + +} // namespace gsl + +#ifdef _MSC_VER + +#undef constexpr +#pragma pop_macro("constexpr") + +#if _MSC_VER <= 1800 +#pragma warning(pop) + +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION +#undef noexcept +#pragma pop_macro("noexcept") +#endif // GSL_THROW_ON_CONTRACT_VIOLATION + +#undef GSL_MSVC_HAS_VARIADIC_CTOR_BUG + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) + +#undef noexcept + +#ifdef _MSC_VER +#pragma warning(pop) +#pragma pop_macro("noexcept") +#endif + +#endif // GSL_THROW_ON_CONTRACT_VIOLATION + +#endif // GSL_SPAN_H diff --git a/lib/gsl/include/string_span.h b/lib/gsl/include/string_span.h new file mode 100644 index 0000000..46bf2d4 --- /dev/null +++ b/lib/gsl/include/string_span.h @@ -0,0 +1,963 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_STRING_SPAN_H +#define GSL_STRING_SPAN_H + +#include "gsl_assert.h" +#include "gsl_util.h" +#include "span.h" +#include +#include + +#ifdef _MSC_VER + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr /* nothing */ + +// VS 2013 workarounds +#if _MSC_VER <= 1800 + +#define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG +#define GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE +#define GSL_MSVC_NO_CPP14_STD_EQUAL +#define GSL_MSVC_NO_DEFAULT_MOVE_CTOR + +// noexcept is not understood +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION +#pragma push_macro("noexcept") +#define noexcept /* nothing */ +#endif + +#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_THROW_ON_CONTRACT_VIOLATION + +#ifdef _MSC_VER +#pragma push_macro("noexcept") +#endif + +#define noexcept /* nothing */ + +#endif // GSL_THROW_ON_CONTRACT_VIOLATION + +namespace gsl +{ +// +// czstring and wzstring +// +// These are "tag" typedef's for C-style strings (i.e. null-terminated character arrays) +// that allow static analysis to help find bugs. +// +// There are no additional features/semantics that we can find a way to add inside the +// type system for these types that will not either incur significant runtime costs or +// (sometimes needlessly) break existing programs when introduced. +// + +template +using basic_zstring = CharT*; + +template +using czstring = basic_zstring; + +template +using cwzstring = basic_zstring; + +template +using zstring = basic_zstring; + +template +using wzstring = basic_zstring; + +// +// ensure_sentinel() +// +// Provides a way to obtain an span from a contiguous sequence +// that ends with a (non-inclusive) sentinel value. +// +// Will fail-fast if sentinel cannot be found before max elements are examined. +// +template +span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) +{ + auto cur = seq; + while ((cur - seq) < max && *cur != Sentinel) ++cur; + Ensures(*cur == Sentinel); + return{ seq, cur - seq }; +} + + +// +// ensure_z - creates a span for a czstring or cwzstring. +// Will fail fast if a null-terminator cannot be found before +// the limit of size_type. +// +template +inline span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) +{ + return ensure_sentinel(sz, max); +} + +// TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation +inline span ensure_z(char* const& sz, std::ptrdiff_t max) +{ + auto len = strnlen(sz, narrow_cast(max)); + Ensures(sz[len] == 0); + return{ sz, static_cast(len) }; +} + +inline span ensure_z(const char* const& sz, std::ptrdiff_t max) +{ + auto len = strnlen(sz, narrow_cast(max)); + Ensures(sz[len] == 0); + return{ sz, static_cast(len) }; +} + +inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) +{ + auto len = wcsnlen(sz, narrow_cast(max)); + Ensures(sz[len] == 0); + return{ sz, static_cast(len) }; +} + +inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) +{ + auto len = wcsnlen(sz, narrow_cast(max)); + Ensures(sz[len] == 0); + return{ sz, static_cast(len) }; +} + +template +span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } + +template +span::type, dynamic_range> ensure_z(Cont& cont) +{ + return ensure_z(cont.data(), static_cast(cont.length())); +} + +template +class basic_string_span; + +namespace details +{ + template + struct is_basic_string_span_oracle : std::false_type + {}; + + template + struct is_basic_string_span_oracle> : std::true_type + {}; + + template + struct is_basic_string_span : is_basic_string_span_oracle> + {}; + + template + struct length_func + {}; + + template <> + struct length_func + { + std::ptrdiff_t operator()(char* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(strnlen(ptr, narrow_cast(length))); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(wchar_t* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(wcsnlen(ptr, narrow_cast(length))); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(strnlen(ptr, narrow_cast(length))); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(wcsnlen(ptr, narrow_cast(length))); + } + }; +} + + +// +// string_span and relatives +// +// Note that Extent is always single-dimension only +// +template +class basic_string_span +{ +public: + using value_type = CharT; + using const_value_type = std::add_const_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t; + using bounds_type = static_bounds; + using impl_type = span; + + using size_type = ptrdiff_t; + using iterator = typename impl_type::iterator; + using const_iterator = typename impl_type::const_iterator; + using reverse_iterator = typename impl_type::reverse_iterator; + using const_reverse_iterator = typename impl_type::const_reverse_iterator; + + // default (empty) + constexpr basic_string_span() = default; + + // copy + constexpr basic_string_span(const basic_string_span& other) = default; + + // move +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_string_span(basic_string_span&& other) = default; +#else + constexpr basic_string_span(basic_string_span&& other) + : span_(std::move(other.span_)) + {} +#endif + + // assign + constexpr basic_string_span& operator=(const basic_string_span& other) = default; + + // move assign +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_string_span& operator=(basic_string_span&& other) = default; +#else + constexpr basic_string_span& operator=(basic_string_span&& other) + { + span_ = std::move(other.span_); + return *this; + } +#endif + + // from nullptr + constexpr basic_string_span(std::nullptr_t ptr) noexcept + : span_(ptr) + {} + + // from nullptr and length + constexpr basic_string_span(std::nullptr_t ptr, size_type length) noexcept + : span_(ptr, length) + {} + + // From static arrays - if 0-terminated, remove 0 from the view + + // from static arrays and string literals + template + constexpr basic_string_span(value_type(&arr)[N]) noexcept + : span_(remove_z(arr)) + {} + + // Those allow 0s within the length, so we do not remove them + + // from raw data and length + constexpr basic_string_span(pointer ptr, size_type length) noexcept + : span_(ptr, length) + {} + + // from string + constexpr basic_string_span(std::string& s) noexcept + : span_(const_cast(s.data()), narrow_cast(s.length())) + {} + + // from containers. Containers must have .size() and .data() function signatures + template ::value + && !details::is_basic_string_span::value + && !(!std::is_const::value && std::is_const::value) // no converting const containers to non-const span + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> + > + constexpr basic_string_span(Cont& cont) + : span_(cont.data(), cont.size()) + {} + + // disallow creation from temporary containers and strings + template ::value + && !details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> + > + basic_string_span(Cont&& cont) = delete; + +#ifndef GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE + // from span + template ::value + && std::is_convertible, bounds_type>::value> + > + constexpr basic_string_span(span other) noexcept + : span_(other) + {} +#else + // from span + constexpr basic_string_span(span other) noexcept + : span_(other) + {} + + template , value_type>::value>> + constexpr basic_string_span(span, Extent> other) noexcept + : span_(other) + {} +#endif + + // from string_span + template , + typename Dummy = std::enable_if_t::value && std::is_convertible::value> + > + constexpr basic_string_span(basic_string_span other) noexcept + : span_(other.data(), other.length()) + {} + + constexpr bool empty() const noexcept + { + return length() == 0; + } + + // first Count elements + template + constexpr basic_string_span first() const noexcept + { + return{ span_.template first() }; + } + + constexpr basic_string_span first(size_type count) const noexcept + { + return{ span_.first(count) }; + } + + // last Count elements + template + constexpr basic_string_span last() const noexcept + { + return{ span_.template last() }; + } + + constexpr basic_string_span last(size_type count) const noexcept + { + return{ span_.last(count) }; + } + + // create a subview of Count elements starting from Offset + template + constexpr basic_string_span subspan() const noexcept + { + return{ span_.template subspan() }; + } + + constexpr basic_string_span subspan(size_type offset, size_type count = dynamic_range) const noexcept + { + return{ span_.subspan(offset, count) }; + } + + constexpr reference operator[](size_type idx) const noexcept + { + return span_[idx]; + } + + constexpr pointer data() const noexcept + { + return span_.data(); + } + + // length of the span in elements + constexpr size_type length() const noexcept + { + return span_.size(); + } + + // length of the span in elements + constexpr size_type size() const noexcept + { + return span_.size(); + } + + // length of the span in bytes + constexpr size_type size_bytes() const noexcept + { + return span_.size_bytes(); + } + + // length of the span in bytes + constexpr size_type length_bytes() const noexcept + { + return span_.length_bytes(); + } + + constexpr iterator begin() const noexcept + { + return span_.begin(); + } + + constexpr iterator end() const noexcept + { + return span_.end(); + } + + constexpr const_iterator cbegin() const noexcept + { + return span_.cbegin(); + } + + constexpr const_iterator cend() const noexcept + { + return span_.cend(); + } + + constexpr reverse_iterator rbegin() const noexcept + { + return span_.rbegin(); + } + + constexpr reverse_iterator rend() const noexcept + { + return span_.rend(); + } + + constexpr const_reverse_iterator crbegin() const noexcept + { + return span_.crbegin(); + } + + constexpr const_reverse_iterator crend() const noexcept + { + return span_.crend(); + } + +private: + + static impl_type remove_z(pointer const& sz, std::ptrdiff_t max) noexcept + { + return{ sz, details::length_func()(sz, max)}; + } + + template + static impl_type remove_z(value_type(&sz)[N]) noexcept + { + return remove_z(&sz[0], narrow_cast(N)); + } + + impl_type span_; +}; + +template +using string_span = basic_string_span; + +template +using cstring_span = basic_string_span; + +template +using wstring_span = basic_string_span; + +template +using cwstring_span = basic_string_span; + +// +// to_string() allow (explicit) conversions from string_span to string +// +#ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG + +template +std::basic_string::type> to_string(basic_string_span view) +{ + return{ view.data(), static_cast(view.length()) }; +} + +#else + +inline std::string to_string(cstring_span<> view) +{ + return{ view.data(), static_cast(view.length()) }; +} + +inline std::string to_string(string_span<> view) +{ + return{ view.data(), static_cast(view.length()) }; +} + +inline std::wstring to_string(cwstring_span<> view) +{ + return{ view.data(), static_cast(view.length()) }; +} + +inline std::wstring to_string(wstring_span<> view) +{ + return{ view.data(), static_cast(view.length()) }; +} + +#endif + +// zero-terminated string span, used to convert +// zero-terminated spans to legacy strings +template +class basic_zstring_span +{ +public: + using value_type = CharT; + using const_value_type = std::add_const_t; + + using pointer = std::add_pointer_t; + using const_pointer = std::add_pointer_t; + + using zstring_type = basic_zstring; + using const_zstring_type = basic_zstring; + + using impl_type = span; + using string_span_type = basic_string_span; + + constexpr basic_zstring_span(impl_type span) noexcept + : span_(span) + { + // expects a zero-terminated span + Expects(span[span.size() - 1] == '\0'); + } + + // copy + constexpr basic_zstring_span(const basic_zstring_span& other) = default; + + // move +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_zstring_span(basic_zstring_span&& other) = default; +#else + constexpr basic_zstring_span(basic_zstring_span&& other) + : span_(std::move(other.span_)) + {} +#endif + + // assign + constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default; + + // move assign +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default; +#else + constexpr basic_zstring_span& operator=(basic_zstring_span&& other) + { + span_ = std::move(other.span_); + return *this; + } +#endif + + constexpr bool empty() const noexcept { return span_.size() == 0; } + + constexpr string_span_type as_string_span() const noexcept { return span_.first(span_.size()-1); } + + constexpr string_span_type ensure_z() const noexcept { return gsl::ensure_z(span_); } + + constexpr const_zstring_type assume_z() const noexcept { return span_.data(); } + +private: + impl_type span_; +}; + +template +using zstring_span = basic_zstring_span; + +template +using wzstring_span = basic_zstring_span; + +template +using czstring_span = basic_zstring_span; + +template +using cwzstring_span = basic_zstring_span; + +} // namespace GSL + +// operator == +template , Extent>>::value> +> +bool operator==(gsl::basic_string_span one, const T& other) noexcept +{ + gsl::basic_string_span, Extent> tmp(other); +#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL + return (one.size() == tmp.size()) && std::equal(one.begin(), one.end(), tmp.begin()); +#else + return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); +#endif +} + +template , Extent>>::value + && !gsl::details::is_basic_string_span::value> +> +bool operator==(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, Extent> tmp(one); +#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL + return (tmp.size() == other.size()) && std::equal(tmp.begin(), tmp.end(), other.begin()); +#else + return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); +#endif +} + +#ifndef _MSC_VER + +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator==(gsl::basic_string_span one, const T& other) noexcept +{ + gsl::basic_string_span, Extent> tmp(other); + return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator==(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, Extent> tmp(one); + return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); +} +#endif + +// operator != +template , Extent>>::value> +> +bool operator!=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one == other); +} + +template , Extent>>::value + && !gsl::details::is_basic_string_span::value> +> +bool operator!=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(one == other); +} + +#ifndef _MSC_VER + +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator!=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one == other); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator!=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(one == other); +} +#endif + +// operator< +template , Extent>>::value> +> +bool operator<(gsl::basic_string_span one, const T& other) noexcept +{ + gsl::basic_string_span, Extent> tmp(other); + return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template , Extent>>::value + && !gsl::details::is_basic_string_span::value> +> +bool operator<(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, Extent> tmp(one); + return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); +} + +#ifndef _MSC_VER + +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator<(gsl::basic_string_span one, const T& other) noexcept +{ + gsl::basic_string_span, Extent> tmp(other); + return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator<(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, Extent> tmp(one); + return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); +} +#endif + +// operator <= +template , Extent>>::value> +> +bool operator<=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(other < one); +} + +template , Extent>>::value + && !gsl::details::is_basic_string_span::value> +> +bool operator<=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(other < one); +} + +#ifndef _MSC_VER + +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator<=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(other < one); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator<=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(other < one); +} +#endif + +// operator> +template , Extent>>::value> +> +bool operator>(gsl::basic_string_span one, const T& other) noexcept +{ + return other < one; +} + +template , Extent>>::value + && !gsl::details::is_basic_string_span::value> +> +bool operator>(const T& one, gsl::basic_string_span other) noexcept +{ + return other < one; +} + +#ifndef _MSC_VER + +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator>(gsl::basic_string_span one, const T& other) noexcept +{ + return other < one; +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator>(const T& one, gsl::basic_string_span other) noexcept +{ + return other < one; +} +#endif + +// operator >= +template , Extent>>::value> +> +bool operator>=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one < other); +} + +template , Extent>>::value + && !gsl::details::is_basic_string_span::value> +> +bool operator>=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(one < other); +} + +#ifndef _MSC_VER + +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator>=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one < other); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator>=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(one < other); +} +#endif + +// VS 2013 workarounds +#ifdef _MSC_VER + +#undef constexpr +#pragma pop_macro("constexpr") + +#if _MSC_VER <= 1800 + +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION +#undef noexcept +#pragma pop_macro("noexcept") +#endif // GSL_THROW_ON_CONTRACT_VIOLATION + +#undef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG +#undef GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE +#undef GSL_MSVC_NO_CPP14_STD_EQUAL +#undef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + +#endif // _MSC_VER <= 1800 +#endif // _MSC_VER + +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) + +#undef noexcept + +#ifdef _MSC_VER +#pragma pop_macro("noexcept") +#endif + +#endif // GSL_THROW_ON_CONTRACT_VIOLATION +#endif // GSL_STRING_SPAN_H diff --git a/lib/gsl/tests/CMakeLists.txt b/lib/gsl/tests/CMakeLists.txt new file mode 100644 index 0000000..7990ec3 --- /dev/null +++ b/lib/gsl/tests/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 2.8.7) + +project(GSLTests CXX) + +add_subdirectory(unittest-cpp) + +include_directories( + ../include + ./unittest-cpp +) + +add_definitions(-DGSL_THROW_ON_CONTRACT_VIOLATION) + +if(MSVC14 OR MSVC12) # has the support we need + # remove unnecessary warnings about unchecked iterators + add_definitions(-D_SCL_SECURE_NO_WARNINGS) + add_compile_options(/W4) +else() + include(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14) + CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) + if(COMPILER_SUPPORTS_CXX14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Wno-missing-braces") + elseif(COMPILER_SUPPORTS_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-missing-braces") + else() + message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") + endif() +endif() + +if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/unittest-cpp) + message(FATAL_ERROR "Could not find unittest-cpp enlistment. Please run 'git clone https://github.com/Microsoft/unittest-cpp.git unittest-cpp' in the tests directory") +endif() + +function(add_gsl_test name) + add_executable(${name} ${name}.cpp ../include/gsl.h ../include/gsl_assert.h ../include/gsl_util.h ../include/span.h ../include/string_span.h) + target_link_libraries(${name} UnitTest++) + install(TARGETS ${name} + RUNTIME DESTINATION bin + ) + add_test( + ${name} + ${name} + ) +endfunction() + +add_gsl_test(span_tests) +add_gsl_test(strided_span_tests) +add_gsl_test(string_span_tests) +add_gsl_test(at_tests) +add_gsl_test(bounds_tests) +add_gsl_test(notnull_tests) +add_gsl_test(assertion_tests) +add_gsl_test(utils_tests) +add_gsl_test(owner_tests) diff --git a/lib/gsl/tests/assertion_tests.cpp b/lib/gsl/tests/assertion_tests.cpp new file mode 100644 index 0000000..acd381a --- /dev/null +++ b/lib/gsl/tests/assertion_tests.cpp @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +using namespace gsl; + +SUITE(assertion_tests) +{ + int f(int i) + { + Expects(i > 0 && i < 10); + return i; + } + + TEST(expects) + { + CHECK(f(2) == 2); + CHECK_THROW(f(10), fail_fast); + } + + int g(int i) + { + i++; + Ensures(i > 0 && i < 10); + return i; + } + + TEST(ensures) + { + CHECK(g(2) == 3); + CHECK_THROW(g(9), fail_fast); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/lib/gsl/tests/at_tests.cpp b/lib/gsl/tests/at_tests.cpp new file mode 100644 index 0000000..d27dd9d --- /dev/null +++ b/lib/gsl/tests/at_tests.cpp @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +using namespace std; +using namespace gsl; + +SUITE(at_tests) +{ + TEST(static_array) + { + int a[] = { 1, 2, 3, 4 }; + + for (int i = 0; i < 4; ++i) + CHECK(at(a, i) == i+1); + + CHECK_THROW(at(a, 4), fail_fast); + } + + TEST(std_array) + { + std::array a = { 1, 2, 3, 4 }; + + for (int i = 0; i < 4; ++i) + CHECK(at(a, i) == i+1); + + CHECK_THROW(at(a, 4), fail_fast); + } + + TEST(StdVector) + { + std::vector a = { 1, 2, 3, 4 }; + + for (int i = 0; i < 4; ++i) + CHECK(at(a, i) == i+1); + + CHECK_THROW(at(a, 4), fail_fast); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/lib/gsl/tests/bounds_tests.cpp b/lib/gsl/tests/bounds_tests.cpp new file mode 100644 index 0000000..0665260 --- /dev/null +++ b/lib/gsl/tests/bounds_tests.cpp @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +using namespace std; +using namespace gsl;; + +namespace +{ + void use(std::ptrdiff_t&) {} +} + +SUITE(bounds_test) +{ + TEST(basic_bounds) + { + for (auto point : static_bounds { 2 }) + { + for (decltype(point)::size_type j = 0; + j < static_cast(decltype(point)::rank); + j++) + { + use(j); + use(point[j]); + } + } + } + + TEST(bounds_basic) + { + static_bounds<3, 4, 5> b; + auto a = b.slice(); + (void)a; + static_bounds<4, dynamic_range, 2> x{ 4 }; + x.slice().slice(); + } + + TEST (arrayview_iterator) + { + static_bounds<4, dynamic_range, 2> bounds{ 3 }; + + auto itr = bounds.begin(); + (void)itr; +#ifdef CONFIRM_COMPILATION_ERRORS + span av(nullptr, bounds); + + auto itr2 = av.cbegin(); + + for (auto& v : av) { + v = 4; + } + fill(av.begin(), av.end(), 0); +#endif + } + + TEST (bounds_convertible) + { + static_bounds<7, 4, 2> b1; + static_bounds<7, dynamic_range, 2> b2 = b1; + (void)b2; +#ifdef CONFIRM_COMPILATION_ERRORS + static_bounds<7, dynamic_range, 1> b4 = b2; +#endif + + static_bounds b3 = b1; + static_bounds<7, 4, 2> b4 = b3; + (void)b4; + + static_bounds b11; + + static_bounds b5; + static_bounds<34> b6; + + b5 = static_bounds<20>(); + CHECK_THROW(b6 = b5, fail_fast); + b5 = static_bounds<34>(); + b6 = b5; + + CHECK(b5 == b6); + CHECK(b5.size() == b6.size()); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/lib/gsl/tests/notnull_tests.cpp b/lib/gsl/tests/notnull_tests.cpp new file mode 100644 index 0000000..67b478a --- /dev/null +++ b/lib/gsl/tests/notnull_tests.cpp @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +using namespace gsl; + +struct MyBase {}; +struct MyDerived : public MyBase {}; +struct Unrelated {}; + +// stand-in for a user-defined ref-counted class +template +struct RefCounted +{ + RefCounted(T* p) : p_(p) {} + operator T*() { return p_; } + T* p_; +}; + +SUITE(NotNullTests) +{ + + bool helper(not_null p) + { + return *p == 12; + } + + TEST(TestNotNullConstructors) + { +#ifdef CONFIRM_COMPILATION_ERRORS + not_null p = nullptr; // yay...does not compile! + not_null*> p = 0; // yay...does not compile! + not_null p; // yay...does not compile! + std::unique_ptr up = std::make_unique(120); + not_null p = up; + + // Forbid non-nullptr assignable types + not_null> f(std::vector{1}); + not_null z(10); + not_null> y({1,2}); +#endif + int i = 12; + auto rp = RefCounted(&i); + not_null p(rp); + CHECK(p.get() == &i); + + not_null> x(std::make_shared(10)); // shared_ptr is nullptr assignable + } + + TEST(TestNotNullCasting) + { + MyBase base; + MyDerived derived; + Unrelated unrelated; + not_null u = &unrelated; + (void)u; + not_null p = &derived; + not_null q = &base; + q = p; // allowed with heterogeneous copy ctor + CHECK(q == p); + +#ifdef CONFIRM_COMPILATION_ERRORS + q = u; // no viable conversion possible between MyBase* and Unrelated* + p = q; // not possible to implicitly convert MyBase* to MyDerived* + + not_null r = p; + not_null s = reinterpret_cast(p); +#endif + not_null t = reinterpret_cast(p.get()); + CHECK((void*)p.get() == (void*)t.get()); + } + + TEST(TestNotNullAssignment) + { + int i = 12; + not_null p = &i; + CHECK(helper(p)); + + int* q = nullptr; + CHECK_THROW(p = q, fail_fast); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/lib/gsl/tests/owner_tests.cpp b/lib/gsl/tests/owner_tests.cpp new file mode 100644 index 0000000..47c223a --- /dev/null +++ b/lib/gsl/tests/owner_tests.cpp @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +using namespace gsl; + +SUITE(owner_tests) +{ + void f(int* i) + { + *i += 1; + } + + TEST(basic_test) + { + owner p = new int(120); + CHECK(*p == 120); + f(p); + CHECK(*p == 121); + delete p; + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/lib/gsl/tests/span_tests.cpp b/lib/gsl/tests/span_tests.cpp new file mode 100644 index 0000000..8b39639 --- /dev/null +++ b/lib/gsl/tests/span_tests.cpp @@ -0,0 +1,1679 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace gsl; + +namespace +{ +struct BaseClass +{ +}; +struct DerivedClass : BaseClass +{ +}; +} + +SUITE(span_tests) +{ + + TEST(default_constructor) + { + { + span s; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + span s; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s; + CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile +#endif + } + + { + span s{}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + } + + TEST(from_nullptr_constructor) + { + { + span s = nullptr; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs = nullptr; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + span s = nullptr; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs = nullptr; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s = nullptr; + CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile +#endif + } + + { + span s{nullptr}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + span s{nullptr}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + } + + TEST(from_nullptr_length_constructor) + { + { + span s{nullptr, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr, 0}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + span s{nullptr, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr, 0}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{nullptr, 0}; + CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile +#endif + } + + { + auto workaround_macro = []() { span s{nullptr, 1}; }; + CHECK_THROW(workaround_macro(), fail_fast); + + auto const_workaround_macro = []() { span cs{nullptr, 1}; }; + CHECK_THROW(const_workaround_macro(), fail_fast); + } + + { + auto workaround_macro = []() { span s{nullptr, 1}; }; + CHECK_THROW(workaround_macro(), fail_fast); + + auto const_workaround_macro = []() { span s{nullptr, 1}; }; + CHECK_THROW(const_workaround_macro(), fail_fast); + } + + { + span s{nullptr, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr, 0}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + } + + TEST(from_element_constructor) + { + int i = 5; + + { + span s = i; + CHECK(s.length() == 1 && s.data() == &i); + CHECK(s[0] == 5); + + span cs = i; + CHECK(cs.length() == 1 && cs.data() == &i); + CHECK(cs[0] == 5); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + const j = 1; + span s = j; +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s = i; + CHECK(s.length() == 0 && s.data() == &i); +#endif + } + + { + span s = i; + CHECK(s.length() == 1 && s.data() == &i); + CHECK(s[0] == 5); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s = i; + CHECK(s.length() == 2 && s.data() == &i); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_a_temp = []() -> int { return 4; }; + auto use_a_span = [](span s) { (void) s; }; + use_a_span(get_a_temp()); +#endif + } + } + + TEST(from_pointer_length_constructor) + { + int arr[4] = {1, 2, 3, 4}; + + { + span s{&arr[0], 2}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + span s{&arr[0], 2}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + int* p = nullptr; + span s{p, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + } + + { + int* p = nullptr; + auto workaround_macro = [=]() { span s{p, 2}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + } + + TEST(from_pointer_pointer_constructor) + { + int arr[4] = {1, 2, 3, 4}; + + { + span s{&arr[0], &arr[2]}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + span s{&arr[0], &arr[2]}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + span s{&arr[0], &arr[0]}; + CHECK(s.length() == 0 && s.data() == &arr[0]); + } + + { + span s{&arr[0], &arr[0]}; + CHECK(s.length() == 0 && s.data() == &arr[0]); + } + + { + auto workaround_macro = [&]() { span s{&arr[1], &arr[0]}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + { + int* p = nullptr; + auto workaround_macro = [&]() { span s{&arr[0], p}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + { + int* p = nullptr; + auto workaround_macro = [&]() { span s{p, p}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + { + int* p = nullptr; + auto workaround_macro = [&]() { span s{&arr[0], p}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + } + + TEST(from_array_constructor) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + span s{arr}; + CHECK(s.length() == 5 && s.data() == &arr[0]); + } + + { + span s{arr}; + CHECK(s.length() == 5 && s.data() == &arr[0]); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr}; +#endif + } + + { + span s{arr}; + CHECK(s.length() == 0 && s.data() == &arr[0]); + } + + int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; + + { + span s{arr2d}; + CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); + CHECK(s[0] == 1 && s[5] == 6); + } + + { + span s{arr2d}; + CHECK(s.length() == 0 && s.data() == &arr2d[0][0]); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr2d}; +#endif + } + + { + span s{arr2d}; + CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); + CHECK(s[0] == 1 && s[5] == 6); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr2d}; +#endif + } + + { + span s{arr2d[0]}; + CHECK(s.length() == 1 && s.data() == &arr2d[0]); + } + + { + span s{arr2d}; + CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); + auto workaround_macro = [&]() { return s[{1, 2}] == 6; }; + CHECK(workaround_macro()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr2d}; +#endif + } + + int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + + { + span s{arr3d}; + CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); + CHECK(s[0] == 1 && s[11] == 12); + } + + { + span s{arr3d}; + CHECK(s.length() == 0 && s.data() == &arr3d[0][0][0]); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr3d}; +#endif + } + + { + span s{arr3d}; + CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); + CHECK(s[0] == 1 && s[5] == 6); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr3d}; +#endif + } + + { + span s{arr3d[0]}; + CHECK(s.length() == 1 && s.data() == &arr3d[0]); + } + + { + span s{arr3d}; + CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); + auto workaround_macro = [&]() { return s[{2, 1, 0}] == 11; }; + CHECK(workaround_macro()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr3d}; +#endif + } + } + + TEST(from_dynamic_array_constructor) + { + double(*arr)[3][4] = new double[100][3][4]; + + { + span s(arr, 10); + CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); + CHECK_THROW(s[10][3][4], fail_fast); + } + + { + span s(arr, 10); + CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); + } + + { + span s(arr, 10); + CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); + } + + { + span s(arr, 0); + CHECK(s.length() == 0 && s.data() == &arr[0][0][0]); + } + + delete[] arr; + } + + TEST(from_std_array_constructor) + { + std::array arr = {1, 2, 3, 4}; + + { + span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + + span cs{arr}; + CHECK(cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data()); + } + + { + span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + + span cs{arr}; + CHECK(cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data()); + } + + { + span s{arr}; + CHECK(s.size() == 2 && s.data() == arr.data()); + + span cs{arr}; + CHECK(cs.size() == 2 && cs.data() == arr.data()); + } + + { + span s{arr}; + CHECK(s.size() == 0 && s.data() == arr.data()); + + span cs{arr}; + CHECK(cs.size() == 0 && cs.data() == arr.data()); + } + + // TODO This is currently an unsupported scenario. We will come back to it as we revise + // the multidimensional interface and what transformations between dimensionality look like + //{ + // span s{arr}; + // CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + //} + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr}; +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_an_array = []() { return std::array{1, 2, 3, 4}; }; + auto take_a_span = [](span s) { (void) s; }; + // try to take a temporary std::array + take_a_span(get_an_array()); +#endif + } + } + + TEST(from_const_std_array_constructor) + { + const std::array arr = {1, 2, 3, 4}; + + { + span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + } + + { + span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + } + + { + span s{arr}; + CHECK(s.size() == 2 && s.data() == arr.data()); + } + + { + span s{arr}; + CHECK(s.size() == 0 && s.data() == arr.data()); + } + + // TODO This is currently an unsupported scenario. We will come back to it as we revise + // the multidimensional interface and what transformations between dimensionality look like + //{ + // span s{arr}; + // CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + //} + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr}; +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_an_array = []() -> const std::array { return {1, 2, 3, 4}; }; + auto take_a_span = [](span s) { (void) s; }; + // try to take a temporary std::array + take_a_span(get_an_array()); +#endif + } + } + + TEST(from_container_constructor) + { + std::vector v = {1, 2, 3}; + const std::vector cv = v; + + { + span s{v}; + CHECK(s.size() == narrow_cast(v.size()) && s.data() == v.data()); + + span cs{v}; + CHECK(cs.size() == narrow_cast(v.size()) && cs.data() == v.data()); + } + + std::string str = "hello"; + const std::string cstr = "hello"; + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{str}; + CHECK(s.size() == narrow_cast(str.size()) && s.data() == str.data()); +#endif + span cs{str}; + CHECK(cs.size() == narrow_cast(str.size()) && cs.data() == str.data()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{cstr}; +#endif + span cs{cstr}; + CHECK(cs.size() == narrow_cast(cstr.size()) && + cs.data() == cstr.data()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_vector = []() -> std::vector { return {}; }; + auto use_span = [](span s) { (void) s; }; + use_span(get_temp_vector()); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_string = []() -> std::string { return {}; }; + auto use_span = [](span s) { (void) s; }; + use_span(get_temp_string()); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_vector = []() -> const std::vector { return {}; }; + auto use_span = [](span s) { (void) s; }; + use_span(get_temp_vector()); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_string = []() -> const std::string { return {}; }; + auto use_span = [](span s) { (void) s; }; + use_span(get_temp_string()); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::map m; + span s{m}; +#endif + } + } + + TEST(from_convertible_span_constructor) + { +#ifdef CONFIRM_COMPILATION_ERRORS + span av1(nullptr, b1); + + auto f = [&]() { span av1(nullptr); }; + CHECK_THROW(f(), fail_fast); +#endif + +#ifdef CONFIRM_COMPILATION_ERRORS + static_bounds b12(b11); + b12 = b11; + b11 = b12; + + span av1 = nullptr; + span av2(av1); + span av2(av1); +#endif + + span avd; +#ifdef CONFIRM_COMPILATION_ERRORS + span avb = avd; +#endif + span avcd = avd; + (void) avcd; + } + + TEST(copy_move_and_assignment) + { + span s1; + CHECK(s1.empty()); + + int arr[] = {3, 4, 5}; + + span s2 = arr; + CHECK(s2.length() == 3 && s2.data() == &arr[0]); + + s2 = s1; + CHECK(s2.empty()); + + auto get_temp_span = [&]() -> span { return {&arr[1], 2}; }; + auto use_span = [&](span s) { CHECK(s.length() == 2 && s.data() == &arr[1]); }; + use_span(get_temp_span()); + + s1 = get_temp_span(); + CHECK(s1.length() == 2 && s1.data() == &arr[1]); + } + + template + void fn(const Bounds&) + { + static_assert(Bounds::static_size == 60, "static bounds is wrong size"); + } + TEST(as_span_reshape) + { + int a[3][4][5]; + auto av = as_span(a); + fn(av.bounds()); + auto av2 = as_span(av, dim<60>()); + auto av3 = as_span(av2, dim<3>(), dim<4>(), dim<5>()); + auto av4 = as_span(av3, dim<4>(), dim<>(3), dim<5>()); + auto av5 = as_span(av4, dim<3>(), dim<4>(), dim<5>()); + auto av6 = as_span(av5, dim<12>(), dim<>(5)); + + fill(av6.begin(), av6.end(), 1); + + auto av7 = as_bytes(av6); + + auto av8 = as_span(av7); + + CHECK(av8.size() == av6.size()); + for (auto i = 0; i < av8.size(); i++) { + CHECK(av8[i] == 1); + } + } + + TEST(first) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + CHECK((av.first<2>().bounds() == static_bounds<2>())); + CHECK(av.first<2>().length() == 2); + CHECK(av.first(2).length() == 2); + } + + { + span av = arr; + CHECK((av.first<0>().bounds() == static_bounds<0>())); + CHECK(av.first<0>().length() == 0); + CHECK(av.first(0).length() == 0); + } + + { + span av = arr; + CHECK((av.first<5>().bounds() == static_bounds<5>())); + CHECK(av.first<5>().length() == 5); + CHECK(av.first(5).length() == 5); + } + + { + span av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(av.first<6>().bounds() == static_bounds<6>()); + CHECK(av.first<6>().length() == 6); + CHECK(av.first<-1>().length() == -1); +#endif + CHECK_THROW(av.first(6).length(), fail_fast); + } + + { + span av; + CHECK((av.first<0>().bounds() == static_bounds<0>())); + CHECK(av.first<0>().length() == 0); + CHECK(av.first(0).length() == 0); + } + } + + TEST(last) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + CHECK((av.last<2>().bounds() == static_bounds<2>())); + CHECK(av.last<2>().length() == 2); + CHECK(av.last(2).length() == 2); + } + + { + span av = arr; + CHECK((av.last<0>().bounds() == static_bounds<0>())); + CHECK(av.last<0>().length() == 0); + CHECK(av.last(0).length() == 0); + } + + { + span av = arr; + CHECK((av.last<5>().bounds() == static_bounds<5>())); + CHECK(av.last<5>().length() == 5); + CHECK(av.last(5).length() == 5); + } + + { + span av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK((av.last<6>().bounds() == static_bounds<6>())); + CHECK(av.last<6>().length() == 6); +#endif + CHECK_THROW(av.last(6).length(), fail_fast); + } + + { + span av; + CHECK((av.last<0>().bounds() == static_bounds<0>())); + CHECK(av.last<0>().length() == 0); + CHECK(av.last(0).length() == 0); + } + } + + TEST(subspan) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + CHECK((av.subspan<2, 2>().bounds() == static_bounds<2>())); + CHECK((av.subspan<2, 2>().length() == 2)); + CHECK(av.subspan(2, 2).length() == 2); + CHECK(av.subspan(2, 3).length() == 3); + } + + { + span av = arr; + CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>())); + CHECK((av.subspan<0, 0>().length() == 0)); + CHECK(av.subspan(0, 0).length() == 0); + } + + { + span av = arr; + CHECK((av.subspan<0, 5>().bounds() == static_bounds<5>())); + CHECK((av.subspan<0, 5>().length() == 5)); + CHECK(av.subspan(0, 5).length() == 5); + CHECK_THROW(av.subspan(0, 6).length(), fail_fast); + CHECK_THROW(av.subspan(1, 5).length(), fail_fast); + } + + { + span av = arr; + CHECK((av.subspan<5, 0>().bounds() == static_bounds<0>())); + CHECK((av.subspan<5, 0>().length() == 0)); + CHECK(av.subspan(5, 0).length() == 0); + CHECK_THROW(av.subspan(6, 0).length(), fail_fast); + } + + { + span av; + CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>())); + CHECK((av.subspan<0, 0>().length() == 0)); + CHECK(av.subspan(0, 0).length() == 0); + CHECK_THROW((av.subspan<1, 0>().length()), fail_fast); + } + + { + span av; + CHECK(av.subspan(0).length() == 0); + CHECK_THROW(av.subspan(1).length(), fail_fast); + } + + { + span av = arr; + CHECK(av.subspan(0).length() == 5); + CHECK(av.subspan(1).length() == 4); + CHECK(av.subspan(4).length() == 1); + CHECK(av.subspan(5).length() == 0); + CHECK_THROW(av.subspan(6).length(), fail_fast); + auto av2 = av.subspan(1); + for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2); + } + + { + span av = arr; + CHECK(av.subspan(0).length() == 5); + CHECK(av.subspan(1).length() == 4); + CHECK(av.subspan(4).length() == 1); + CHECK(av.subspan(5).length() == 0); + CHECK_THROW(av.subspan(6).length(), fail_fast); + auto av2 = av.subspan(1); + for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2); + } + } + + TEST(rank) + { + int arr[2] = {1, 2}; + + { + span s; + CHECK(s.rank() == 1); + } + + { + span s = arr; + CHECK(s.rank() == 1); + } + + int arr2d[1][1] = {}; + { + span s = arr2d; + CHECK(s.rank() == 2); + } + } + + TEST(extent) + { + { + span s; + CHECK(s.extent() == 0); + CHECK(s.extent(0) == 0); + CHECK_THROW(s.extent(1), fail_fast); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(s.extent<1>() == 0); +#endif + } + + { + span s; + CHECK(s.extent() == 0); + CHECK(s.extent(0) == 0); + CHECK_THROW(s.extent(1), fail_fast); + } + + { + int arr2d[1][2] = {}; + + span s = arr2d; + CHECK(s.extent() == 1); + CHECK(s.extent<0>() == 1); + CHECK(s.extent<1>() == 2); + CHECK(s.extent(0) == 1); + CHECK(s.extent(1) == 2); + CHECK_THROW(s.extent(3), fail_fast); + } + + { + int arr2d[1][2] = {}; + + span s = arr2d; + CHECK(s.extent() == 0); + CHECK(s.extent<0>() == 0); + CHECK(s.extent<1>() == 2); + CHECK(s.extent(0) == 0); + CHECK(s.extent(1) == 2); + CHECK_THROW(s.extent(3), fail_fast); + } + } + + TEST(operator_function_call) + { + int arr[4] = {1, 2, 3, 4}; + + { + span s = arr; + CHECK(s(0) == 1); + CHECK_THROW(s(5), fail_fast); + } + + int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; + + { + span s = arr2d; + CHECK(s(0, 0) == 1); + CHECK(s(1, 2) == 6); + } + } + + TEST(comparison_operators) + { + { + int arr[10][2]; + auto s1 = as_span(arr); + span s2 = s1; + + CHECK(s1 == s2); + + span s3 = as_span(s1, dim<>(20)); + CHECK(s3 == s2 && s3 == s1); + } + + { + auto s1 = nullptr; + auto s2 = nullptr; + CHECK(s1 == s2); + CHECK(!(s1 != s2)); + CHECK(!(s1 < s2)); + CHECK(s1 <= s2); + CHECK(!(s1 > s2)); + CHECK(s1 >= s2); + CHECK(s2 == s1); + CHECK(!(s2 != s1)); + CHECK(!(s2 < s1)); + CHECK(s2 <= s1); + CHECK(!(s2 > s1)); + CHECK(s2 >= s1); + } + + { + int arr[] = {2, 1}; // bigger + + span s1 = nullptr; + span s2 = arr; + + CHECK(s1 != s2); + CHECK(s2 != s1); + CHECK(!(s1 == s2)); + CHECK(!(s2 == s1)); + CHECK(s1 < s2); + CHECK(!(s2 < s1)); + CHECK(s1 <= s2); + CHECK(!(s2 <= s1)); + CHECK(s2 > s1); + CHECK(!(s1 > s2)); + CHECK(s2 >= s1); + CHECK(!(s1 >= s2)); + } + + { + int arr1[] = {1, 2}; + int arr2[] = {1, 2}; + span s1 = arr1; + span s2 = arr2; + + CHECK(s1 == s2); + CHECK(!(s1 != s2)); + CHECK(!(s1 < s2)); + CHECK(s1 <= s2); + CHECK(!(s1 > s2)); + CHECK(s1 >= s2); + CHECK(s2 == s1); + CHECK(!(s2 != s1)); + CHECK(!(s2 < s1)); + CHECK(s2 <= s1); + CHECK(!(s2 > s1)); + CHECK(s2 >= s1); + } + + { + int arr[] = {1, 2, 3}; + + span s1 = {&arr[0], 2}; // shorter + span s2 = arr; // longer + + CHECK(s1 != s2); + CHECK(s2 != s1); + CHECK(!(s1 == s2)); + CHECK(!(s2 == s1)); + CHECK(s1 < s2); + CHECK(!(s2 < s1)); + CHECK(s1 <= s2); + CHECK(!(s2 <= s1)); + CHECK(s2 > s1); + CHECK(!(s1 > s2)); + CHECK(s2 >= s1); + CHECK(!(s1 >= s2)); + } + + { + int arr1[] = {1, 2}; // smaller + int arr2[] = {2, 1}; // bigger + + span s1 = arr1; + span s2 = arr2; + + CHECK(s1 != s2); + CHECK(s2 != s1); + CHECK(!(s1 == s2)); + CHECK(!(s2 == s1)); + CHECK(s1 < s2); + CHECK(!(s2 < s1)); + CHECK(s1 <= s2); + CHECK(!(s2 <= s1)); + CHECK(s2 > s1); + CHECK(!(s1 > s2)); + CHECK(s2 >= s1); + CHECK(!(s1 >= s2)); + } + } + + TEST(basics) + { + auto ptr = as_span(new int[10], 10); + fill(ptr.begin(), ptr.end(), 99); + for (int num : ptr) { + CHECK(num == 99); + } + + delete[] ptr.data(); + } + + TEST(bounds_checks) + { + int arr[10][2]; + auto av = as_span(arr); + + fill(begin(av), end(av), 0); + + av[2][0] = 1; + av[1][1] = 3; + + // out of bounds + CHECK_THROW(av[1][3] = 3, fail_fast); + CHECK_THROW((av[{1, 3}] = 3), fail_fast); + + CHECK_THROW(av[10][2], fail_fast); + CHECK_THROW((av[{10, 2}]), fail_fast); + } + + void overloaded_func(span exp, int expected_value) + { + for (auto val : exp) { + CHECK(val == expected_value); + } + } + + void overloaded_func(span exp, char expected_value) + { + for (auto val : exp) { + CHECK(val == expected_value); + } + } + + void fixed_func(span exp, int expected_value) + { + for (auto val : exp) { + CHECK(val == expected_value); + } + } + + TEST(span_parameter_test) + { + auto data = new int[4][3][5]; + + auto av = as_span(data, 4); + + CHECK(av.size() == 60); + + fill(av.begin(), av.end(), 34); + + int count = 0; + for_each(av.rbegin(), av.rend(), [&](int val) { count += val; }); + CHECK(count == 34 * 60); + overloaded_func(av, 34); + + overloaded_func(as_span(av, dim<>(4), dim<>(3), dim<>(5)), 34); + + // fixed_func(av, 34); + delete[] data; + } + + TEST(md_access) + { + auto width = 5, height = 20; + + auto imgSize = width * height; + auto image_ptr = new int[imgSize][3]; + + // size check will be done + auto image_view = + as_span(as_span(image_ptr, imgSize), dim<>(height), dim<>(width), dim<3>()); + + iota(image_view.begin(), image_view.end(), 1); + + int expected = 0; + for (auto i = 0; i < height; i++) { + for (auto j = 0; j < width; j++) { + CHECK(expected + 1 == image_view[i][j][0]); + CHECK(expected + 2 == image_view[i][j][1]); + CHECK(expected + 3 == image_view[i][j][2]); + + auto val = image_view[{i, j, 0}]; + CHECK(expected + 1 == val); + val = image_view[{i, j, 1}]; + CHECK(expected + 2 == val); + val = image_view[{i, j, 2}]; + CHECK(expected + 3 == val); + + expected += 3; + } + } + } + + TEST(as_span) + { + { + int* arr = new int[150]; + + auto av = as_span(arr, dim<10>(), dim<>(3), dim<5>()); + + fill(av.begin(), av.end(), 24); + overloaded_func(av, 24); + + delete[] arr; + + array stdarr{0}; + auto av2 = as_span(stdarr); + overloaded_func(as_span(av2, dim<>(1), dim<3>(), dim<5>()), 0); + + string str = "ttttttttttttttt"; // size = 15 + auto t = str.data(); + (void) t; + auto av3 = as_span(str); + overloaded_func(as_span(av3, dim<>(1), dim<3>(), dim<5>()), 't'); + } + + { + string str; + span strspan = as_span(str); + (void) strspan; + const string cstr; + span cstrspan = as_span(cstr); + (void) cstrspan; + } + + { + int a[3][4][5]; + auto av = as_span(a); + const int(*b)[4][5]; + b = a; + auto bv = as_span(b, 3); + + CHECK(av == bv); + + const std::array arr = {0.0, 0.0, 0.0}; + auto cv = as_span(arr); + (void) cv; + + vector vec(3); + auto dv = as_span(vec); + (void) dv; + +#ifdef CONFIRM_COMPILATION_ERRORS + auto dv2 = as_span(std::move(vec)); +#endif + } + } + + TEST(empty_spans) + { + { + span empty_av(nullptr); + + CHECK(empty_av.bounds().index_bounds() == index<1>{0}); + CHECK_THROW(empty_av[0], fail_fast); + CHECK_THROW(empty_av.begin()[0], fail_fast); + CHECK_THROW(empty_av.cbegin()[0], fail_fast); + for (auto& v : empty_av) { + (void) v; + CHECK(false); + } + } + + { + span empty_av = {}; + CHECK(empty_av.bounds().index_bounds() == index<1>{0}); + CHECK_THROW(empty_av[0], fail_fast); + CHECK_THROW(empty_av.begin()[0], fail_fast); + CHECK_THROW(empty_av.cbegin()[0], fail_fast); + for (auto& v : empty_av) { + (void) v; + CHECK(false); + } + } + } + + TEST(index_constructor) + { + auto arr = new int[8]; + for (int i = 0; i < 4; ++i) { + arr[2 * i] = 4 + i; + arr[2 * i + 1] = i; + } + + span av(arr, 8); + + ptrdiff_t a[1] = {0}; + index<1> i = a; + + CHECK(av[i] == 4); + + auto av2 = as_span(av, dim<4>(), dim<>(2)); + ptrdiff_t a2[2] = {0, 1}; + index<2> i2 = a2; + + CHECK(av2[i2] == 0); + CHECK(av2[0][i] == 4); + + delete[] arr; + } + + TEST(index_constructors) + { + { + // components of the same type + index<3> i1(0, 1, 2); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + size_t c1 = 1; + index<3> i2(c0, c1, 2); + CHECK(i2[0] == 0); + + // from array + index<3> i3 = {0, 1, 2}; + CHECK(i3[0] == 0); + + // from other index of the same size type + index<3> i4 = i3; + CHECK(i4[0] == 0); + + // default + index<3> i7; + CHECK(i7[0] == 0); + + // default + index<3> i9 = {}; + CHECK(i9[0] == 0); + } + + { + // components of the same type + index<1> i1(0); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + index<1> i2(c0); + CHECK(i2[0] == 0); + + // from array + index<1> i3 = {0}; + CHECK(i3[0] == 0); + + // from int + index<1> i4 = 0; + CHECK(i4[0] == 0); + + // from other index of the same size type + index<1> i5 = i3; + CHECK(i5[0] == 0); + + // default + index<1> i8; + CHECK(i8[0] == 0); + + // default + index<1> i9 = {}; + CHECK(i9[0] == 0); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + index<3> i1(0, 1); + index<3> i2(0, 1, 2, 3); + index<3> i3 = {0}; + index<3> i4 = {0, 1, 2, 3}; + index<1> i5 = {0, 1}; + } +#endif + } + + TEST(index_operations) + { + ptrdiff_t a[3] = {0, 1, 2}; + ptrdiff_t b[3] = {3, 4, 5}; + index<3> i = a; + index<3> j = b; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + + { + index<3> k = i + j; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 3); + CHECK(k[1] == 5); + CHECK(k[2] == 7); + } + + { + index<3> k = i * 3; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 0); + CHECK(k[1] == 3); + CHECK(k[2] == 6); + } + + { + index<3> k = 3 * i; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 0); + CHECK(k[1] == 3); + CHECK(k[2] == 6); + } + + { + index<2> k = details::shift_left(i); + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 1); + CHECK(k[1] == 2); + } + } + + void iterate_second_column(span av) + { + auto length = av.size() / 2; + + // view to the second column + auto section = av.section({0, 1}, {length, 1}); + + CHECK(section.size() == length); + for (auto i = 0; i < section.size(); ++i) { + CHECK(section[i][0] == av[i][1]); + } + + for (auto i = 0; i < section.size(); ++i) { + auto idx = index<2>{i, 0}; // avoid braces inside the CHECK macro + CHECK(section[idx] == av[i][1]); + } + + CHECK(section.bounds().index_bounds()[0] == length); + CHECK(section.bounds().index_bounds()[1] == 1); + for (auto i = 0; i < section.bounds().index_bounds()[0]; ++i) { + for (auto j = 0; j < section.bounds().index_bounds()[1]; ++j) { + auto idx = index<2>{i, j}; // avoid braces inside the CHECK macro + CHECK(section[idx] == av[i][1]); + } + } + + size_t check_sum = 0; + for (auto i = 0; i < length; ++i) { + check_sum += av[i][1]; + } + + { + auto idx = 0; + size_t sum = 0; + for (auto num : section) { + CHECK(num == av[idx][1]); + sum += num; + idx++; + } + + CHECK(sum == check_sum); + } + { + size_t idx = length - 1; + size_t sum = 0; + for (auto iter = section.rbegin(); iter != section.rend(); ++iter) { + CHECK(*iter == av[idx][1]); + sum += *iter; + idx--; + } + + CHECK(sum == check_sum); + } + } + + TEST(span_section_iteration) + { + int arr[4][2] = {{4, 0}, {5, 1}, {6, 2}, {7, 3}}; + + // static bounds + { + span av = arr; + iterate_second_column(av); + } + // first bound is dynamic + { + span av = arr; + iterate_second_column(av); + } + // second bound is dynamic + { + span av = arr; + iterate_second_column(av); + } + // both bounds are dynamic + { + span av = arr; + iterate_second_column(av); + } + } + + TEST(dynamic_span_section_iteration) + { + auto height = 4, width = 2; + auto size = height * width; + + auto arr = new int[size]; + for (auto i = 0; i < size; ++i) { + arr[i] = i; + } + + auto av = as_span(arr, size); + + // first bound is dynamic + { + span av2 = as_span(av, dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + // second bound is dynamic + { + span av2 = as_span(av, dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + // both bounds are dynamic + { + span av2 = as_span(av, dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + + delete[] arr; + } + + TEST(span_structure_size) + { + double(*arr)[3][4] = new double[100][3][4]; + span av1(arr, 10); + + struct EffectiveStructure + { + double* v1; + ptrdiff_t v2; + }; + CHECK(sizeof(av1) == sizeof(EffectiveStructure)); + + CHECK_THROW(av1[10][3][4], fail_fast); + + span av2 = as_span(av1, dim<>(5), dim<6>(), dim<4>()); + (void) av2; + } + + TEST(fixed_size_conversions) + { + int arr[] = {1, 2, 3, 4}; + + // converting to an span from an equal size array is ok + span av4 = arr; + CHECK(av4.length() == 4); + + // converting to dynamic_range a_v is always ok + { + span av = av4; + (void) av; + } + { + span av = arr; + (void) av; + } + +// initialization or assignment to static span that REDUCES size is NOT ok +#ifdef CONFIRM_COMPILATION_ERRORS + { + span av2 = arr; + } + { + span av2 = av4; + } +#endif + + { + span av = arr; + span av2 = av; + (void) av2; + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + span av = arr; + span av2 = av.as_span(dim<2>(), dim<2>()); + } +#endif + + { + span av = arr; + span av2 = as_span(av, dim<>(2), dim<>(2)); + auto workaround_macro = [&]() { return av2[{1, 0}] == 2; }; + CHECK(workaround_macro()); + } + + // but doing so explicitly is ok + + // you can convert statically + { + span av2 = {arr, 2}; + (void) av2; + } + { + span av2 = av4.first<1>(); + (void) av2; + } + + // ...or dynamically + { + // NB: implicit conversion to span from span + span av2 = av4.first(1); + (void) av2; + } + + // initialization or assignment to static span that requires size INCREASE is not ok. + int arr2[2] = {1, 2}; + +#ifdef CONFIRM_COMPILATION_ERRORS + { + span av4 = arr2; + } + { + span av2 = arr2; + span av4 = av2; + } +#endif + { + auto f = [&]() { + span av9 = {arr2, 2}; + (void) av9; + }; + CHECK_THROW(f(), fail_fast); + } + + // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one + span av = arr2; + auto f = [&]() { + span av2 = av; + (void) av2; + }; + CHECK_THROW(f(), fail_fast); + } + + TEST(as_writeable_bytes) + { + int a[] = {1, 2, 3, 4}; + + { +#ifdef CONFIRM_COMPILATION_ERRORS + // you should not be able to get writeable bytes for const objects + span av = a; + auto wav = av.as_writeable_bytes(); +#endif + } + + { + span av; + auto wav = as_writeable_bytes(av); + CHECK(wav.length() == av.length()); + CHECK(wav.length() == 0); + CHECK(wav.size_bytes() == 0); + } + + { + span av = a; + auto wav = as_writeable_bytes(av); + CHECK(wav.data() == (byte*) &a[0]); + CHECK(wav.length() == sizeof(a)); + } + } + + TEST(iterator) + { + int a[] = {1, 2, 3, 4}; + + { + span av = a; + auto wav = as_writeable_bytes(av); + for (auto& b : wav) { + b = byte(0); + } + for (size_t i = 0; i < 4; ++i) { + CHECK(a[i] == 0); + } + } + + { + span av = a; + for (auto& n : av) { + n = 1; + } + for (size_t i = 0; i < 4; ++i) { + CHECK(a[i] == 1); + } + } + } +} + +int main(int, const char* []) { return UnitTest::RunAllTests(); } diff --git a/lib/gsl/tests/strided_span_tests.cpp b/lib/gsl/tests/strided_span_tests.cpp new file mode 100644 index 0000000..0fbf1d7 --- /dev/null +++ b/lib/gsl/tests/strided_span_tests.cpp @@ -0,0 +1,748 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace gsl; + +namespace +{ + struct BaseClass {}; + struct DerivedClass : BaseClass {}; +} + +SUITE(strided_span_tests) +{ + TEST (span_section_test) + { + int a[30][4][5]; + + auto av = as_span(a); + auto sub = av.section({15, 0, 0}, gsl::index<3>{2, 2, 2}); + auto subsub = sub.section({1, 0, 0}, gsl::index<3>{1, 1, 1}); + (void)subsub; + } + + TEST(span_section) + { + std::vector data(5 * 10); + std::iota(begin(data), end(data), 0); + const span av = as_span(span{data}, dim<5>(), dim<10>()); + + strided_span av_section_1 = av.section({ 1, 2 }, { 3, 4 }); + CHECK((av_section_1[{0, 0}] == 12)); + CHECK((av_section_1[{0, 1}] == 13)); + CHECK((av_section_1[{1, 0}] == 22)); + CHECK((av_section_1[{2, 3}] == 35)); + + strided_span av_section_2 = av_section_1.section({ 1, 2 }, { 2,2 }); + CHECK((av_section_2[{0, 0}] == 24)); + CHECK((av_section_2[{0, 1}] == 25)); + CHECK((av_section_2[{1, 0}] == 34)); + } + + TEST(strided_span_constructors) + { + // Check stride constructor + { + int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + const int carr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + strided_span sav1{ arr, {{9}, {1}} }; // T -> T + CHECK(sav1.bounds().index_bounds() == index<1>{ 9 }); + CHECK(sav1.bounds().stride() == 1); + CHECK(sav1[0] == 1 && sav1[8] == 9); + + + strided_span sav2{ carr, {{ 4 }, { 2 }} }; // const T -> const T + CHECK(sav2.bounds().index_bounds() == index<1>{ 4 }); + CHECK(sav2.bounds().strides() == index<1>{2}); + CHECK(sav2[0] == 1 && sav2[3] == 7); + + strided_span sav3{ arr, {{ 2, 2 },{ 6, 2 }} }; // T -> const T + CHECK((sav3.bounds().index_bounds() == index<2>{ 2, 2 })); + CHECK((sav3.bounds().strides() == index<2>{ 6, 2 })); + CHECK((sav3[{0, 0}] == 1 && sav3[{0, 1}] == 3 && sav3[{1, 0}] == 7)); + } + + // Check span constructor + { + int arr[] = { 1, 2 }; + + // From non-cv-qualified source + { + const span src = arr; + + strided_span sav{ src, {2, 1} }; + CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav.bounds().strides() == index<1>{ 1 }); + CHECK(sav[1] == 2); + +#if _MSC_VER > 1800 + //strided_span sav_c{ {src}, {2, 1} }; + strided_span sav_c{ span{src}, strided_bounds<1>{2, 1} }; +#else + strided_span sav_c{ span{src}, strided_bounds<1>{2, 1} }; +#endif + CHECK(sav_c.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav_c.bounds().strides() == index<1>{ 1 }); + CHECK(sav_c[1] == 2); + +#if _MSC_VER > 1800 + strided_span sav_v{ src, {2, 1} }; +#else + strided_span sav_v{ span{src}, strided_bounds<1>{2, 1} }; +#endif + CHECK(sav_v.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav_v.bounds().strides() == index<1>{ 1 }); + CHECK(sav_v[1] == 2); + +#if _MSC_VER > 1800 + strided_span sav_cv{ src, {2, 1} }; +#else + strided_span sav_cv{ span{src}, strided_bounds<1>{2, 1} }; +#endif + CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav_cv.bounds().strides() == index<1>{ 1 }); + CHECK(sav_cv[1] == 2); + } + + // From const-qualified source + { + const span src{ arr }; + + strided_span sav_c{ src, {2, 1} }; + CHECK(sav_c.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav_c.bounds().strides() == index<1>{ 1 }); + CHECK(sav_c[1] == 2); + +#if _MSC_VER > 1800 + strided_span sav_cv{ src, {2, 1} }; +#else + strided_span sav_cv{ span{src}, strided_bounds<1>{2, 1} }; +#endif + + CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav_cv.bounds().strides() == index<1>{ 1 }); + CHECK(sav_cv[1] == 2); + } + + // From volatile-qualified source + { + const span src{ arr }; + + strided_span sav_v{ src, {2, 1} }; + CHECK(sav_v.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav_v.bounds().strides() == index<1>{ 1 }); + CHECK(sav_v[1] == 2); + +#if _MSC_VER > 1800 + strided_span sav_cv{ src, {2, 1} }; +#else + strided_span sav_cv{ span{src}, strided_bounds<1>{2, 1} }; +#endif + CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav_cv.bounds().strides() == index<1>{ 1 }); + CHECK(sav_cv[1] == 2); + } + + // From cv-qualified source + { + const span src{ arr }; + + strided_span sav_cv{ src, {2, 1} }; + CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav_cv.bounds().strides() == index<1>{ 1 }); + CHECK(sav_cv[1] == 2); + } + } + + // Check const-casting constructor + { + int arr[2] = { 4, 5 }; + + const span av(arr, 2); + span av2{ av }; + CHECK(av2[1] == 5); + + static_assert(std::is_convertible, span>::value, "ctor is not implicit!"); + + const strided_span src{ arr, {2, 1} }; + strided_span sav{ src }; + CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav.bounds().stride() == 1); + CHECK(sav[1] == 5); + + static_assert(std::is_convertible, strided_span>::value, "ctor is not implicit!"); + } + + // Check copy constructor + { + int arr1[2] = { 3, 4 }; + const strided_span src1{ arr1, {2, 1} }; + strided_span sav1{ src1 }; + + CHECK(sav1.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav1.bounds().stride() == 1); + CHECK(sav1[0] == 3); + + int arr2[6] = { 1, 2, 3, 4, 5, 6 }; + const strided_span src2{ arr2, {{ 3, 2 }, { 2, 1 }} }; + strided_span sav2{ src2 }; + CHECK((sav2.bounds().index_bounds() == index<2>{ 3, 2 })); + CHECK((sav2.bounds().strides() == index<2>{ 2, 1 })); + CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5)); + } + + // Check const-casting assignment operator + { + int arr1[2] = { 1, 2 }; + int arr2[6] = { 3, 4, 5, 6, 7, 8 }; + + const strided_span src{ arr1, {{2}, {1}} }; + strided_span sav{ arr2, {{3}, {2}} }; + strided_span& sav_ref = (sav = src); + CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav.bounds().strides() == index<1>{ 1 }); + CHECK(sav[0] == 1); + CHECK(&sav_ref == &sav); + } + + // Check copy assignment operator + { + int arr1[2] = { 3, 4 }; + int arr1b[1] = { 0 }; + const strided_span src1{ arr1, {2, 1} }; + strided_span sav1{ arr1b, {1, 1} }; + strided_span& sav1_ref = (sav1 = src1); + CHECK(sav1.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav1.bounds().strides() == index<1>{ 1 }); + CHECK(sav1[0] == 3); + CHECK(&sav1_ref == &sav1); + + const int arr2[6] = { 1, 2, 3, 4, 5, 6 }; + const int arr2b[1] = { 0 }; + const strided_span src2{ arr2, {{ 3, 2 },{ 2, 1 }} }; + strided_span sav2{ arr2b, {{ 1, 1 },{ 1, 1 }} }; + strided_span& sav2_ref = (sav2 = src2); + CHECK((sav2.bounds().index_bounds() == index<2>{ 3, 2 })); + CHECK((sav2.bounds().strides() == index<2>{ 2, 1 })); + CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5)); + CHECK(&sav2_ref == &sav2); + } + } + + TEST(strided_span_slice) + { + std::vector data(5 * 10); + std::iota(begin(data), end(data), 0); + const span src = as_span(span{data}, dim<5>(), dim<10>()); + + const strided_span sav{ src, {{5, 10}, {10, 1}} }; +#ifdef CONFIRM_COMPILATION_ERRORS + const strided_span csav{ {src},{ { 5, 10 },{ 10, 1 } } }; +#endif + const strided_span csav{ span{ src }, { { 5, 10 },{ 10, 1 } } }; + + strided_span sav_sl = sav[2]; + CHECK(sav_sl[0] == 20); + CHECK(sav_sl[9] == 29); + + strided_span csav_sl = sav[3]; + CHECK(csav_sl[0] == 30); + CHECK(csav_sl[9] == 39); + + CHECK(sav[4][0] == 40); + CHECK(sav[4][9] == 49); + } + + TEST(strided_span_column_major) + { + // strided_span may be used to accomodate more peculiar + // use cases, such as column-major multidimensional array + // (aka. "FORTRAN" layout). + + int cm_array[3 * 5] = { + 1, 4, 7, 10, 13, + 2, 5, 8, 11, 14, + 3, 6, 9, 12, 15 + }; + strided_span cm_sav{ cm_array, {{ 5, 3 },{ 1, 5 }} }; + + // Accessing elements + CHECK((cm_sav[{0, 0}] == 1)); + CHECK((cm_sav[{0, 1}] == 2)); + CHECK((cm_sav[{1, 0}] == 4)); + CHECK((cm_sav[{4, 2}] == 15)); + + // Slice + strided_span cm_sl = cm_sav[3]; + + CHECK(cm_sl[0] == 10); + CHECK(cm_sl[1] == 11); + CHECK(cm_sl[2] == 12); + + // Section + strided_span cm_sec = cm_sav.section( { 2, 1 }, { 3, 2 }); + + CHECK((cm_sec.bounds().index_bounds() == index<2>{3, 2})); + CHECK((cm_sec[{0, 0}] == 8)); + CHECK((cm_sec[{0, 1}] == 9)); + CHECK((cm_sec[{1, 0}] == 11)); + CHECK((cm_sec[{2, 1}] == 15)); + } + + TEST(strided_span_bounds) + { + int arr[] = { 0, 1, 2, 3 }; + span av(arr); + + { + // incorrect sections + + CHECK_THROW(av.section(0, 0)[0], fail_fast); + CHECK_THROW(av.section(1, 0)[0], fail_fast); + CHECK_THROW(av.section(1, 1)[1], fail_fast); + + CHECK_THROW(av.section(2, 5), fail_fast); + CHECK_THROW(av.section(5, 2), fail_fast); + CHECK_THROW(av.section(5, 0), fail_fast); + CHECK_THROW(av.section(0, 5), fail_fast); + CHECK_THROW(av.section(5, 5), fail_fast); + } + + { + // zero stride + strided_span sav{ av,{ { 4 },{} } }; + CHECK(sav[0] == 0); + CHECK(sav[3] == 0); + CHECK_THROW(sav[4], fail_fast); + } + + { + // zero extent + strided_span sav{ av,{ {},{ 1 } } }; + CHECK_THROW(sav[0], fail_fast); + } + + { + // zero extent and stride + strided_span sav{ av,{ {},{} } }; + CHECK_THROW(sav[0], fail_fast); + } + + { + // strided array ctor with matching strided bounds + strided_span sav{ arr,{ 4, 1 } }; + CHECK(sav.bounds().index_bounds() == index<1>{ 4 }); + CHECK(sav[3] == 3); + CHECK_THROW(sav[4], fail_fast); + } + + { + // strided array ctor with smaller strided bounds + strided_span sav{ arr,{ 2, 1 } }; + CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav[1] == 1); + CHECK_THROW(sav[2], fail_fast); + } + + { + // strided array ctor with fitting irregular bounds + strided_span sav{ arr,{ 2, 3 } }; + CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav[0] == 0); + CHECK(sav[1] == 3); + CHECK_THROW(sav[2], fail_fast); + } + + { + // bounds cross data boundaries - from static arrays + CHECK_THROW((strided_span { arr, { 3, 2 } }), fail_fast); + CHECK_THROW((strided_span { arr, { 3, 3 } }), fail_fast); + CHECK_THROW((strided_span { arr, { 4, 5 } }), fail_fast); + CHECK_THROW((strided_span { arr, { 5, 1 } }), fail_fast); + CHECK_THROW((strided_span { arr, { 5, 5 } }), fail_fast); + } + + { + // bounds cross data boundaries - from array view + CHECK_THROW((strided_span { av, { 3, 2 } }), fail_fast); + CHECK_THROW((strided_span { av, { 3, 3 } }), fail_fast); + CHECK_THROW((strided_span { av, { 4, 5 } }), fail_fast); + CHECK_THROW((strided_span { av, { 5, 1 } }), fail_fast); + CHECK_THROW((strided_span { av, { 5, 5 } }), fail_fast); + } + + { + // bounds cross data boundaries - from dynamic arrays + CHECK_THROW((strided_span { av.data(), 4, { 3, 2 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 4, { 3, 3 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 4, { 4, 5 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 4, { 5, 1 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 4, { 5, 5 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 2, { 2, 2 } }), fail_fast); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + strided_span sav0{ av.data(), { 3, 2 } }; + strided_span sav1{ arr, { 1 } }; + strided_span sav2{ arr, { 1,1,1 } }; + strided_span sav3{ av, { 1 } }; + strided_span sav4{ av, { 1,1,1 } }; + strided_span sav5{ av.as_span(dim<2>(), dim<2>()), { 1 } }; + strided_span sav6{ av.as_span(dim<2>(), dim<2>()), { 1,1,1 } }; + strided_span sav7{ av.as_span(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } }; + + index<1> index{ 0, 1 }; + strided_span sav8{ arr,{ 1,{ 1,1 } } }; + strided_span sav9{ arr,{ { 1,1 },{ 1,1 } } }; + strided_span sav10{ av,{ 1,{ 1,1 } } }; + strided_span sav11{ av,{ { 1,1 },{ 1,1 } } }; + strided_span sav12{ av.as_span(dim<2>(), dim<2>()),{ { 1 },{ 1 } } }; + strided_span sav13{ av.as_span(dim<2>(), dim<2>()),{ { 1 },{ 1,1,1 } } }; + strided_span sav14{ av.as_span(dim<2>(), dim<2>()),{ { 1,1,1 },{ 1 } } }; + } +#endif + } + + TEST(strided_span_type_conversion) + { + int arr[] = { 0, 1, 2, 3 }; + span av(arr); + + { + strided_span sav{ av.data(), av.size(), { av.size() / 2, 2 } }; +#ifdef CONFIRM_COMPILATION_ERRORS + strided_span lsav1 = sav.as_strided_span(); +#endif + } + { + strided_span sav{ av, { av.size() / 2, 2 } }; +#ifdef CONFIRM_COMPILATION_ERRORS + strided_span lsav1 = sav.as_strided_span(); +#endif + } + + span bytes = as_bytes(av); + + // retype strided array with regular strides - from raw data + { + strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } }; + strided_span sav2{ bytes.data(), bytes.size(), bounds }; + strided_span sav3 = sav2.as_strided_span(); + CHECK(sav3[0][0] == 0); + CHECK(sav3[1][0] == 2); + CHECK_THROW(sav3[1][1], fail_fast); + CHECK_THROW(sav3[0][1], fail_fast); + } + + // retype strided array with regular strides - from span + { + strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } }; + span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + strided_span sav3 = sav2.as_strided_span(); + CHECK(sav3[0][0] == 0); + CHECK(sav3[1][0] == 2); + CHECK_THROW(sav3[1][1], fail_fast); + CHECK_THROW(sav3[0][1], fail_fast); + } + + // retype strided array with not enough elements - last dimension of the array is too small + { + strided_bounds<2> bounds{ { 4,2 },{ 4, 1 } }; + span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_span(), fail_fast); + } + + // retype strided array with not enough elements - strides are too small + { + strided_bounds<2> bounds{ { 4,2 },{ 2, 1 } }; + span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_span(), fail_fast); + } + + // retype strided array with not enough elements - last dimension does not divide by the new typesize + { + strided_bounds<2> bounds{ { 2,6 },{ 4, 1 } }; + span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_span(), fail_fast); + } + + // retype strided array with not enough elements - strides does not divide by the new typesize + { + strided_bounds<2> bounds{ { 2, 1 },{ 6, 1 } }; + span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_span(), fail_fast); + } + + // retype strided array with irregular strides - from raw data + { + strided_bounds<1> bounds{ bytes.size() / 2, 2 }; + strided_span sav2{ bytes.data(), bytes.size(), bounds }; + CHECK_THROW(sav2.as_strided_span(), fail_fast); + } + + // retype strided array with irregular strides - from span + { + strided_bounds<1> bounds{ bytes.size() / 2, 2 }; + strided_span sav2{ bytes, bounds }; + CHECK_THROW(sav2.as_strided_span(), fail_fast); + } + } + + TEST(empty_strided_spans) + { + { + span empty_av(nullptr); + strided_span empty_sav{ empty_av, { 0, 1 } }; + + CHECK(empty_sav.bounds().index_bounds() == index<1>{ 0 }); + CHECK_THROW(empty_sav[0], fail_fast); + CHECK_THROW(empty_sav.begin()[0], fail_fast); + CHECK_THROW(empty_sav.cbegin()[0], fail_fast); + + for (auto& v : empty_sav) + { + (void)v; + CHECK(false); + } + } + + { + strided_span empty_sav{ nullptr, 0, { 0, 1 } }; + + CHECK(empty_sav.bounds().index_bounds() == index<1>{ 0 }); + CHECK_THROW(empty_sav[0], fail_fast); + CHECK_THROW(empty_sav.begin()[0], fail_fast); + CHECK_THROW(empty_sav.cbegin()[0], fail_fast); + + for (auto& v : empty_sav) + { + (void)v; + CHECK(false); + } + } + } + + void iterate_every_other_element(span av) + { + // pick every other element + + auto length = av.size() / 2; +#if _MSC_VER > 1800 + auto bounds = strided_bounds<1>({length}, {2}); +#else + auto bounds = strided_bounds<1>(index<1>{ length }, index<1>{ 2 }); +#endif + strided_span strided(&av.data()[1], av.size() - 1, bounds); + + CHECK(strided.size() == length); + CHECK(strided.bounds().index_bounds()[0] == length); + for (auto i = 0; i < strided.size(); ++i) + { + CHECK(strided[i] == av[2 * i + 1]); + } + + int idx = 0; + for (auto num : strided) + { + CHECK(num == av[2 * idx + 1]); + idx++; + } + } + + TEST(strided_span_section_iteration) + { + int arr[8] = {4,0,5,1,6,2,7,3}; + + // static bounds + { + span av(arr, 8); + iterate_every_other_element(av); + } + + // dynamic bounds + { + span av(arr, 8); + iterate_every_other_element(av); + } + } + + TEST(dynamic_strided_span_section_iteration) + { + auto arr = new int[8]; + for (int i = 0; i < 4; ++i) + { + arr[2 * i] = 4 + i; + arr[2 * i + 1] = i; + } + + auto av = as_span(arr, 8); + iterate_every_other_element(av); + + delete[] arr; + } + + void iterate_second_slice(span av) + { + int expected[6] = {2,3,10,11,18,19}; + auto section = av.section({0,1,0}, {3,1,2}); + + for (auto i = 0; i < section.extent<0>(); ++i) + { + for (auto j = 0; j < section.extent<1>(); ++j) + for (auto k = 0; k < section.extent<2>(); ++k) + { + auto idx = index<3>{i,j,k}; // avoid braces in the CHECK macro + CHECK(section[idx] == expected[2 * i + 2 * j + k]); + } + } + + for (auto i = 0; i < section.extent<0>(); ++i) + { + for (auto j = 0; j < section.extent<1>(); ++j) + for (auto k = 0; k < section.extent<2>(); ++k) + CHECK(section[i][j][k] == expected[2 * i + 2 * j + k]); + } + + int i = 0; + for (auto num : section) + { + CHECK(num == expected[i]); + i++; + } + } + + TEST(strided_span_section_iteration_3d) + { + int arr[3][4][2]; + for (auto i = 0; i < 3; ++i) + { + for (auto j = 0; j < 4; ++j) + for (auto k = 0; k < 2; ++k) + arr[i][j][k] = 8 * i + 2 * j + k; + } + + { + span av = arr; + iterate_second_slice(av); + } + } + + TEST(dynamic_strided_span_section_iteration_3d) + { + auto height = 12, width = 2; + auto size = height * width; + + auto arr = new int[size]; + for (auto i = 0; i < size; ++i) + { + arr[i] = i; + } + + { + auto av = as_span(as_span(arr, 24), dim<3>(), dim<4>(), dim<2>()); + iterate_second_slice(av); + } + + { + auto av = as_span(as_span(arr, 24), dim<>(3), dim<4>(), dim<2>()); + iterate_second_slice(av); + } + + { + auto av = as_span(as_span(arr, 24), dim<3>(), dim<>(4), dim<2>()); + iterate_second_slice(av); + } + + { + auto av = as_span(as_span(arr, 24), dim<3>(), dim<4>(), dim<>(2)); + iterate_second_slice(av); + } + delete[] arr; + } + + TEST(strided_span_conversion) + { + // get an span of 'c' values from the list of X's + + struct X { int a; int b; int c; }; + + X arr[4] = {{0,1,2},{3,4,5},{6,7,8},{9,10,11}}; + + int s = sizeof(int) / sizeof(byte); + auto d2 = 3 * s; + auto d1 = sizeof(int) * 12 / d2; + + // convert to 4x12 array of bytes + auto av = as_span(as_bytes(as_span(arr, 4)), dim<>(d1), dim<>(d2)); + + CHECK(av.bounds().index_bounds()[0] == 4); + CHECK(av.bounds().index_bounds()[1] == 12); + + // get the last 4 columns + auto section = av.section({0, 2 * s}, {4, s}); // { { arr[0].c[0], arr[0].c[1], arr[0].c[2], arr[0].c[3] } , { arr[1].c[0], ... } , ... } + + // convert to array 4x1 array of integers + auto cs = section.as_strided_span(); // { { arr[0].c }, {arr[1].c } , ... } + + CHECK(cs.bounds().index_bounds()[0] == 4); + CHECK(cs.bounds().index_bounds()[1] == 1); + + // transpose to 1x4 array + strided_bounds<2> reverse_bounds{ + {cs.bounds().index_bounds()[1] , cs.bounds().index_bounds()[0]}, + {cs.bounds().strides()[1], cs.bounds().strides()[0]} + }; + + strided_span transposed{cs.data(), cs.bounds().total_size(), reverse_bounds}; + + // slice to get a one-dimensional array of c's + strided_span result = transposed[0]; + + CHECK(result.bounds().index_bounds()[0] == 4); + CHECK_THROW(result.bounds().index_bounds()[1], fail_fast); + + int i = 0; + for (auto& num : result) + { + CHECK(num == arr[i].c); + i++; + } + + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/lib/gsl/tests/string_span_tests.cpp b/lib/gsl/tests/string_span_tests.cpp new file mode 100644 index 0000000..28d7353 --- /dev/null +++ b/lib/gsl/tests/string_span_tests.cpp @@ -0,0 +1,952 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +using namespace std; +using namespace gsl; + + +SUITE(string_span_tests) +{ + + TEST(TestLiteralConstruction) + { + cwstring_span<> v = ensure_z(L"Hello"); + CHECK(5 == v.length()); + +#ifdef CONFIRM_COMPILATION_ERRORS + wstring_span<> v2 = ensure0(L"Hello"); +#endif + } + + TEST(TestConstructFromStdString) + { + std::string s = "Hello there world"; + cstring_span<> v = s; + CHECK(v.length() == static_cast::size_type>(s.length())); + } + + TEST(TestConstructFromStdVector) + { + std::vector vec(5, 'h'); + string_span<> v = vec; + CHECK(v.length() == static_cast::size_type>(vec.size())); + } + + TEST(TestStackArrayConstruction) + { + wchar_t stack_string[] = L"Hello"; + + { + cwstring_span<> v = ensure_z(stack_string); + CHECK(v.length() == 5); + } + + { + cwstring_span<> v = stack_string; + CHECK(v.length() == 5); + } + + { + wstring_span<> v = ensure_z(stack_string); + CHECK(v.length() == 5); + } + + { + wstring_span<> v = stack_string; + CHECK(v.length() == 5); + } + } + + TEST(TestConstructFromConstCharPointer) + { + const char* s = "Hello"; + cstring_span<> v = ensure_z(s); + CHECK(v.length() == 5); + } + + TEST(TestConversionToConst) + { + char stack_string[] = "Hello"; + string_span<> v = ensure_z(stack_string); + cstring_span<> v2 = v; + CHECK(v.length() == v2.length()); + } + + TEST(TestConversionFromConst) + { + char stack_string[] = "Hello"; + cstring_span<> v = ensure_z(stack_string); + (void)v; +#ifdef CONFIRM_COMPILATION_ERRORS + string_span<> v2 = v; + string_span<> v3 = "Hello"; +#endif + } + + TEST(TestToString) + { + auto s = gsl::to_string(cstring_span<>{}); + CHECK(s.length() == 0); + + char stack_string[] = "Hello"; + cstring_span<> v = ensure_z(stack_string); + auto s2 = gsl::to_string(v); + CHECK(static_cast::size_type>(s2.length()) == v.length()); + CHECK(s2.length() == 5); + } + + TEST(EqualityAndImplicitConstructors) + { + { + cstring_span<> span = "Hello"; + cstring_span<> span1; + + // comparison to empty span + CHECK(span1 != span); + CHECK(span != span1); + } + + { + cstring_span<> span = "Hello"; + cstring_span<> span1 = "Hello1"; + + // comparison to different span + CHECK(span1 != span); + CHECK(span != span1); + } + + { + cstring_span<> span = "Hello"; + + const char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + const char ar1[] = "Hello"; + const char ar2[10] = "Hello"; + const char* ptr = "Hello"; + const std::string str = "Hello"; + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + gsl::span sp = ensure_z("Hello"); + + // comparison to literal + CHECK(span == cstring_span<>("Hello")); + + // comparison to static array with no null termination + CHECK(span == cstring_span<>(ar)); + + // comparison to static array with null at the end + CHECK(span == cstring_span<>(ar1)); + + // comparison to static array with null in the middle + CHECK(span == cstring_span<>(ar2)); + + // comparison to null-terminated c string + CHECK(span == cstring_span<>(ptr, 5)); + + // comparison to string + CHECK(span == cstring_span<>(str)); + + // comparison to vector of charaters with no null termination + CHECK(span == cstring_span<>(vec)); + + // comparison to span + CHECK(span == cstring_span<>(sp)); + + // comparison to string_span + CHECK(span == span); + } + + { + char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + + string_span<> span = ar; + + char ar1[] = "Hello"; + char ar2[10] = "Hello"; + char* ptr = ar; + std::string str = "Hello"; + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + gsl::span sp = ensure_z(ar1); + + // comparison to static array with no null termination + CHECK(span == string_span<>(ar)); + + // comparison to static array with null at the end + CHECK(span == string_span<>(ar1)); + + // comparison to static array with null in the middle + CHECK(span == string_span<>(ar2)); + + // comparison to null-terminated c string + CHECK(span == string_span<>(ptr, 5)); + + // comparison to string + CHECK(span == string_span<>(str)); + + // comparison to vector of charaters with no null termination + CHECK(span == string_span<>(vec)); + + // comparison to span + CHECK(span == string_span<>(sp)); + + // comparison to string_span + CHECK(span == span); + } + + + { + const char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + const char ar1[] = "Hello"; + const char ar2[10] = "Hello"; + const std::string str = "Hello"; + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + gsl::span sp = ensure_z("Hello"); + + cstring_span<> span = "Hello"; + + // const span, const other type + + CHECK(span == "Hello"); + CHECK(span == ar); + CHECK(span == ar1); + CHECK(span == ar2); +#ifdef CONFIRM_COMPILATION_ERRORS + const char* ptr = "Hello"; + CHECK(span == ptr); +#endif + CHECK(span == str); + CHECK(span == vec); + CHECK(span == sp); + + CHECK("Hello" == span); + CHECK(ar == span); + CHECK(ar1 == span); + CHECK(ar2 == span); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(ptr == span); +#endif + CHECK(str == span); + CHECK(vec == span); + CHECK(sp == span); + + // const span, non-const other type + + char _ar[] = { 'H', 'e', 'l', 'l', 'o' }; + char _ar1[] = "Hello"; + char _ar2[10] = "Hello"; + char* _ptr = _ar; + std::string _str = "Hello"; + std::vector _vec = { 'H', 'e', 'l', 'l', 'o' }; + gsl::span _sp{ _ar, 5 }; + + CHECK(span == _ar); + CHECK(span == _ar1); + CHECK(span == _ar2); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(span == _ptr); +#endif + CHECK(span == _str); + CHECK(span == _vec); + CHECK(span == _sp); + + CHECK(_ar == span); + CHECK(_ar1 == span); + CHECK(_ar2 == span); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(_ptr == span); +#endif + CHECK(_str == span); + CHECK(_vec == span); + CHECK(_sp == span); + + string_span<> _span{ _ptr, 5 }; + + // non-const span, non-const other type + + CHECK(_span == _ar); + CHECK(_span == _ar1); + CHECK(_span == _ar2); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(_span == _ptr); +#endif + CHECK(_span == _str); + CHECK(_span == _vec); + CHECK(_span == _sp); + + CHECK(_ar == _span); + CHECK(_ar1 == _span); + CHECK(_ar2 == _span); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(_ptr == _span); +#endif + CHECK(_str == _span); + CHECK(_vec == _span); + CHECK(_sp == _span); + + // non-const span, const other type + + CHECK(_span == "Hello"); + CHECK(_span == ar); + CHECK(_span == ar1); + CHECK(_span == ar2); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(_span == ptr); +#endif + CHECK(_span == str); + CHECK(_span == vec); + CHECK(_span == sp); + + CHECK("Hello" == _span); + CHECK(ar == _span); + CHECK(ar1 == _span); + CHECK(ar2 == _span); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(ptr == _span); +#endif + CHECK(str == _span); + CHECK(vec == _span); + CHECK(sp == _span); + + // two spans + + CHECK(_span == span); + CHECK(span == _span); + } + + { + std::vector str1 = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> span1 = str1; + std::vector str2 = std::move(str1); + cstring_span<> span2 = str2; + + // comparison of spans from the same vector before and after move (ok) + CHECK(span1 == span2); + } + } + + TEST(ComparisonAndImplicitConstructors) + { + { + cstring_span<> span = "Hello"; + + const char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + const char ar1[] = "Hello"; + const char ar2[10] = "Hello"; + const char* ptr = "Hello"; + const std::string str = "Hello"; + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + + // comparison to literal + CHECK(span < cstring_span<>("Helloo")); + CHECK(span > cstring_span<>("Hell")); + + // comparison to static array with no null termination + CHECK(span >= cstring_span<>(ar)); + + // comparison to static array with null at the end + CHECK(span <= cstring_span<>(ar1)); + + // comparison to static array with null in the middle + CHECK(span >= cstring_span<>(ar2)); + + // comparison to null-terminated c string + CHECK(span <= cstring_span<>(ptr, 5)); + + // comparison to string + CHECK(span >= cstring_span<>(str)); + + // comparison to vector of charaters with no null termination + CHECK(span <= cstring_span<>(vec)); + } + + { + char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + + string_span<> span = ar; + + char larr[] = "Hell"; + char rarr[] = "Helloo"; + + char ar1[] = "Hello"; + char ar2[10] = "Hello"; + char* ptr = ar; + std::string str = "Hello"; + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + + + // comparison to static array with no null termination + CHECK(span <= string_span<>(ar)); + CHECK(span < string_span<>(rarr)); + CHECK(span > string_span<>(larr)); + + // comparison to static array with null at the end + CHECK(span >= string_span<>(ar1)); + + // comparison to static array with null in the middle + CHECK(span <= string_span<>(ar2)); + + // comparison to null-terminated c string + CHECK(span >= string_span<>(ptr, 5)); + + // comparison to string + CHECK(span <= string_span<>(str)); + + // comparison to vector of charaters with no null termination + CHECK(span >= string_span<>(vec)); + } + } + TEST(ConstrutorsEnsureZ) + { + // remove z from literals + { + cstring_span<> sp = "hello"; + CHECK((sp.length() == 5)); + } + + // take the string as is + { + auto str = std::string("hello"); + cstring_span<> sp = str; + CHECK((sp.length() == 5)); + } + + // ensure z on c strings + { + char* ptr = new char[3]; + + ptr[0] = 'a'; + ptr[1] = 'b'; + ptr[2] = '\0'; + + string_span<> span = ensure_z(ptr); + CHECK(span.length() == 2); + + delete[] ptr; + } + } + + TEST(Constructors) + { + // creating cstring_span + + // from span of a final extent + { + span sp = "Hello"; + cstring_span<> span = sp; + CHECK(span.length() == 6); + } + + // from const span of a final extent to non-const string_span +#ifdef CONFIRM_COMPILATION_ERRORS + { + span sp = "Hello"; + string_span<> span = sp; + CHECK(span.length() == 6); + } +#endif + + // from string temporary +#ifdef CONFIRM_COMPILATION_ERRORS + { + cstring_span<> span = std::string("Hello"); + } +#endif + + // default + { + cstring_span<> span; + CHECK(span.length() == 0); + } + + // from nullptr + { + cstring_span<> span(nullptr); + CHECK(span.length() == 0); + } + + // from string literal + { + cstring_span<> span = "Hello"; + CHECK(span.length() == 5); + } + + // from const static array + { + const char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> span = ar; + CHECK(span.length() == 5); + } + + // from non-const static array + { + char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> span = ar; + CHECK(span.length() == 5); + } + + // from const ptr and length + { + const char* ptr = "Hello"; + cstring_span<> span{ ptr, 5 }; + CHECK(span.length() == 5); + } + + // from const ptr and length, include 0 + { + const char* ptr = "Hello"; + cstring_span<> span{ ptr, 6 }; + CHECK(span.length() == 6); + } + + // from const ptr and length, 0 inside + { + const char* ptr = "He\0lo"; + cstring_span<> span{ ptr, 5 }; + CHECK(span.length() == 5); + } + + // from non-const ptr and length + { + char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + char* ptr = ar; + cstring_span<> span{ ptr, 5 }; + CHECK(span.length() == 5); + } + + // from non-const ptr and length, 0 inside + { + char ar[] = { 'H', 'e', '\0', 'l', 'o' }; + char* ptr = ar; + cstring_span<> span{ ptr, 5 }; + CHECK(span.length() == 5); + } + + // from const string + { + const std::string str = "Hello"; + cstring_span<> span = str; + CHECK(span.length() == 5); + } + + // from non-const string + { + std::string str = "Hello"; + cstring_span<> span = str; + CHECK(span.length() == 5); + } + + // from const vector + { + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> span = vec; + CHECK(span.length() == 5); + } + + // from non-const vector + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> span = vec; + CHECK(span.length() == 5); + } + + // from const span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + const span inner = vec; + cstring_span<> span = inner; + CHECK(span.length() == 5); + } + + // from non-const span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + span inner = vec; + cstring_span<> span = inner; + CHECK(span.length() == 5); + } + + // from const string_span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> tmp = vec; + cstring_span<> span = tmp; + CHECK(span.length() == 5); + } + + // from non-const string_span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> tmp = vec; + cstring_span<> span = tmp; + CHECK(span.length() == 5); + } + + // creating string_span + + // from string literal + { +#ifdef CONFIRM_COMPILATION_ERRORS + string_span<> span = "Hello"; +#endif + } + + // from const static array + { +#ifdef CONFIRM_COMPILATION_ERRORS + const char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = ar; + CHECK(span.length() == 5); +#endif + } + + // from non-const static array + { + char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = ar; + CHECK(span.length() == 5); + } + + // from const ptr and length + { +#ifdef CONFIRM_COMPILATION_ERRORS + const char* ptr = "Hello"; + string_span<> span{ ptr, 5 }; + CHECK(span.length() == 5); +#endif + } + + // from non-const ptr and length + { + char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + char* ptr = ar; + string_span<> span{ ptr, 5 }; + CHECK(span.length() == 5); + } + + // from const string + { +#ifdef CONFIRM_COMPILATION_ERRORS + const std::string str = "Hello"; + string_span<> span = str; + CHECK(span.length() == 5); +#endif + } + + // from non-const string + { + std::string str = "Hello"; + string_span<> span = str; + CHECK(span.length() == 5); + } + + // from const vector + { +#ifdef CONFIRM_COMPILATION_ERRORS + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = vec; + CHECK(span.length() == 5); +#endif + } + + // from non-const vector + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = vec; + CHECK(span.length() == 5); + } + + // from const span + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + const span inner = vec; + string_span<> span = inner; + CHECK(span.length() == 5); +#endif + } + + // from non-const span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + span inner = vec; + string_span<> span = inner; + CHECK(span.length() == 5); + } + + // from non-const span of non-const data from const vector + { +#ifdef CONFIRM_COMPILATION_ERRORS + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + const span inner = vec; + string_span<> span = inner; + CHECK(span.length() == 5); +#endif + } + + // from const string_span + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> tmp = vec; + string_span<> span = tmp; + CHECK(span.length() == 5); +#endif + } + + // from non-const string_span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> tmp = vec; + string_span<> span = tmp; + CHECK(span.length() == 5); + } + + // from non-const string_span from const vector + { +#ifdef CONFIRM_COMPILATION_ERRORS + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> tmp = vec; + string_span<> span = tmp; + CHECK(span.length() == 5); +#endif + } + + // from const string_span of non-const data + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + const string_span<> tmp = vec; + string_span<> span = tmp; + CHECK(span.length() == 5); + } + } + + template + T move_wrapper(T&& t) + { + return std::move(t); + } + + template + T create() { return T{}; } + + template + void use(basic_string_span s) {} + + TEST(MoveConstructors) + { + // move string_span + { + cstring_span<> span = "Hello"; + auto span1 = std::move(span); + CHECK(span1.length() == 5); + } + { + cstring_span<> span = "Hello"; + auto span1 = move_wrapper(std::move(span)); + CHECK(span1.length() == 5); + } + { + cstring_span<> span = "Hello"; + auto span1 = move_wrapper(std::move(span)); + CHECK(span1.length() == 5); + } + + // move span + { + span span = ensure_z("Hello"); + cstring_span<> span1 = std::move(span); + CHECK(span1.length() == 5); + } + { + span span = ensure_z("Hello"); + cstring_span<> span2 = move_wrapper(std::move(span)); + CHECK(span2.length() == 5); + } + + // move string + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::string str = "Hello"; + string_span<> span = std::move(str); + CHECK(span.length() == 5); +#endif + } + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::string str = "Hello"; + string_span<> span = move_wrapper(std::move(str)); + CHECK(span.length() == 5); +#endif + } + { +#ifdef CONFIRM_COMPILATION_ERRORS + use(create()); +#endif + } + + // move container + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = std::move(vec); + CHECK(span.length() == 5); +#endif + } + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = move_wrapper>(std::move(vec)); + CHECK(span.length() == 5); +#endif + } + { +#ifdef CONFIRM_COMPILATION_ERRORS + use(create>()); +#endif + } + } + + TEST(Conversion) + { +#ifdef CONFIRM_COMPILATION_ERRORS + cstring_span<> span = "Hello"; + cwstring_span<> wspan{ span }; + CHECK(wspan.length() == 5); +#endif + } + + czstring_span<> CreateTempName(string_span<> span) + { + Expects(span.size() > 1); + + int last = 0; + if (span.size() > 4) + { + span[0] = 't'; + span[1] = 'm'; + span[2] = 'p'; + last = 3; + } + span[last] = '\0'; + + auto ret = span.subspan(0, 4); + return{ ret }; + } + + TEST(zstring) + { + + // create zspan from zero terminated string + { + char buf[1]; + buf[0] = '\0'; + + zstring_span<> zspan({ buf, 1 }); + + CHECK(strlen(zspan.assume_z()) == 0); + CHECK(zspan.as_string_span().size() == 0); + CHECK(zspan.ensure_z().size() == 0); + } + + // create zspan from non-zero terminated string + { + char buf[1]; + buf[0] = 'a'; + + auto workaround_macro = [&]() { zstring_span<> zspan({ buf, 1 }); }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + // usage scenario: create zero-terminated temp file name and pass to a legacy API + { + char buf[10]; + + auto name = CreateTempName({ buf, 10 }); + if (!name.empty()) + { + czstring<> str = name.assume_z(); + CHECK(strlen(str) == 3); + CHECK(*(str+3) == '\0'); + } + } + + } + + cwzstring_span<> CreateTempNameW(wstring_span<> span) + { + Expects(span.size() > 1); + + int last = 0; + if (span.size() > 4) + { + span[0] = L't'; + span[1] = L'm'; + span[2] = L'p'; + last = 3; + } + span[last] = L'\0'; + + auto ret = span.subspan(0, 4); + return{ ret }; + } + + TEST(wzstring) + { + + // create zspan from zero terminated string + { + wchar_t buf[1]; + buf[0] = L'\0'; + + wzstring_span<> zspan({ buf, 1 }); + + CHECK(wcsnlen(zspan.assume_z(), 1) == 0); + CHECK(zspan.as_string_span().size() == 0); + CHECK(zspan.ensure_z().size() == 0); + } + + // create zspan from non-zero terminated string + { + wchar_t buf[1]; + buf[0] = L'a'; + + auto workaround_macro = [&]() { wzstring_span<> zspan({ buf, 1 }); }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + // usage scenario: create zero-terminated temp file name and pass to a legacy API + { + wchar_t buf[10]; + + auto name = CreateTempNameW({ buf, 10 }); + if (!name.empty()) + { + cwzstring<> str = name.assume_z(); + CHECK(wcsnlen(str, 10) == 3); + CHECK(*(str + 3) == L'\0'); + } + } + + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/lib/gsl/tests/utils_tests.cpp b/lib/gsl/tests/utils_tests.cpp new file mode 100644 index 0000000..a46d6e4 --- /dev/null +++ b/lib/gsl/tests/utils_tests.cpp @@ -0,0 +1,119 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +using namespace gsl; + +SUITE(utils_tests) +{ + void f(int& i) + { + i += 1; + } + + TEST(finally_lambda) + { + int i = 0; + { + auto _ = finally([&]() {f(i);}); + CHECK(i == 0); + } + CHECK(i == 1); + } + + TEST(finally_lambda_move) + { + int i = 0; + { + auto _1 = finally([&]() {f(i);}); + { + auto _2 = std::move(_1); + CHECK(i == 0); + } + CHECK(i == 1); + { + auto _2 = std::move(_1); + CHECK(i == 1); + } + CHECK(i == 1); + } + CHECK(i == 1); + } + + TEST(finally_function_with_bind) + { + int i = 0; + { + auto _ = finally(std::bind(&f, std::ref(i))); + CHECK(i == 0); + } + CHECK(i == 1); + } + + int j = 0; + void g() { j += 1; }; + TEST(finally_function_ptr) + { + j = 0; + { + auto _ = finally(&g); + CHECK(j == 0); + } + CHECK(j == 1); + } + + TEST(narrow_cast) + { + int n = 120; + char c = narrow_cast(n); + CHECK(c == 120); + + n = 300; + unsigned char uc = narrow_cast(n); + CHECK(uc == 44); + } + + TEST(narrow) + { + int n = 120; + char c = narrow(n); + CHECK(c == 120); + + n = 300; + CHECK_THROW(narrow(n), narrowing_error); + + const auto int32_max = std::numeric_limits::max(); + const auto int32_min = std::numeric_limits::min(); + + CHECK(narrow(int32_t(0)) == 0); + CHECK(narrow(int32_t(1)) == 1); + CHECK(narrow(int32_max) == int32_max); + + CHECK_THROW(narrow(int32_t(-1)), narrowing_error); + CHECK_THROW(narrow(int32_min), narrowing_error); + + n = -42; + CHECK_THROW(narrow(n), narrowing_error); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +}