forked from microsoft/GSL
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0da5eff392 | |||
| a75212b9f3 | |||
| b2f6bec48e | |||
| f3c5967126 | |||
| 4524208d32 | |||
| 66ca39d9c8 | |||
| 0249144ad1 | |||
| 688ffcde90 | |||
| fcf3fe37c6 | |||
| 9f9f65c7e2 | |||
| 5ca9c77666 | |||
| bf9d5e1aef | |||
| 756c91ab89 | |||
| 1883887359 | |||
| c31617f56a | |||
| 4fb59125d1 | |||
| 543d0dd3fe | |||
| 494e6e988c | |||
| 7e0943d20d | |||
| 2d343b0440 | |||
| c21970972b | |||
| 466e4ebaa5 | |||
| 3325bbd33d |
+1
-1
@@ -31,4 +31,4 @@ AlignConsecutiveAssignments: false
|
||||
AlignTrailingComments: true
|
||||
|
||||
SpaceAfterCStyleCast: true
|
||||
CommentPragmas: '^ NO-FORMAT:'
|
||||
WhitespaceSensitiveMacros: [GSL_SUPPRESS]
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# GitHub Copilot Instructions for GSL (Guidelines Support Library)
|
||||
|
||||
## Project Overview
|
||||
This repository contains the Guidelines Support Library (GSL), a Microsoft implementation of types and functions
|
||||
suggested for use by the C++ Core Guidelines. It's a header-only C++ library with emphasis on safety,
|
||||
correctness, and zero overhead.
|
||||
|
||||
## Coding Standards
|
||||
|
||||
### General
|
||||
- Follow C++ Core Guidelines wherever possible
|
||||
- Use meaningful type, function, and template parameter names
|
||||
- Keep functions small and focused with clear preconditions/postconditions
|
||||
- Include comments for complex code, but prefer self-documenting code
|
||||
- Use the Expects() and Ensures() macros for contract verification
|
||||
|
||||
### Style Guidelines
|
||||
- Use 4 spaces for indentation (not tabs)
|
||||
- Maximum line length of 100 characters
|
||||
- Follow GSL naming conventions (lowercase with underscores)
|
||||
- Keep templates clean and readable with appropriate spacing
|
||||
- Use C++14 features since this is the minimum standard supported
|
||||
|
||||
### Error Handling
|
||||
- Use Expects() for preconditions and Ensures() for postconditions
|
||||
- Design for fail-fast semantics (std::terminate) on contract violations
|
||||
- Template constraints should use static_assert or SFINAE
|
||||
- Don't throw exceptions from basic operations
|
||||
|
||||
### Testing
|
||||
- Write thorough unit tests for every component using GTest
|
||||
- Test for all edge cases and error conditions
|
||||
- Ensure cross-platform compatibility in tests
|
||||
- Maintain 100% code coverage for changed code
|
||||
|
||||
## Project-Specific Conventions
|
||||
|
||||
### Architecture
|
||||
- All public types must be in the gsl namespace
|
||||
- Design for zero overhead abstractions when possible
|
||||
- Respect the distinction between Owners and Views
|
||||
- Maintain backward compatibility with existing GSL code
|
||||
|
||||
### Version Control
|
||||
- Link all PRs to related issues
|
||||
- Use clear commit messages explaining what and why
|
||||
- Follow the contribution guidelines documented in CONTRIBUTING.md
|
||||
- PRs should include appropriate tests with 100% coverage for changed code
|
||||
|
||||
### Documentation
|
||||
- Document all public APIs with clarity on preconditions and postconditions
|
||||
- Keep header comments up-to-date
|
||||
- Include examples for complex functionality in docs/headers.md
|
||||
|
||||
## Technology Stack
|
||||
- C++14 (minimum) for core implementation
|
||||
- CMake build system (3.14+)
|
||||
- Google Test for unit testing
|
||||
- Support for multiple compilers (MSVC, GCC, Clang)
|
||||
|
||||
## Security Considerations
|
||||
- Bounds checking is a core principle - enforce it consistently
|
||||
- Design for safety while minimizing overhead
|
||||
- Ensure undefined behavior is explicitly detected where possible
|
||||
|
||||
## Performance Guidelines
|
||||
- Optimize for both safety and performance
|
||||
- Constexpr-enable functions wherever possible
|
||||
- Avoid hidden allocations
|
||||
- Use noexcept appropriately for move operations and other performance-critical functions
|
||||
|
||||
## Cross-Platform Support
|
||||
- Code must work across:
|
||||
- Windows (MSVC)
|
||||
- Linux (GCC, Clang)
|
||||
- macOS (AppleClang)
|
||||
- Android and iOS where applicable
|
||||
|
||||
## Copilot Tasks
|
||||
- You can find the CMake artifacts for C++20 in build-cxx20 and C++14 in build-cxx14.
|
||||
- Before publishing a PR, verify the following:
|
||||
- There are no compiler warnings or errors when building the test suite.
|
||||
- The test suite passes on all supported platforms and compilers.
|
||||
- The test suite passes for both C++14 and C++20.
|
||||
@@ -17,13 +17,13 @@ jobs:
|
||||
run:
|
||||
working-directory: build
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Create build directory
|
||||
run: mkdir -p build
|
||||
working-directory: .
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: 8
|
||||
distribution: zulu
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
name: Code Formatting
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CLANG_VERSION: "20"
|
||||
|
||||
jobs:
|
||||
clang-format:
|
||||
name: Run clang-format
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Install the exact clang-format binary
|
||||
- name: Install clang-format
|
||||
run: |
|
||||
sudo apt install clang-format-${{ env.CLANG_VERSION }}
|
||||
|
||||
# Prints the version of clang-format being used
|
||||
- name: Log clang-format version
|
||||
run: clang-format-${{ env.CLANG_VERSION }} --version
|
||||
|
||||
# Runs clang-format over the repository codebase
|
||||
- name: Check format
|
||||
run: |
|
||||
{
|
||||
find include/gsl -type f
|
||||
find tests -type f \( -name '*.cpp' -o -name '*.h' \)
|
||||
} | xargs clang-format-${{ env.CLANG_VERSION }} --dry-run --Werror
|
||||
@@ -1,56 +1,33 @@
|
||||
name: Composite CMake
|
||||
inputs:
|
||||
cmake_generator:
|
||||
required: false
|
||||
type: string
|
||||
default: 'Unix Makefiles'
|
||||
cmake_build_type:
|
||||
cmake_preset:
|
||||
required: true
|
||||
type: string
|
||||
default: ''
|
||||
cmake_cxx_compiler:
|
||||
required: false
|
||||
type: string
|
||||
gsl_cxx_standard:
|
||||
required: true
|
||||
type: number
|
||||
extra_cmake_args:
|
||||
extra_cmake_build_args:
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
build_cmd:
|
||||
required: true
|
||||
type: string
|
||||
default: 'make'
|
||||
test_cmd:
|
||||
extra_cmake_configure_args:
|
||||
required: false
|
||||
type: string
|
||||
default: 'make test'
|
||||
shell:
|
||||
required: false
|
||||
type: string
|
||||
default: 'bash'
|
||||
|
||||
default: ''
|
||||
extra_ctest_args:
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Create build directory
|
||||
run: mkdir build
|
||||
shell: ${{ inputs.shell }}
|
||||
|
||||
- name: Configure CMake
|
||||
working-directory: build
|
||||
run: cmake -G "${{ inputs.cmake_generator }}" -DCMAKE_BUILD_TYPE=${{ inputs.cmake_build_type }} -DCMAKE_CXX_COMPILER=${{ inputs.cmake_cxx_compiler }} -DGSL_CXX_STANDARD=${{ inputs.gsl_cxx_standard }} -DCI_TESTING:BOOL=ON -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -Werror=dev ${{ inputs.extra_cmake_args }} ..
|
||||
shell: ${{ inputs.shell }}
|
||||
run: cmake --preset ${{ inputs.cmake_preset }} ${{ inputs.extra_cmake_configure_args }} -DCI_TESTING:BOOL=ON -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -Werror=dev
|
||||
shell: ${{ env.RUNNER_OS == 'Windows' && 'pwsh' || 'bash' }}
|
||||
|
||||
- name: Build
|
||||
working-directory: build
|
||||
run: ${{ inputs.build_cmd }}
|
||||
shell: ${{ inputs.shell }}
|
||||
- name: Build (with preset)
|
||||
run: cmake --build --preset ${{ inputs.cmake_preset }} ${{ inputs.extra_cmake_build_args }}
|
||||
shell: ${{ env.RUNNER_OS == 'Windows' && 'pwsh' || 'bash' }}
|
||||
|
||||
- name: Test
|
||||
working-directory: build
|
||||
run: ${{ inputs.test_cmd }}
|
||||
shell: ${{ inputs.shell }}
|
||||
- name: Test (with preset)
|
||||
run: ctest --preset ${{ inputs.cmake_preset }} ${{ inputs.extra_ctest_args }} --output-on-failure --no-compress-output
|
||||
shell: ${{ env.RUNNER_OS == 'Windows' && 'pwsh' || 'bash' }}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, macos-latest ]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: lukka/get-cmake@latest
|
||||
with:
|
||||
cmakeVersion: 3.14.0
|
||||
|
||||
@@ -30,14 +30,12 @@ jobs:
|
||||
cxx_version: 23
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Run CMake (configure, build, test)
|
||||
uses: ./.github/workflows/cmake
|
||||
with:
|
||||
cmake_build_type: ${{ matrix.build_type }}
|
||||
cmake_cxx_compiler: g++-${{ matrix.gcc_version }}
|
||||
gsl_cxx_standard: ${{ matrix.cxx_version }}
|
||||
cmake_preset: gcc-${{ matrix.cxx_version }}-${{ matrix.build_type == 'Debug' && 'debug' || 'release' }}
|
||||
|
||||
clang:
|
||||
strategy:
|
||||
@@ -53,24 +51,22 @@ jobs:
|
||||
cxx_version: 23
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Run CMake (configure, build, test)
|
||||
uses: ./.github/workflows/cmake
|
||||
with:
|
||||
cmake_build_type: ${{ matrix.build_type }}
|
||||
cmake_cxx_compiler: clang++-${{ matrix.clang_version }}
|
||||
gsl_cxx_standard: ${{ matrix.cxx_version }}
|
||||
cmake_preset: clang-${{ matrix.cxx_version }}-${{ matrix.build_type == 'Debug' && 'debug' || 'release' }}
|
||||
|
||||
xcode:
|
||||
strategy:
|
||||
matrix:
|
||||
xcode_version: [ '15.4' ]
|
||||
xcode_version: [ '16.4' ]
|
||||
build_type: [ Debug, Release ]
|
||||
cxx_version: [ 14, 17, 20, 23 ]
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: select xcode version
|
||||
run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode_version }}.app
|
||||
@@ -78,38 +74,33 @@ jobs:
|
||||
- name: Run CMake (configure, build, test)
|
||||
uses: ./.github/workflows/cmake
|
||||
with:
|
||||
cmake_build_type: ${{ matrix.build_type }}
|
||||
cmake_cxx_compiler: clang++
|
||||
gsl_cxx_standard: ${{ matrix.cxx_version }}
|
||||
cmake_preset: clang-${{ matrix.cxx_version }}-${{ matrix.build_type == 'Debug' && 'debug' || 'release' }}
|
||||
extra_cmake_configure_args: '-DCMAKE_CXX_FLAGS="-isysroot \"$(xcode-select --print-path)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk\""'
|
||||
|
||||
VisualStudio:
|
||||
msvc:
|
||||
strategy:
|
||||
matrix:
|
||||
generator: [ 'Visual Studio 16 2019', 'Visual Studio 17 2022' ]
|
||||
image: [ windows-2019, windows-2022 ]
|
||||
image: [ windows-2022, windows-2025 ]
|
||||
build_type: [ Debug, Release ]
|
||||
extra_args: [ '', '-T ClangCL' ]
|
||||
toolset: [ 'msvc', 'ClangCL' ]
|
||||
cxx_version: [ 14, 17, 20, 23 ]
|
||||
exclude:
|
||||
- generator: 'Visual Studio 17 2022'
|
||||
image: windows-2019
|
||||
- generator: 'Visual Studio 16 2019'
|
||||
image: windows-2022
|
||||
- generator: 'Visual Studio 16 2019'
|
||||
cxx_version: 23
|
||||
include:
|
||||
# Regular MSVC builds use Ninja (from preset)
|
||||
- toolset: 'msvc'
|
||||
generator_override: ''
|
||||
# ClangCL builds require Visual Studio generator
|
||||
- toolset: 'ClangCL'
|
||||
generator_override: '-G "Visual Studio 17 2022" -T ClangCL'
|
||||
runs-on: ${{ matrix.image }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: microsoft/setup-msbuild@v2
|
||||
- uses: actions/checkout@v6
|
||||
- uses: microsoft/setup-msbuild@v3
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Run CMake (configure, build, test)
|
||||
uses: ./.github/workflows/cmake
|
||||
with:
|
||||
cmake_generator: ${{ matrix.generator }}
|
||||
cmake_build_type: ${{ matrix.build_type }}
|
||||
gsl_cxx_standard: ${{ matrix.cxx_version }}
|
||||
extra_cmake_args: ${{ matrix.extra_args }}
|
||||
build_cmd: msbuild GSL.sln
|
||||
test_cmd: ctest . --output-on-failure --no-compress-output
|
||||
shell: pwsh
|
||||
|
||||
cmake_preset: msvc-${{ matrix.cxx_version }}-${{ matrix.build_type == 'Debug' && 'debug' || 'release' }}
|
||||
extra_cmake_configure_args: ${{ matrix.generator_override }}
|
||||
extra_cmake_build_args: ${{ matrix.toolset == 'ClangCL' && format('--config {0}', matrix.build_type) || '' }}
|
||||
extra_ctest_args: ${{ matrix.toolset == 'ClangCL' && format('-C {0}', matrix.build_type) || '' }}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
name: "Copilot Setup Steps"
|
||||
|
||||
on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
copilot-setup-steps:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install Build Dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y clang cmake make
|
||||
|
||||
- name: Configure CMake (C++14)
|
||||
run: cmake --preset clang-14-debug
|
||||
|
||||
- name: Configure CMake (C++20)
|
||||
run: cmake --preset clang-20-debug
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
run:
|
||||
working-directory: build
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Create build directory
|
||||
run: mkdir -p build
|
||||
@@ -25,11 +25,12 @@ jobs:
|
||||
-GXcode \
|
||||
-DCMAKE_SYSTEM_NAME=iOS \
|
||||
"-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=9 \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 \
|
||||
-DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \
|
||||
"-DMACOSX_BUNDLE_GUI_IDENTIFIER=GSL.\$(EXECUTABLE_NAME)" \
|
||||
-DMACOSX_BUNDLE_BUNDLE_VERSION=3.1.0 \
|
||||
-DMACOSX_BUNDLE_SHORT_VERSION_STRING=3.1.0 \
|
||||
-DMACOSX_BUNDLE_BUNDLE_VERSION=4.2.0 \
|
||||
-DMACOSX_BUNDLE_SHORT_VERSION_STRING=4.2.0 \
|
||||
-DCMAKE_CXX_FLAGS="-Wno-missing-include-dirs" \
|
||||
..
|
||||
|
||||
- name: Build
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
name: Shell script linter
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
clang-format:
|
||||
name: Run shfmt and shellcheck
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Install the needed binaries
|
||||
- name: Install shfmt and shellcheck
|
||||
run: |
|
||||
sudo apt install shfmt shellcheck
|
||||
|
||||
- name: Check format
|
||||
run: |
|
||||
find scripts -type f -name '*.sh' -exec shfmt -l {} \;
|
||||
|
||||
- name: Run shellcheck
|
||||
run: |
|
||||
find scripts -type f -name '*.sh' -exec shellcheck {} \;
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
CMakeFiles
|
||||
build
|
||||
build*/
|
||||
tests/CMakeFiles
|
||||
tests/Debug
|
||||
*.opensdf
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.14...3.16)
|
||||
|
||||
project(GSL VERSION 4.1.0 LANGUAGES CXX)
|
||||
project(GSL VERSION 4.2.0 LANGUAGES CXX)
|
||||
|
||||
add_library(GSL INTERFACE)
|
||||
add_library(Microsoft.GSL::GSL ALIAS GSL)
|
||||
|
||||
@@ -0,0 +1,459 @@
|
||||
{
|
||||
"version": 3,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "base",
|
||||
"hidden": true,
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"installDir": "${sourceDir}/install/${presetName}",
|
||||
"generator": "Ninja",
|
||||
"cacheVariables": {
|
||||
"GSL_CXX_STANDARD": "14",
|
||||
"GSL_TEST": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "msvc-base",
|
||||
"inherits": "base",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_CXX_COMPILER": "cl"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gcc-base",
|
||||
"inherits": "base",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CMAKE_CXX_COMPILER": "g++",
|
||||
"CMAKE_C_COMPILER": "gcc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clang-base",
|
||||
"inherits": "base",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CMAKE_CXX_COMPILER": "clang++",
|
||||
"CMAKE_C_COMPILER": "clang"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "msvc-14-debug",
|
||||
"displayName": "MSVC C++14 Debug",
|
||||
"inherits": "msvc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"GSL_CXX_STANDARD": "14"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "msvc-14-release",
|
||||
"displayName": "MSVC C++14 Release",
|
||||
"inherits": "msvc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"GSL_CXX_STANDARD": "14"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "msvc-17-debug",
|
||||
"displayName": "MSVC C++17 Debug",
|
||||
"inherits": "msvc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"GSL_CXX_STANDARD": "17"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "msvc-17-release",
|
||||
"displayName": "MSVC C++17 Release",
|
||||
"inherits": "msvc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"GSL_CXX_STANDARD": "17"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "msvc-20-debug",
|
||||
"displayName": "MSVC C++20 Debug",
|
||||
"inherits": "msvc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"GSL_CXX_STANDARD": "20"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "msvc-20-release",
|
||||
"displayName": "MSVC C++20 Release",
|
||||
"inherits": "msvc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"GSL_CXX_STANDARD": "20"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "msvc-23-debug",
|
||||
"displayName": "MSVC C++23 Debug",
|
||||
"inherits": "msvc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"GSL_CXX_STANDARD": "23"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "msvc-23-release",
|
||||
"displayName": "MSVC C++23 Release",
|
||||
"inherits": "msvc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"GSL_CXX_STANDARD": "23"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gcc-14-debug",
|
||||
"displayName": "GCC C++14 Debug",
|
||||
"inherits": "gcc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"GSL_CXX_STANDARD": "14"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gcc-14-release",
|
||||
"displayName": "GCC C++14 Release",
|
||||
"inherits": "gcc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"GSL_CXX_STANDARD": "14"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gcc-17-debug",
|
||||
"displayName": "GCC C++17 Debug",
|
||||
"inherits": "gcc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"GSL_CXX_STANDARD": "17"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gcc-17-release",
|
||||
"displayName": "GCC C++17 Release",
|
||||
"inherits": "gcc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"GSL_CXX_STANDARD": "17"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gcc-20-debug",
|
||||
"displayName": "GCC C++20 Debug",
|
||||
"inherits": "gcc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"GSL_CXX_STANDARD": "20"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gcc-20-release",
|
||||
"displayName": "GCC C++20 Release",
|
||||
"inherits": "gcc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"GSL_CXX_STANDARD": "20"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gcc-23-debug",
|
||||
"displayName": "GCC C++23 Debug",
|
||||
"inherits": "gcc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"GSL_CXX_STANDARD": "23"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gcc-23-release",
|
||||
"displayName": "GCC C++23 Release",
|
||||
"inherits": "gcc-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"GSL_CXX_STANDARD": "23"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clang-14-debug",
|
||||
"displayName": "Clang C++14 Debug",
|
||||
"inherits": "clang-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"GSL_CXX_STANDARD": "14"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clang-14-release",
|
||||
"displayName": "Clang C++14 Release",
|
||||
"inherits": "clang-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"GSL_CXX_STANDARD": "14"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clang-17-debug",
|
||||
"displayName": "Clang C++17 Debug",
|
||||
"inherits": "clang-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"GSL_CXX_STANDARD": "17"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clang-17-release",
|
||||
"displayName": "Clang C++17 Release",
|
||||
"inherits": "clang-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"GSL_CXX_STANDARD": "17"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clang-20-debug",
|
||||
"displayName": "Clang C++20 Debug",
|
||||
"inherits": "clang-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"GSL_CXX_STANDARD": "20"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clang-20-release",
|
||||
"displayName": "Clang C++20 Release",
|
||||
"inherits": "clang-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"GSL_CXX_STANDARD": "20"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clang-23-debug",
|
||||
"displayName": "Clang C++23 Debug",
|
||||
"inherits": "clang-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"GSL_CXX_STANDARD": "23"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clang-23-release",
|
||||
"displayName": "Clang C++23 Release",
|
||||
"inherits": "clang-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"GSL_CXX_STANDARD": "23"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "msvc-14-debug",
|
||||
"configurePreset": "msvc-14-debug"
|
||||
},
|
||||
{
|
||||
"name": "msvc-14-release",
|
||||
"configurePreset": "msvc-14-release"
|
||||
},
|
||||
{
|
||||
"name": "msvc-17-debug",
|
||||
"configurePreset": "msvc-17-debug"
|
||||
},
|
||||
{
|
||||
"name": "msvc-17-release",
|
||||
"configurePreset": "msvc-17-release"
|
||||
},
|
||||
{
|
||||
"name": "msvc-20-debug",
|
||||
"configurePreset": "msvc-20-debug"
|
||||
},
|
||||
{
|
||||
"name": "msvc-20-release",
|
||||
"configurePreset": "msvc-20-release"
|
||||
},
|
||||
{
|
||||
"name": "msvc-23-debug",
|
||||
"configurePreset": "msvc-23-debug"
|
||||
},
|
||||
{
|
||||
"name": "msvc-23-release",
|
||||
"configurePreset": "msvc-23-release"
|
||||
},
|
||||
{
|
||||
"name": "gcc-14-debug",
|
||||
"configurePreset": "gcc-14-debug"
|
||||
},
|
||||
{
|
||||
"name": "gcc-14-release",
|
||||
"configurePreset": "gcc-14-release"
|
||||
},
|
||||
{
|
||||
"name": "gcc-17-debug",
|
||||
"configurePreset": "gcc-17-debug"
|
||||
},
|
||||
{
|
||||
"name": "gcc-17-release",
|
||||
"configurePreset": "gcc-17-release"
|
||||
},
|
||||
{
|
||||
"name": "gcc-20-debug",
|
||||
"configurePreset": "gcc-20-debug"
|
||||
},
|
||||
{
|
||||
"name": "gcc-20-release",
|
||||
"configurePreset": "gcc-20-release"
|
||||
},
|
||||
{
|
||||
"name": "gcc-23-debug",
|
||||
"configurePreset": "gcc-23-debug"
|
||||
},
|
||||
{
|
||||
"name": "gcc-23-release",
|
||||
"configurePreset": "gcc-23-release"
|
||||
},
|
||||
{
|
||||
"name": "clang-14-debug",
|
||||
"configurePreset": "clang-14-debug"
|
||||
},
|
||||
{
|
||||
"name": "clang-14-release",
|
||||
"configurePreset": "clang-14-release"
|
||||
},
|
||||
{
|
||||
"name": "clang-17-debug",
|
||||
"configurePreset": "clang-17-debug"
|
||||
},
|
||||
{
|
||||
"name": "clang-17-release",
|
||||
"configurePreset": "clang-17-release"
|
||||
},
|
||||
{
|
||||
"name": "clang-20-debug",
|
||||
"configurePreset": "clang-20-debug"
|
||||
},
|
||||
{
|
||||
"name": "clang-20-release",
|
||||
"configurePreset": "clang-20-release"
|
||||
},
|
||||
{
|
||||
"name": "clang-23-debug",
|
||||
"configurePreset": "clang-23-debug"
|
||||
},
|
||||
{
|
||||
"name": "clang-23-release",
|
||||
"configurePreset": "clang-23-release"
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "msvc-14-debug",
|
||||
"configurePreset": "msvc-14-debug"
|
||||
},
|
||||
{
|
||||
"name": "msvc-14-release",
|
||||
"configurePreset": "msvc-14-release"
|
||||
},
|
||||
{
|
||||
"name": "msvc-17-debug",
|
||||
"configurePreset": "msvc-17-debug"
|
||||
},
|
||||
{
|
||||
"name": "msvc-17-release",
|
||||
"configurePreset": "msvc-17-release"
|
||||
},
|
||||
{
|
||||
"name": "msvc-20-debug",
|
||||
"configurePreset": "msvc-20-debug"
|
||||
},
|
||||
{
|
||||
"name": "msvc-20-release",
|
||||
"configurePreset": "msvc-20-release"
|
||||
},
|
||||
{
|
||||
"name": "msvc-23-debug",
|
||||
"configurePreset": "msvc-23-debug"
|
||||
},
|
||||
{
|
||||
"name": "msvc-23-release",
|
||||
"configurePreset": "msvc-23-release"
|
||||
},
|
||||
{
|
||||
"name": "gcc-14-debug",
|
||||
"configurePreset": "gcc-14-debug"
|
||||
},
|
||||
{
|
||||
"name": "gcc-14-release",
|
||||
"configurePreset": "gcc-14-release"
|
||||
},
|
||||
{
|
||||
"name": "gcc-17-debug",
|
||||
"configurePreset": "gcc-17-debug"
|
||||
},
|
||||
{
|
||||
"name": "gcc-17-release",
|
||||
"configurePreset": "gcc-17-release"
|
||||
},
|
||||
{
|
||||
"name": "gcc-20-debug",
|
||||
"configurePreset": "gcc-20-debug"
|
||||
},
|
||||
{
|
||||
"name": "gcc-20-release",
|
||||
"configurePreset": "gcc-20-release"
|
||||
},
|
||||
{
|
||||
"name": "gcc-23-debug",
|
||||
"configurePreset": "gcc-23-debug"
|
||||
},
|
||||
{
|
||||
"name": "gcc-23-release",
|
||||
"configurePreset": "gcc-23-release"
|
||||
},
|
||||
{
|
||||
"name": "clang-14-debug",
|
||||
"configurePreset": "clang-14-debug"
|
||||
},
|
||||
{
|
||||
"name": "clang-14-release",
|
||||
"configurePreset": "clang-14-release"
|
||||
},
|
||||
{
|
||||
"name": "clang-17-debug",
|
||||
"configurePreset": "clang-17-debug"
|
||||
},
|
||||
{
|
||||
"name": "clang-17-release",
|
||||
"configurePreset": "clang-17-release"
|
||||
},
|
||||
{
|
||||
"name": "clang-20-debug",
|
||||
"configurePreset": "clang-20-debug"
|
||||
},
|
||||
{
|
||||
"name": "clang-20-release",
|
||||
"configurePreset": "clang-20-release"
|
||||
},
|
||||
{
|
||||
"name": "clang-23-debug",
|
||||
"configurePreset": "clang-23-debug"
|
||||
},
|
||||
{
|
||||
"name": "clang-23-release",
|
||||
"configurePreset": "clang-23-release"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "x64-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"inheritEnvironments": [
|
||||
"msvc_x64_x64"
|
||||
],
|
||||
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
|
||||
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
|
||||
"cmakeCommandArgs": "-DGSL_CXX_STANDARD=17",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": "",
|
||||
"codeAnalysisRuleset": "CppCoreCheckRules.ruleset"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -41,7 +41,7 @@ span_p | &
|
||||
[cu32zstring](docs/headers.md#user-content-H-zstring) | ☑ | An alias to `basic_zstring` with dynamic extent and a char type of `const char32_t`
|
||||
[**2. Owners**][cg-owners] | |
|
||||
stack_array | ☐ | A stack-allocated array
|
||||
dyn_array | ☐ | A heap-allocated array
|
||||
dyn_array | ☑ | A heap-allocated array
|
||||
[**3. Assertions**][cg-assertions] | |
|
||||
[Expects](docs/headers.md#user-content-H-assert-expects) | ☑ | A precondition assertion; on failure it terminates
|
||||
[Ensures](docs/headers.md#user-content-H-assert-ensures) | ☑ | A postcondition assertion; on failure it terminates
|
||||
@@ -49,7 +49,7 @@ dyn_array | &
|
||||
move_owner | ☐ | A helper function that moves one `owner` to the other
|
||||
[final_action](docs/headers.md#user-content-H-util-final_action) | ☑ | A RAII style class that invokes a functor on its destruction
|
||||
[finally](docs/headers.md#user-content-H-util-finally) | ☑ | A helper function instantiating [final_action](docs/headers.md#user-content-H-util-final_action)
|
||||
[GSL_SUPPRESS](docs/headers.md#user-content-H-assert-gsl_suppress) | ☑ | A macro that takes an argument and turns it into `[[gsl::suppress(x)]]` or `[[gsl::suppress("x")]]`
|
||||
[GSL_SUPPRESS](docs/headers.md#user-content-H-assert-gsl_suppress) | ☑ | A macro that takes an argument and turns it into `[[gsl::suppress(x)]]` or `[[gsl::suppress("x")]]` depending on the compiler.
|
||||
[[implicit]] | ☐ | A "marker" to put on single-argument constructors to explicitly make them non-explicit
|
||||
[index](docs/headers.md#user-content-H-util-index) | ☑ | A type to use for all container and array indexing (currently an alias for `std::ptrdiff_t`)
|
||||
[narrow](docs/headers.md#user-content-H-narrow-narrow) | ☑ | A checked version of `narrow_cast`; it can throw [narrowing_error](docs/headers.md#user-content-H-narrow-narrowing_error)
|
||||
@@ -202,7 +202,7 @@ include(FetchContent)
|
||||
|
||||
FetchContent_Declare(GSL
|
||||
GIT_REPOSITORY "https://github.com/microsoft/GSL"
|
||||
GIT_TAG "v4.1.0"
|
||||
GIT_TAG "v4.2.0"
|
||||
GIT_SHALLOW ON
|
||||
)
|
||||
|
||||
|
||||
+216
-30
@@ -2,13 +2,14 @@ The Guidelines Support Library (GSL) interface is very lightweight and exposed v
|
||||
|
||||
Types and functions are exported in the namespace `gsl`.
|
||||
|
||||
See [GSL: Guidelines support library](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-gsl)
|
||||
See [GSL: Guidelines support library](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#s-gsl)
|
||||
|
||||
# <a name="H" />Headers
|
||||
|
||||
- [`<algorithms>`](#user-content-H-algorithms)
|
||||
- [`<assert>`](#user-content-H-assert)
|
||||
- [`<byte>`](#user-content-H-byte)
|
||||
- [`<dyn_array>`](#user-content-H-dyn_array)
|
||||
- [`<gsl>`](#user-content-H-gsl)
|
||||
- [`<narrow>`](#user-content-H-narrow)
|
||||
- [`<pointers>`](#user-content-H-pointers)
|
||||
@@ -38,7 +39,7 @@ that the destination `span` is at least as large as the source `span`.
|
||||
|
||||
This header contains some macros used for contract checking and suppressing code analysis warnings.
|
||||
|
||||
See [GSL.assert: Assertions](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#SS-assertions)
|
||||
See [GSL.assert: Assertions](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-assertions)
|
||||
|
||||
- [`GSL_SUPPRESS`](#user-content-H-assert-gsl_suppress)
|
||||
- [`Expects`](#user-content-H-assert-expects)
|
||||
@@ -49,9 +50,9 @@ See [GSL.assert: Assertions](https://isocpp.github.io/CppCoreGuidelines/CppCoreG
|
||||
This macro can be used to suppress a code analysis warning.
|
||||
|
||||
The core guidelines request tools that check for the rules to respect suppressing a rule by writing
|
||||
`[[gsl::suppress(tag)]]` or `[[gsl::suppress(tag, justification: "message")]]`.
|
||||
`[[gsl::suppress("tag")]]` or `[[gsl::suppress("tag", justification: "message")]]`.
|
||||
|
||||
Clang does not use exactly that syntax, but requires `tag` to be put in double quotes `[[gsl::suppress("tag")]]`.
|
||||
Older versions of MSVC (VS 2022 and earlier) only understand `[[gsl::suppress(tag)]]` without the double quotes around `tag`.
|
||||
|
||||
For portable code you can use `GSL_SUPPRESS(tag)`.
|
||||
|
||||
@@ -86,7 +87,7 @@ If `GSL_USE_STD_BYTE` is not defined, then the header file will check if `std::b
|
||||
If you do so, you might want to `#define GSL_USE_STD_BYTE 0` to a fixed value to be sure that both projects use exactly
|
||||
the same type. Otherwise you might get linker errors.
|
||||
|
||||
See [SL.str.5: Use `std::byte` to refer to byte values that do not necessarily represent characters](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rstr-byte)
|
||||
See [SL.str.5: Use `std::byte` to refer to byte values that do not necessarily represent characters](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rstr-byte)
|
||||
|
||||
### Non-member functions
|
||||
|
||||
@@ -155,6 +156,177 @@ constexpr byte to_byte() noexcept;
|
||||
|
||||
Convert the given value `I` to a `byte`. The template requires `I` to be in the valid range 0..255 for a `gsl::byte`.
|
||||
|
||||
## <a name="H-dyn_array" />`<dyn_array>`
|
||||
|
||||
This header contains an owning dynamically allocated array type whose size is fixed at construction.
|
||||
|
||||
- [`gsl::dyn_array`](#user-content-H-dyn_array-dyn_array)
|
||||
|
||||
### <a name="H-dyn_array-dyn_array" />`gsl::dyn_array`
|
||||
|
||||
```cpp
|
||||
template <typename T, typename Allocator = std::allocator<T>>
|
||||
class dyn_array;
|
||||
```
|
||||
|
||||
`gsl::dyn_array` owns a contiguous sequence of `T` objects allocated with `Allocator`.
|
||||
The number of elements is established when the object is constructed and remains unchanged.
|
||||
It provides bounds-checked element access and checked random-access iterators.
|
||||
|
||||
`gsl::dyn_array` is useful when the number of elements is known only at runtime, but the array should not grow or shrink through container operations.
|
||||
|
||||
#### Member Types
|
||||
|
||||
```cpp
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using iterator = details::dyn_array_iterator<T>;
|
||||
using const_iterator = details::dyn_array_iterator<const T>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using size_type = std::size_t;
|
||||
|
||||
using allocator_type = Allocator;
|
||||
```
|
||||
|
||||
#### Member functions
|
||||
|
||||
##### Construct/Copy
|
||||
|
||||
```cpp
|
||||
explicit constexpr dyn_array(const Allocator& alloc = {});
|
||||
```
|
||||
|
||||
Constructs an empty `dyn_array`.
|
||||
No elements are allocated and `data()` returns `nullptr`.
|
||||
|
||||
```cpp
|
||||
constexpr explicit dyn_array(size_type count, const Allocator& alloc = {});
|
||||
|
||||
constexpr dyn_array(size_type count, const T& value, const Allocator& alloc = {});
|
||||
```
|
||||
|
||||
Constructs a `dyn_array` with `count` elements using `alloc`.
|
||||
The first overload default-constructs each element.
|
||||
The second overload constructs each element as a copy of `value`.
|
||||
|
||||
```cpp
|
||||
template <typename InputIt>
|
||||
constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {});
|
||||
```
|
||||
|
||||
Constructs a `dyn_array` by copying the elements in the range `[first, last)`.
|
||||
|
||||
```cpp
|
||||
template <std::ranges::input_range InputRg>
|
||||
constexpr dyn_array(std::from_range_t, InputRg&& rg, const Allocator& alloc = {});
|
||||
```
|
||||
|
||||
Constructs a `dyn_array` by copying the elements in `rg`.
|
||||
This overload is available when container ranges are supported.
|
||||
|
||||
```cpp
|
||||
constexpr dyn_array(const dyn_array& other, const Allocator& alloc = {});
|
||||
|
||||
constexpr dyn_array(std::initializer_list<T> init, const Allocator& alloc = {});
|
||||
```
|
||||
|
||||
Constructs a `dyn_array` by copying the elements from another `dyn_array` or from an initializer list.
|
||||
|
||||
```cpp
|
||||
constexpr auto operator=(const dyn_array& other) -> dyn_array&;
|
||||
|
||||
constexpr dyn_array(dyn_array&&) = delete;
|
||||
dyn_array& operator=(dyn_array&&) = delete;
|
||||
```
|
||||
|
||||
Copy assignment, move assignment, and move construction are explicitly deleted.
|
||||
|
||||
##### Observers
|
||||
|
||||
```cpp
|
||||
constexpr auto size() const;
|
||||
constexpr auto empty() const;
|
||||
constexpr auto max_size() const;
|
||||
constexpr auto get_allocator() -> Allocator&;
|
||||
```
|
||||
|
||||
Returns the number of elements, whether the array is empty, the maximum representable size, or the allocator used by the `dyn_array`.
|
||||
|
||||
##### Element access
|
||||
|
||||
```cpp
|
||||
constexpr auto operator[](size_type pos) -> reference;
|
||||
constexpr auto operator[](size_type pos) const -> const_reference;
|
||||
```
|
||||
|
||||
Returns a reference to the element at the given index.
|
||||
[`Expects`](#user-content-H-assert-expects) that `pos` is less than the `dyn_array`'s size.
|
||||
|
||||
```cpp
|
||||
constexpr auto data();
|
||||
constexpr auto data() const -> const T*;
|
||||
```
|
||||
|
||||
Returns a pointer to the beginning of the contained data.
|
||||
If the `dyn_array` is empty, this returns `nullptr`.
|
||||
|
||||
##### Iterators
|
||||
|
||||
```cpp
|
||||
constexpr auto begin();
|
||||
constexpr auto begin() const;
|
||||
constexpr auto cbegin() const;
|
||||
|
||||
constexpr auto end();
|
||||
constexpr auto end() const;
|
||||
constexpr auto cend() const;
|
||||
```
|
||||
|
||||
Returns an iterator to the first element or to one past the last element.
|
||||
|
||||
```cpp
|
||||
constexpr auto rbegin();
|
||||
constexpr auto rbegin() const;
|
||||
constexpr auto crbegin() const;
|
||||
|
||||
constexpr auto rend();
|
||||
constexpr auto rend() const;
|
||||
constexpr auto crend() const;
|
||||
```
|
||||
|
||||
Returns a reverse iterator to the first element of the reversed range or to one past the last element of the reversed range.
|
||||
|
||||
The iterators are random-access iterators and perform bounds checking; they can never be invalidated.
|
||||
Dereferencing `end()`, moving before `begin()` or past `end()`, or comparing iterators from different arrays violates preconditions.
|
||||
|
||||
##### Comparisons
|
||||
|
||||
```cpp
|
||||
constexpr auto operator==(const dyn_array& other) const;
|
||||
constexpr auto operator!=(const dyn_array& other) const;
|
||||
```
|
||||
|
||||
Compares two `dyn_array`s by size and element value.
|
||||
|
||||
#### Deduction guides
|
||||
|
||||
```cpp
|
||||
template <class InputIt,
|
||||
class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>>
|
||||
dyn_array(InputIt, InputIt,
|
||||
Alloc = {}) -> dyn_array<typename std::iterator_traits<InputIt>::value_type, Alloc>;
|
||||
|
||||
template <std::ranges::input_range InputRg,
|
||||
class Alloc = std::allocator<std::ranges::range_value_t<InputRg>>>
|
||||
dyn_array(std::from_range_t, InputRg&&,
|
||||
Alloc = {}) -> dyn_array<std::ranges::range_value_t<InputRg>, Alloc>;
|
||||
```
|
||||
|
||||
The range deduction guide is available when container ranges are supported.
|
||||
|
||||
## <a name="H-gsl" />`<gsl>`
|
||||
|
||||
This header is a convenience header that includes all other [GSL headers](#user-content-H).
|
||||
@@ -164,7 +336,7 @@ Since `<narrow>` requires exceptions, it will only be included if exceptions are
|
||||
|
||||
This header contains utility functions and classes, for narrowing casts, which require exceptions. The narrowing-related utilities that don't require exceptions are found inside [util](#user-content-H-util).
|
||||
|
||||
See [GSL.util: Utilities](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#SS-utilities)
|
||||
See [GSL.util: Utilities](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-utilities)
|
||||
|
||||
- [`gsl::narrowing_error`](#user-content-H-narrow-narrowing_error)
|
||||
- [`gsl::narrow`](#user-content-H-narrow-narrow)
|
||||
@@ -180,13 +352,13 @@ If the argument `x` cannot be represented in the target type `T`, then the funct
|
||||
|
||||
Note: compare [`gsl::narrow_cast`](#user-content-H-util-narrow_cast) in header [util](#user-content-H-util).
|
||||
|
||||
See [ES.46: Avoid lossy (narrowing, truncating) arithmetic conversions](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-narrowing) and [ES.49: If you must use a cast, use a named cast](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-casts-named)
|
||||
See [ES.46: Avoid lossy (narrowing, truncating) arithmetic conversions](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-narrowing) and [ES.49: If you must use a cast, use a named cast](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-casts-named)
|
||||
|
||||
## <a name="H-pointers" />`<pointers>`
|
||||
|
||||
This header contains some pointer types.
|
||||
|
||||
See [GSL.view](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#SS-views)
|
||||
See [GSL.view](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-views)
|
||||
|
||||
- [`gsl::unique_ptr`](#user-content-H-pointers-unique_ptr)
|
||||
- [`gsl::shared_ptr`](#user-content-H-pointers-shared_ptr)
|
||||
@@ -198,13 +370,13 @@ See [GSL.view](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#SS-v
|
||||
|
||||
`gsl::unique_ptr` is an alias to `std::unique_ptr`.
|
||||
|
||||
See [GSL.owner: Ownership pointers](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#SS-ownership)
|
||||
See [GSL.owner: Ownership pointers](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-ownership)
|
||||
|
||||
### <a name="H-pointers-shared_ptr" />`gsl::shared_ptr`
|
||||
|
||||
`gsl::shared_ptr` is an alias to `std::shared_ptr`.
|
||||
|
||||
See [GSL.owner: Ownership pointers](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#SS-ownership)
|
||||
See [GSL.owner: Ownership pointers](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-ownership)
|
||||
|
||||
### <a name="H-pointers-owner" />`gsl::owner`
|
||||
|
||||
@@ -213,7 +385,7 @@ See [GSL.owner: Ownership pointers](https://isocpp.github.io/CppCoreGuidelines/C
|
||||
|
||||
A `gsl::owner<T>` is a typedef to `T`. It adds no runtime overhead whatsoever, as it is purely syntactic and does not add any runtime checks. Instead, it serves as an annotation for static analysis tools which check for memory safety, and as a code comprehension guide for human readers.
|
||||
|
||||
See Enforcement section of [C.31: All resources acquired by a class must be released by the class’s destructor](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-dtor-release).
|
||||
See Enforcement section of [C.31: All resources acquired by a class must be released by the class’s destructor](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rc-dtor-release).
|
||||
|
||||
### <a name="H-pointers-not_null" />`gsl::not_null`
|
||||
|
||||
@@ -222,7 +394,7 @@ See Enforcement section of [C.31: All resources acquired by a class must be rele
|
||||
The checks for ensuring that the pointer is not null are done in the constructor. There is no overhead when retrieving or dereferencing the checked pointer.
|
||||
When a nullptr check fails, `std::terminate` is called.
|
||||
|
||||
See [F.23: Use a `not_null<T>` to indicate that “null” is not a valid value](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-nullptr)
|
||||
See [F.23: Use a `not_null<T>` to indicate that “null” is not a valid value](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rf-nullptr)
|
||||
|
||||
#### Member Types
|
||||
|
||||
@@ -278,7 +450,7 @@ not_null& operator+=(std::ptrdiff_t) = delete;
|
||||
not_null& operator-=(std::ptrdiff_t) = delete;
|
||||
```
|
||||
|
||||
Explicitly deleted operators. Pointers point to single objects ([I.13: Do not pass an array as a single pointer](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Ri-array)), so don't allow these operators.
|
||||
Explicitly deleted operators. Pointers point to single objects ([I.13: Do not pass an array as a single pointer](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ri-array)), so don't allow these operators.
|
||||
|
||||
##### Observers
|
||||
|
||||
@@ -300,7 +472,7 @@ Dereference the underlying pointer.
|
||||
void operator[](std::ptrdiff_t) const = delete;
|
||||
```
|
||||
|
||||
Array index operator is explicitly deleted. Pointers point to single objects ([I.13: Do not pass an array as a single pointer](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Ri-array)), so don't allow treating them as an array.
|
||||
Array index operator is explicitly deleted. Pointers point to single objects ([I.13: Do not pass an array as a single pointer](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ri-array)), so don't allow treating them as an array.
|
||||
|
||||
```cpp
|
||||
void swap(not_null<T>& other) { std::swap(ptr_, other.ptr_); }
|
||||
@@ -375,7 +547,7 @@ template <class T>
|
||||
not_null<T> operator+(std::ptrdiff_t, const not_null<T>&) = delete;
|
||||
```
|
||||
|
||||
Addition and subtraction are explicitly deleted. Pointers point to single objects ([I.13: Do not pass an array as a single pointer](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Ri-array)), so don't allow these operators.
|
||||
Addition and subtraction are explicitly deleted. Pointers point to single objects ([I.13: Do not pass an array as a single pointer](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ri-array)), so don't allow these operators.
|
||||
|
||||
##### STL integration
|
||||
|
||||
@@ -412,22 +584,36 @@ The `gsl::span` is based on the standardized version of `std::span` which was ad
|
||||
deprecate `gsl::span` when `std::span` finished standardization, however that plan changed when the runtime bounds checking
|
||||
was removed from `std::span`'s design.
|
||||
|
||||
The only difference between `gsl::span` and `std::span` is that `gsl::span` strictly enforces runtime bounds checking.
|
||||
Any violations of the bounds check results in termination of the program.
|
||||
Like `gsl::span`, `gsl::span`'s iterators also differ from `std::span`'s iterator in that all access operations are bounds checked.
|
||||
The key differences between `gsl::span` and `std::span` are:
|
||||
- `gsl::span` strictly enforces runtime bounds checking for all access operations
|
||||
- Any violations of the bounds check results in termination of the program
|
||||
- `gsl::span`'s iterators also perform bounds checking, unlike `std::span`'s iterators
|
||||
|
||||
#### Which version of span should I use?
|
||||
|
||||
##### Use `gsl::span` if
|
||||
The following table compares the different span implementations to help you choose which one is best for your project:
|
||||
|
||||
- you want to guarantee bounds safety in your project.
|
||||
- All data accessing operations use bounds checking to ensure you are only accessing valid memory.
|
||||
- your project uses C++14 or C++17.
|
||||
- `std::span` is not available as it was not introduced into the STL until C++20.
|
||||
| Feature/Version | `std::span` (C++20/23) | Hardened `std::span` (C++26) | `gsl::span` |
|
||||
|-----------------|------------------------|------------------------------|-------------|
|
||||
| **C++ Standard** | Requires C++20 or later | Requires C++26 or backported implementation | Works with C++14 or later |
|
||||
| **Element Access** | No bounds checking | Bounds checking | Bounds checking |
|
||||
| **Iterator Safety** | No bounds checking | Implementation-defined, may depend on vendor | Full bounds checking |
|
||||
| **Error Behavior** | Undefined behavior on invalid access | Implementation-defined, may be configurable | Always calls [`std::terminate()`](https://en.cppreference.com/w/cpp/error/terminate) via [gsl::details::terminate()](https://github.com/microsoft/GSL/blob/main/include/gsl/assert#L111-L118) |
|
||||
| **Performance** | Fastest (no checking) | Varies by implementation and configuration | May have performance impact from bounds checking |
|
||||
|
||||
##### Use `std::span` if
|
||||
**Recommendations:**
|
||||
|
||||
- your project is C++20 and you need the performance offered by `std::span`.
|
||||
- **C++14 & C++17 projects**: Use `gsl::span` as `std::span` is not available.
|
||||
- **C++20 & C++23 projects**:
|
||||
- Use `gsl::span` if safety is your priority.
|
||||
- Use `std::span` if performance is critical and you're confident in your index calculations.
|
||||
- **C++26 projects**:
|
||||
- Use `gsl::span` if you need guaranteed iterator safety across all platforms.
|
||||
- Use hardened `std::span` if you want standard library compliance and acceptable safety.
|
||||
|
||||
**Implementation notes for hardened `std::span` in C++26:**
|
||||
- For MSVC: See [Microsoft STL Hardening](https://github.com/microsoft/STL/wiki/STL-Hardening)
|
||||
- For Clang/LLVM: See [libc++ Hardening](https://libcxx.llvm.org/Hardening.html)
|
||||
|
||||
#### Types
|
||||
|
||||
@@ -770,14 +956,14 @@ that a pointer points to a zero terminated C style string. This helps static cod
|
||||
`u32zstring` is a zero terminated `char32_t` string.
|
||||
`cu32zstring` is a const zero terminated `char32_t` string.
|
||||
|
||||
See [GSL.view](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#SS-views) and [SL.str.3: Use zstring or czstring to refer to a C-style, zero-terminated, sequence of characters](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rstr-zstring).
|
||||
See [GSL.view](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-views) and [SL.str.3: Use zstring or czstring to refer to a C-style, zero-terminated, sequence of characters](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rstr-zstring).
|
||||
|
||||
## <a name="H-util" />`<util>`
|
||||
|
||||
This header contains utility functions and classes. This header works without exceptions being available. The parts that require
|
||||
exceptions being available are in their own header file [narrow](#user-content-H-narrow).
|
||||
|
||||
See [GSL.util: Utilities](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#SS-utilities)
|
||||
See [GSL.util: Utilities](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-utilities)
|
||||
|
||||
- [`gsl::narrow_cast`](#user-content-H-util-narrow_cast)
|
||||
- [`gsl::final_action`](#user-content-H-util-final_action)
|
||||
@@ -793,7 +979,7 @@ An alias to `std::ptrdiff_t`. It serves as the index type for all container inde
|
||||
|
||||
Note: compare the throwing version [`gsl::narrow`](#user-content-H-narrow-narrow) in header [narrow](#user-content-H-narrow).
|
||||
|
||||
See [ES.46: Avoid lossy (narrowing, truncating) arithmetic conversions](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-narrowing) and [ES.49: If you must use a cast, use a named cast](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-casts-named)
|
||||
See [ES.46: Avoid lossy (narrowing, truncating) arithmetic conversions](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-narrowing) and [ES.49: If you must use a cast, use a named cast](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-casts-named)
|
||||
|
||||
### <a name="H-util-final_action" />`gsl::final_action`
|
||||
|
||||
@@ -804,7 +990,7 @@ class final_action { ... };
|
||||
|
||||
`final_action` allows you to ensure something gets run at the end of a scope.
|
||||
|
||||
See [E.19: Use a final_action object to express cleanup if no suitable resource handle is available](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Re-finally)
|
||||
See [E.19: Use a final_action object to express cleanup if no suitable resource handle is available](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#re-finally)
|
||||
|
||||
#### Member functions
|
||||
|
||||
@@ -844,7 +1030,7 @@ The function `gsl::at` offers a safe way to access data with index bounds checki
|
||||
|
||||
Note: `gsl::at` supports indexes up to `PTRDIFF_MAX`.
|
||||
|
||||
See [ES.42: Keep use of pointers simple and straightforward](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-ptr)
|
||||
See [ES.42: Keep use of pointers simple and straightforward](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-ptr)
|
||||
|
||||
```cpp
|
||||
template <class T, std::size_t N>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
> When bumping the version, you need to update the following files:
|
||||
|
||||
1. [ ] [CMakeLists.txt](../CMakeLists.txt) Bump `GSL_VERSION`
|
||||
1. [ ] [README.md](../README.md) Bump `GIT_TAG`
|
||||
1. [ ] [ios.yml](../.github/workflows/ios.yml) Bump `MACOSX_BUNDLE_BUNDLE_VERSION` and
|
||||
`MACOSX_BUNDLE_SHORT_VERSION_STRING`
|
||||
|
||||
> After updating, you need to create a new GitHub release:
|
||||
|
||||
1. [ ] [Microsoft/GSL - Create Release](https://github.com/microsoft/GSL/releases/new)
|
||||
|
||||
Be sure to update the release notes accordingly and properly mention open-source
|
||||
contributors.
|
||||
|
||||
> After a new release exists, update the `ms-gsl` vcpkg port:
|
||||
|
||||
1. [ ] [Microsoft/vcpkg - ms-gsl port](https://github.com/microsoft/vcpkg/tree/master/ports/ms-gsl)
|
||||
|
||||
Be sure to monitor the PR that updates the port for any feedback from vcpkg maintainers.
|
||||
@@ -48,9 +48,7 @@ void copy(span<SrcElementType, SrcExtent> src, span<DestElementType, DestExtent>
|
||||
"Source range is longer than target range");
|
||||
|
||||
Expects(dest.size() >= src.size());
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(stl.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(stl.1)
|
||||
std::copy_n(src.data(), src.size(), dest.data());
|
||||
}
|
||||
|
||||
|
||||
+6
-7
@@ -47,13 +47,14 @@
|
||||
//
|
||||
#if defined(__clang__)
|
||||
#define GSL_SUPPRESS(x) [[gsl::suppress(#x)]]
|
||||
#else
|
||||
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__NVCC__)
|
||||
#elif defined(_MSC_VER) && _MSC_VER >= 1950
|
||||
// Visual Studio versions after 2022 (_MSC_VER > 1944) support the justification message.
|
||||
#define GSL_SUPPRESS(x) [[gsl::suppress(#x)]]
|
||||
#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__NVCC__)
|
||||
#define GSL_SUPPRESS(x) [[gsl::suppress(x)]]
|
||||
#else
|
||||
#define GSL_SUPPRESS(x)
|
||||
#endif // _MSC_VER
|
||||
#endif // __clang__
|
||||
#endif // defined(__clang__)
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define GSL_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
@@ -92,9 +93,7 @@ namespace details
|
||||
|
||||
typedef void(__cdecl* terminate_handler)();
|
||||
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(f.6) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(f.6)
|
||||
[[noreturn]] inline void __cdecl default_terminate_handler()
|
||||
{
|
||||
__fastfail(RANGE_CHECKS_FAILURE);
|
||||
|
||||
+18
-16
@@ -82,11 +82,13 @@ namespace gsl
|
||||
{
|
||||
#if GSL_USE_STD_BYTE
|
||||
|
||||
namespace impl {
|
||||
// impl::byte is used by gsl::as_bytes so our own code does not trigger a deprecation warning as would be the case when we used gsl::byte.
|
||||
// Users of GSL should only use gsl::byte, not gsl::impl::byte.
|
||||
using byte = std::byte;
|
||||
}
|
||||
namespace impl
|
||||
{
|
||||
// impl::byte is used by gsl::as_bytes so our own code does not trigger a deprecation warning as
|
||||
// would be the case when we used gsl::byte. Users of GSL should only use gsl::byte, not
|
||||
// gsl::impl::byte.
|
||||
using byte = std::byte;
|
||||
} // namespace impl
|
||||
|
||||
using byte GSL_DEPRECATED("Use std::byte instead.") = std::byte;
|
||||
|
||||
@@ -100,11 +102,13 @@ enum class byte_may_alias byte : unsigned char
|
||||
{
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
// impl::byte is used by gsl::as_bytes so our own code does not trigger a deprecation warning as would be the case when we used gsl::byte.
|
||||
// Users of GSL should only use gsl::byte, not gsl::impl::byte.
|
||||
using byte = gsl::byte;
|
||||
}
|
||||
namespace impl
|
||||
{
|
||||
// impl::byte is used by gsl::as_bytes so our own code does not trigger a deprecation warning as
|
||||
// would be the case when we used gsl::byte. Users of GSL should only use gsl::byte, not
|
||||
// gsl::impl::byte.
|
||||
using byte = gsl::byte;
|
||||
} // namespace impl
|
||||
|
||||
template <class IntegerType, std::enable_if_t<std::is_integral<IntegerType>::value, bool> = true>
|
||||
constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept
|
||||
@@ -170,15 +174,13 @@ constexpr IntegerType to_integer(byte b) noexcept
|
||||
|
||||
#endif // GSL_USE_STD_BYTE
|
||||
|
||||
|
||||
template <typename T>
|
||||
// NOTE: need suppression since c++14 does not allow "return {t}"
|
||||
// GSL_SUPPRESS(type.4) // NO-FORMAT: attribute // TODO: suppression does not work
|
||||
constexpr gsl::impl::byte to_byte(T t) noexcept
|
||||
{
|
||||
static_assert(std::is_same<T, unsigned char>::value,
|
||||
"gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. "
|
||||
"If you are calling to_byte with an integer constant use: gsl::to_byte<t>() version.");
|
||||
static_assert(
|
||||
std::is_same<T, unsigned char>::value,
|
||||
"gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. "
|
||||
"If you are calling to_byte with an integer constant use: gsl::to_byte<t>() version.");
|
||||
return gsl::impl::byte(t);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,439 @@
|
||||
// -*- C++ -*-
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2026 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.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef GSL_DYN_ARRAY_H
|
||||
#define GSL_DYN_ARRAY_H
|
||||
|
||||
#include "./assert"
|
||||
#include "./narrow"
|
||||
#include "./util"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L)
|
||||
#include <ranges>
|
||||
#endif /* __cpp_lib_ranges >= 201911L */
|
||||
|
||||
namespace gsl
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
template <typename T, typename Allocator = std::allocator<T>>
|
||||
class dyn_array_base : public Allocator
|
||||
{
|
||||
using pointer = T*;
|
||||
using size_type = std::size_t;
|
||||
|
||||
template <typename... Args>
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void construct(pointer ptr, Args&&... args)
|
||||
{
|
||||
std::allocator_traits<Allocator>::construct(static_cast<Allocator&>(*this), ptr,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void destroy(pointer ptr)
|
||||
{
|
||||
std::allocator_traits<Allocator>::destroy(static_cast<Allocator&>(*this), ptr);
|
||||
}
|
||||
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void destroy_range(pointer first, pointer last)
|
||||
{
|
||||
for (; first != last; ++first) { destroy(first); }
|
||||
}
|
||||
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void rollback_construction(pointer first, pointer last)
|
||||
{
|
||||
destroy_range(first, last);
|
||||
std::allocator_traits<Allocator>::deallocate(static_cast<Allocator&>(*this), _data,
|
||||
_count);
|
||||
_data = nullptr;
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
constexpr auto data() const { return _data; }
|
||||
|
||||
constexpr auto count() const { return _count; }
|
||||
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void resize(size_type count)
|
||||
{
|
||||
// This should only be called when constructing a non-forward iterator.
|
||||
// It neither frees nor copies `_data`.
|
||||
Expects(_data == nullptr && _count == 0);
|
||||
if (count != 0)
|
||||
{
|
||||
_data = std::allocator_traits<Allocator>::allocate(static_cast<Allocator&>(*this),
|
||||
count);
|
||||
_count = count;
|
||||
}
|
||||
}
|
||||
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void fill(pointer first, size_type count, const T& value)
|
||||
{
|
||||
pointer current = first;
|
||||
try
|
||||
{
|
||||
for (size_type i = 0; i < count; ++i, ++current) { construct(current, value); }
|
||||
} catch (...)
|
||||
{
|
||||
rollback_construction(first, current);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputIt>
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void copy(InputIt first, InputIt last, pointer output)
|
||||
{
|
||||
pointer current = output;
|
||||
try
|
||||
{
|
||||
for (; first != last; ++first, ++current) { construct(current, *first); }
|
||||
} catch (...)
|
||||
{
|
||||
rollback_construction(output, current);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void default_construct(pointer first, size_type count)
|
||||
{
|
||||
pointer current = first;
|
||||
try
|
||||
{
|
||||
for (size_type i = 0; i < count; ++i, ++current) { construct(current); }
|
||||
} catch (...)
|
||||
{
|
||||
rollback_construction(first, current);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
pointer _data;
|
||||
size_type _count;
|
||||
|
||||
public:
|
||||
constexpr dyn_array_base(const Allocator& alloc)
|
||||
: Allocator{alloc}, _data{nullptr}, _count{0}
|
||||
{
|
||||
Ensures((_count == 0 && _data == nullptr) || (_count > 0 && _data != nullptr));
|
||||
}
|
||||
|
||||
constexpr dyn_array_base(size_type count, const Allocator& alloc)
|
||||
: Allocator{alloc}
|
||||
, _data{count == 0 ? nullptr
|
||||
: std::allocator_traits<Allocator>::allocate(
|
||||
static_cast<Allocator&>(*this), count)}
|
||||
, _count{count}
|
||||
{
|
||||
Ensures((_count == 0 && _data == nullptr) || (_count > 0 && _data != nullptr));
|
||||
}
|
||||
|
||||
GSL_CONSTEXPR_SINCE_CPP20 ~dyn_array_base()
|
||||
{
|
||||
if (_data)
|
||||
{
|
||||
if (!std::is_trivially_destructible<T>::value)
|
||||
{
|
||||
destroy_range(_data, _data + _count);
|
||||
}
|
||||
std::allocator_traits<Allocator>::deallocate(static_cast<Allocator&>(*this), _data,
|
||||
_count);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class dyn_array_iterator
|
||||
{
|
||||
using size_type = std::size_t;
|
||||
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L)
|
||||
constexpr dyn_array_iterator() = default;
|
||||
#endif /* __cpp_lib_ranges >= 201911L */
|
||||
|
||||
constexpr dyn_array_iterator(pointer ptr, size_type pos, size_type end_pos)
|
||||
: _ptr{ptr}, _pos{pos}, _end_pos{end_pos}
|
||||
{
|
||||
Ensures((_ptr != nullptr && _end_pos > 0) || (_ptr == nullptr && _end_pos == 0));
|
||||
Ensures(_pos <= _end_pos);
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER) && defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L)
|
||||
constexpr operator pointer() const { return _ptr + gsl::narrow<size_type>(_pos); }
|
||||
#endif /* defined(_MSC_VER) && __cpp_lib_ranges >= 201911L */
|
||||
|
||||
constexpr auto operator==(const dyn_array_iterator& other) const
|
||||
{
|
||||
Expects(_ptr == other._ptr);
|
||||
Expects(_end_pos == other._end_pos);
|
||||
return _pos == other._pos;
|
||||
}
|
||||
|
||||
constexpr auto operator!=(const dyn_array_iterator& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr auto operator*() const -> reference
|
||||
{
|
||||
Expects(_ptr != nullptr);
|
||||
Expects(_pos < _end_pos);
|
||||
return _ptr[_pos];
|
||||
}
|
||||
|
||||
constexpr auto operator++() -> dyn_array_iterator&
|
||||
{
|
||||
Expects(_pos < _end_pos);
|
||||
++_pos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr auto operator++(int)
|
||||
{
|
||||
auto rv = *this;
|
||||
++(*this);
|
||||
return rv;
|
||||
}
|
||||
|
||||
constexpr auto operator--() -> dyn_array_iterator&
|
||||
{
|
||||
Expects(_pos > 0);
|
||||
--_pos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr auto operator--(int)
|
||||
{
|
||||
auto rv = *this;
|
||||
--(*this);
|
||||
return rv;
|
||||
}
|
||||
|
||||
constexpr auto operator+=(difference_type diff) -> dyn_array_iterator&
|
||||
{
|
||||
auto new_pos = gsl::narrow<difference_type>(_pos) + diff;
|
||||
Expects(new_pos >= 0);
|
||||
Expects(new_pos <= gsl::narrow<difference_type>(_end_pos));
|
||||
_pos = gsl::narrow<size_type>(new_pos);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr auto operator-=(difference_type diff) -> dyn_array_iterator&
|
||||
{
|
||||
auto new_pos = gsl::narrow<difference_type>(_pos) - diff;
|
||||
Expects(new_pos >= 0);
|
||||
Expects(new_pos <= gsl::narrow<difference_type>(_end_pos));
|
||||
_pos = gsl::narrow<size_type>(new_pos);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr auto operator+(difference_type diff) const
|
||||
{
|
||||
auto new_pos = gsl::narrow<difference_type>(_pos) + diff;
|
||||
return dyn_array_iterator{_ptr, gsl::narrow<size_type>(new_pos), _end_pos};
|
||||
}
|
||||
|
||||
constexpr auto operator-(difference_type diff) const { return *this + (-diff); }
|
||||
|
||||
constexpr auto operator-(const dyn_array_iterator& other) const
|
||||
{
|
||||
Expects(_ptr == other._ptr);
|
||||
Expects(_end_pos == other._end_pos);
|
||||
return gsl::narrow<difference_type>(_pos) - gsl::narrow<difference_type>(other._pos);
|
||||
}
|
||||
|
||||
constexpr auto operator[](size_type pos) -> reference
|
||||
{
|
||||
Expects(_pos + pos < _end_pos);
|
||||
return _ptr[_pos + pos];
|
||||
}
|
||||
|
||||
constexpr auto operator[](size_type pos) const -> const_reference
|
||||
{
|
||||
return const_cast<dyn_array_iterator&>(*this).operator[](pos);
|
||||
}
|
||||
|
||||
private:
|
||||
pointer _ptr{};
|
||||
size_type _pos{};
|
||||
size_type _end_pos{};
|
||||
};
|
||||
} // namespace details
|
||||
|
||||
template <typename T, typename Allocator = std::allocator<T>>
|
||||
class dyn_array : private details::dyn_array_base<T, Allocator>
|
||||
{
|
||||
using base = details::dyn_array_base<T, Allocator>;
|
||||
using pointer = T*;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using iterator = details::dyn_array_iterator<T>;
|
||||
using const_iterator = details::dyn_array_iterator<const T>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using size_type = std::size_t;
|
||||
|
||||
using allocator_type = Allocator;
|
||||
|
||||
explicit constexpr dyn_array(const Allocator& alloc = {}) : base{alloc} {}
|
||||
|
||||
constexpr dyn_array(size_type count, const T& value, const Allocator& alloc = {})
|
||||
: base{count, alloc}
|
||||
{
|
||||
base::fill(data(), size(), value);
|
||||
}
|
||||
|
||||
template <typename InputIt,
|
||||
std::enable_if_t<details::is_fwd_iterator<InputIt>::value, bool> = true>
|
||||
constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {})
|
||||
: base{gsl::narrow<size_type>(std::distance(first, last)), alloc}
|
||||
{
|
||||
base::copy(first, last, data());
|
||||
}
|
||||
|
||||
template <typename InputIt, std::enable_if_t<!details::is_fwd_iterator<InputIt>::value &&
|
||||
details::is_iterator<InputIt>::value,
|
||||
bool> = true>
|
||||
constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {}) : dyn_array{alloc}
|
||||
{
|
||||
std::vector<T> tmp(first, last);
|
||||
base::resize(tmp.size());
|
||||
base::copy(std::begin(tmp), std::end(tmp), data());
|
||||
}
|
||||
|
||||
#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L)
|
||||
template <typename InputRg>
|
||||
requires(std::ranges::input_range<InputRg>)
|
||||
constexpr dyn_array(std::from_range_t, InputRg&& rg, const Allocator& alloc = {})
|
||||
: base{gsl::narrow<size_type>(std::size(rg)), alloc}
|
||||
{
|
||||
base::copy(std::ranges::begin(rg), std::ranges::end(rg), data());
|
||||
}
|
||||
#endif /* __cpp_lib_containers_ranges >= 202202L */
|
||||
|
||||
constexpr explicit dyn_array(size_type count, const Allocator& alloc = {}) : base{count, alloc}
|
||||
{
|
||||
base::default_construct(data(), size());
|
||||
}
|
||||
|
||||
constexpr dyn_array(const dyn_array& other, const Allocator& alloc = {})
|
||||
: dyn_array(other.begin(), other.end(), alloc)
|
||||
{}
|
||||
|
||||
constexpr dyn_array(std::initializer_list<T> init, const Allocator& alloc = {})
|
||||
: dyn_array(init.begin(), init.end(), alloc)
|
||||
{}
|
||||
|
||||
constexpr dyn_array(dyn_array&&) = delete;
|
||||
dyn_array& operator=(dyn_array&&) = delete;
|
||||
|
||||
constexpr auto operator==(const dyn_array& other) const
|
||||
{
|
||||
return size() == other.size() && std::equal(begin(), end(), other.begin(), other.end());
|
||||
}
|
||||
|
||||
constexpr auto operator!=(const dyn_array& other) const { return !(*this == other); }
|
||||
|
||||
constexpr auto size() const { return base::count(); }
|
||||
|
||||
constexpr auto empty() const { return size() == 0; }
|
||||
|
||||
constexpr auto max_size() const { return static_cast<size_type>(-1); }
|
||||
|
||||
constexpr auto get_allocator() -> Allocator& { return *this; }
|
||||
|
||||
constexpr auto operator[](size_type pos) -> reference
|
||||
{
|
||||
Expects(pos < size());
|
||||
return data()[pos];
|
||||
}
|
||||
|
||||
constexpr auto operator[](size_type pos) const -> const_reference
|
||||
{
|
||||
return const_cast<dyn_array&>(*this)[pos];
|
||||
}
|
||||
|
||||
constexpr auto data() { return base::data(); }
|
||||
constexpr auto data() const -> const T* { return const_cast<dyn_array&>(*this).data(); }
|
||||
|
||||
constexpr auto begin() { return iterator{data(), 0, size()}; }
|
||||
constexpr auto begin() const { return const_iterator{data(), 0, size()}; }
|
||||
constexpr auto cbegin() const { return begin(); }
|
||||
|
||||
constexpr auto rbegin() { return reverse_iterator{end()}; }
|
||||
constexpr auto rbegin() const { return const_reverse_iterator{end()}; }
|
||||
constexpr auto crbegin() const { return rbegin(); }
|
||||
|
||||
#ifdef _MSC_VER
|
||||
constexpr auto _Unchecked_begin() { return data(); }
|
||||
constexpr auto _Unchecked_begin() const -> const T*
|
||||
{
|
||||
return const_cast<dyn_array&>(*this)._Unchecked_begin();
|
||||
}
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
constexpr auto end() { return iterator{data(), size(), size()}; }
|
||||
constexpr auto end() const { return const_iterator{data(), size(), size()}; }
|
||||
constexpr auto cend() const { return end(); }
|
||||
|
||||
constexpr auto rend() { return reverse_iterator{begin()}; }
|
||||
constexpr auto rend() const { return const_reverse_iterator{begin()}; }
|
||||
constexpr auto crend() const { return rend(); }
|
||||
|
||||
#ifdef _MSC_VER
|
||||
constexpr auto _Unchecked_end() { return data() + size(); }
|
||||
constexpr auto _Unchecked_end() const -> const T*
|
||||
{
|
||||
return const_cast<dyn_array&>(*this)._Unchecked_end();
|
||||
}
|
||||
#endif /* _MSC_VER */
|
||||
};
|
||||
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
|
||||
template <class InputIt,
|
||||
class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>>
|
||||
dyn_array(InputIt, InputIt, Alloc = {})
|
||||
-> dyn_array<typename std::iterator_traits<InputIt>::value_type, Alloc>;
|
||||
|
||||
#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L)
|
||||
template <std::ranges::input_range InputRg,
|
||||
class Alloc = std::allocator<std::ranges::range_value_t<InputRg>>>
|
||||
dyn_array(std::from_range_t, InputRg&&, Alloc = {})
|
||||
-> dyn_array<std::ranges::range_value_t<InputRg>, Alloc>;
|
||||
#endif /* __cpp_lib_containers_ranges >= 202202L */
|
||||
|
||||
#endif /* __cpp_deduction_guides >= 201703L */
|
||||
} // namespace gsl
|
||||
|
||||
#endif /* defined(GSL_DYN_ARRAY_H) */
|
||||
+7
-7
@@ -18,13 +18,13 @@
|
||||
#define GSL_GSL_H
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "./algorithm" // copy
|
||||
#include "./assert" // Ensures/Expects
|
||||
#include "./byte" // byte
|
||||
#include "./pointers" // owner, not_null
|
||||
#include "./span" // span
|
||||
#include "./zstring" // zstring
|
||||
#include "./util" // finally()/narrow_cast()...
|
||||
#include "./algorithm" // copy
|
||||
#include "./assert" // Ensures/Expects
|
||||
#include "./byte" // byte
|
||||
#include "./pointers" // owner, not_null
|
||||
#include "./span" // span
|
||||
#include "./util" // finally()/narrow_cast()...
|
||||
#include "./zstring" // zstring
|
||||
|
||||
#ifdef __cpp_exceptions
|
||||
#include "./narrow" // narrow()
|
||||
|
||||
+20
-26
@@ -16,8 +16,8 @@
|
||||
|
||||
#ifndef GSL_NARROW_H
|
||||
#define GSL_NARROW_H
|
||||
#include "./assert" // for GSL_SUPPRESS
|
||||
#include "./util" // for narrow_cast
|
||||
#include "./assert" // for GSL_SUPPRESS
|
||||
#include "./util" // for narrow_cast
|
||||
#include <exception> // for std::exception
|
||||
namespace gsl
|
||||
{
|
||||
@@ -28,28 +28,28 @@ struct narrowing_error : public std::exception
|
||||
|
||||
// narrow() : a checked version of narrow_cast() that throws if the cast changed the value
|
||||
template <class T, class U, typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr>
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
|
||||
GSL_SUPPRESS(es.46) // NO-FORMAT: attribute // The warning suggests that a floating->unsigned conversion can occur
|
||||
// in the static_cast below, and that gsl::narrow should be used instead.
|
||||
// Suppress this warning, since gsl::narrow is defined in terms of
|
||||
// static_cast
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(type.1)
|
||||
GSL_SUPPRESS(es.46) // The warning suggests that a floating->unsigned conversion can occur
|
||||
// in the static_cast below, and that gsl::narrow should be used instead.
|
||||
// Suppress this warning, since gsl::narrow is defined in terms of
|
||||
// static_cast
|
||||
constexpr T narrow(U u)
|
||||
{
|
||||
constexpr const bool is_different_signedness =
|
||||
(std::is_signed<T>::value != std::is_signed<U>::value);
|
||||
|
||||
GSL_SUPPRESS(es.103) // NO-FORMAT: attribute // don't overflow
|
||||
GSL_SUPPRESS(es.104) // NO-FORMAT: attribute // don't underflow
|
||||
GSL_SUPPRESS(p.2) // NO-FORMAT: attribute // don't rely on undefined behavior
|
||||
const T t = narrow_cast<T>(u); // While this is technically undefined behavior in some cases (i.e., if the source value is of floating-point type
|
||||
// and cannot fit into the destination integral type), the resultant behavior is benign on the platforms
|
||||
// that we target (i.e., no hardware trap representations are hit).
|
||||
GSL_SUPPRESS(es.103) // don't overflow
|
||||
GSL_SUPPRESS(es.104) // don't underflow
|
||||
GSL_SUPPRESS(p.2) // don't rely on undefined behavior
|
||||
const T t = narrow_cast<T>(
|
||||
u); // While this is technically undefined behavior in some cases (i.e., if the source value
|
||||
// is of floating-point type and cannot fit into the destination integral type), the
|
||||
// resultant behavior is benign on the platforms that we target (i.e., no hardware trap
|
||||
// representations are hit).
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
#endif
|
||||
// Note: NaN will always throw, since NaN != NaN
|
||||
if (static_cast<U>(t) != u || (is_different_signedness && ((t < T{}) != (u < U{}))))
|
||||
@@ -57,24 +57,18 @@ GSL_SUPPRESS(p.2) // NO-FORMAT: attribute // don't rely on undefined behavior
|
||||
throw narrowing_error{};
|
||||
}
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
template <class T, class U, typename std::enable_if<!std::is_arithmetic<T>::value>::type* = nullptr>
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
constexpr T narrow(U u)
|
||||
GSL_SUPPRESS(type.1) constexpr T narrow(U u)
|
||||
{
|
||||
const T t = narrow_cast<T>(u);
|
||||
|
||||
if (static_cast<U>(t) != u)
|
||||
{
|
||||
throw narrowing_error{};
|
||||
}
|
||||
if (static_cast<U>(t) != u) { throw narrowing_error{}; }
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
+68
-47
@@ -1,3 +1,4 @@
|
||||
// -*- C++ -*-
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
||||
@@ -18,13 +19,13 @@
|
||||
#define GSL_POINTERS_H
|
||||
|
||||
#include "./assert" // for Ensures, Expects
|
||||
#include "./util" // for GSL_DEPRECATED
|
||||
#include "./util" // for GSL_DEPRECATED
|
||||
|
||||
#include <cstddef> // for ptrdiff_t, nullptr_t, size_t
|
||||
#include <functional> // for less, greater
|
||||
#include <memory> // for shared_ptr, unique_ptr, hash
|
||||
#include <type_traits> // for enable_if_t, is_convertible, is_assignable
|
||||
#include <utility> // for declval, forward
|
||||
#include <cstddef> // for ptrdiff_t, nullptr_t, size_t
|
||||
#include <functional> // for less, greater
|
||||
#include <memory> // for shared_ptr, unique_ptr, hash
|
||||
#include <type_traits> // for enable_if_t, is_convertible, is_assignable
|
||||
#include <utility> // for declval, forward
|
||||
|
||||
#if !defined(GSL_NO_IOSTREAMS)
|
||||
#include <iosfwd> // for ostream
|
||||
@@ -48,15 +49,16 @@ namespace details
|
||||
{
|
||||
};
|
||||
|
||||
// Resolves to the more efficient of `const T` or `const T&`, in the context of returning a const-qualified value
|
||||
// of type T.
|
||||
// Resolves to the more efficient of `const T` or `const T&`, in the context of returning a
|
||||
// const-qualified value of type T.
|
||||
//
|
||||
// Copied from cppfront's implementation of the CppCoreGuidelines F.16 (https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-in)
|
||||
template<typename T>
|
||||
using value_or_reference_return_t = std::conditional_t<
|
||||
sizeof(T) < 2*sizeof(void*) && std::is_trivially_copy_constructible<T>::value,
|
||||
const T,
|
||||
const T&>;
|
||||
// Copied from cppfront's implementation of the CppCoreGuidelines F.16
|
||||
// (https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rf-in)
|
||||
template <typename T>
|
||||
using value_or_reference_return_t =
|
||||
std::conditional_t<sizeof(T) <= 2 * sizeof(void*) &&
|
||||
std::is_trivially_copy_constructible<T>::value,
|
||||
const T, const T&>;
|
||||
|
||||
} // namespace details
|
||||
|
||||
@@ -72,9 +74,10 @@ using unique_ptr GSL_DEPRECATED("Use std::unique_ptr instead") = std::unique_ptr
|
||||
//
|
||||
// owner
|
||||
//
|
||||
// `gsl::owner<T>` is designed as a safety mechanism for code that must deal directly with raw pointers that own memory.
|
||||
// Ideally such code should be restricted to the implementation of low-level abstractions. `gsl::owner` can also be used
|
||||
// as a stepping point in converting legacy code to use more modern RAII constructs, such as smart pointers.
|
||||
// `gsl::owner<T>` is designed as a safety mechanism for code that must deal directly with raw
|
||||
// pointers that own memory. Ideally such code should be restricted to the implementation of
|
||||
// low-level abstractions. `gsl::owner` can also be used as a stepping point in converting legacy
|
||||
// code to use more modern RAII constructs, such as smart pointers.
|
||||
//
|
||||
// T must be a pointer type
|
||||
// - disallow construction from any type other than pointer type
|
||||
@@ -105,19 +108,23 @@ public:
|
||||
using element_type = T;
|
||||
|
||||
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||
constexpr not_null(U&& u) noexcept(std::is_nothrow_move_constructible<T>::value) : ptr_(std::forward<U>(u))
|
||||
constexpr not_null(U&& u) noexcept(std::is_nothrow_move_constructible<T>::value)
|
||||
: ptr_(std::forward<U>(u))
|
||||
{
|
||||
Expects(ptr_ != nullptr);
|
||||
}
|
||||
|
||||
template <typename = std::enable_if_t<!std::is_same<std::nullptr_t, T>::value>>
|
||||
constexpr not_null(T u) noexcept(std::is_nothrow_move_constructible<T>::value) : ptr_(std::move(u))
|
||||
constexpr not_null(T u) noexcept(std::is_nothrow_move_constructible<T>::value)
|
||||
: ptr_(std::move(u))
|
||||
{
|
||||
Expects(ptr_ != nullptr);
|
||||
}
|
||||
|
||||
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||
constexpr not_null(const not_null<U>& other) noexcept(std::is_nothrow_move_constructible<T>::value) : not_null(other.get())
|
||||
constexpr not_null(const not_null<U>& other) noexcept(
|
||||
std::is_nothrow_move_constructible<T>::value)
|
||||
: not_null(other.get())
|
||||
{}
|
||||
|
||||
not_null(const not_null& other) = default;
|
||||
@@ -145,14 +152,16 @@ public:
|
||||
not_null& operator-=(std::ptrdiff_t) = delete;
|
||||
void operator[](std::ptrdiff_t) const = delete;
|
||||
|
||||
void swap(not_null<T>& other) { std::swap(ptr_, other.ptr_); }
|
||||
void swap(not_null<T>& other) noexcept { std::swap(ptr_, other.ptr_); }
|
||||
|
||||
private:
|
||||
T ptr_;
|
||||
};
|
||||
|
||||
template <typename T, std::enable_if_t<std::is_move_assignable<T>::value && std::is_move_constructible<T>::value, bool> = true>
|
||||
void swap(not_null<T>& a, not_null<T>& b)
|
||||
template <typename T, std::enable_if_t<std::is_move_assignable<T>::value &&
|
||||
std::is_move_constructible<T>::value,
|
||||
bool> = true>
|
||||
void swap(not_null<T>& a, not_null<T>& b) noexcept
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
@@ -173,48 +182,50 @@ std::ostream& operator<<(std::ostream& os, const not_null<T>& val)
|
||||
#endif // !defined(GSL_NO_IOSTREAMS)
|
||||
|
||||
template <class T, class U>
|
||||
auto operator==(const not_null<T>& lhs,
|
||||
const not_null<U>& rhs) noexcept(noexcept(lhs.get() == rhs.get()))
|
||||
constexpr auto operator==(const not_null<T>& lhs,
|
||||
const not_null<U>& rhs) noexcept(noexcept(lhs.get() == rhs.get()))
|
||||
-> decltype(lhs.get() == rhs.get())
|
||||
{
|
||||
return lhs.get() == rhs.get();
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto operator!=(const not_null<T>& lhs,
|
||||
const not_null<U>& rhs) noexcept(noexcept(lhs.get() != rhs.get()))
|
||||
constexpr auto operator!=(const not_null<T>& lhs,
|
||||
const not_null<U>& rhs) noexcept(noexcept(lhs.get() != rhs.get()))
|
||||
-> decltype(lhs.get() != rhs.get())
|
||||
{
|
||||
return lhs.get() != rhs.get();
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto operator<(const not_null<T>& lhs,
|
||||
const not_null<U>& rhs) noexcept(noexcept(std::less<>{}(lhs.get(), rhs.get())))
|
||||
-> decltype(std::less<>{}(lhs.get(), rhs.get()))
|
||||
constexpr auto operator<(const not_null<T>& lhs, const not_null<U>& rhs) noexcept(
|
||||
noexcept(std::less<>{}(lhs.get(), rhs.get()))) -> decltype(std::less<>{}(lhs.get(), rhs.get()))
|
||||
{
|
||||
return std::less<>{}(lhs.get(), rhs.get());
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto operator<=(const not_null<T>& lhs,
|
||||
const not_null<U>& rhs) noexcept(noexcept(std::less_equal<>{}(lhs.get(), rhs.get())))
|
||||
constexpr auto
|
||||
operator<=(const not_null<T>& lhs,
|
||||
const not_null<U>& rhs) noexcept(noexcept(std::less_equal<>{}(lhs.get(), rhs.get())))
|
||||
-> decltype(std::less_equal<>{}(lhs.get(), rhs.get()))
|
||||
{
|
||||
return std::less_equal<>{}(lhs.get(), rhs.get());
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto operator>(const not_null<T>& lhs,
|
||||
const not_null<U>& rhs) noexcept(noexcept(std::greater<>{}(lhs.get(), rhs.get())))
|
||||
constexpr auto
|
||||
operator>(const not_null<T>& lhs,
|
||||
const not_null<U>& rhs) noexcept(noexcept(std::greater<>{}(lhs.get(), rhs.get())))
|
||||
-> decltype(std::greater<>{}(lhs.get(), rhs.get()))
|
||||
{
|
||||
return std::greater<>{}(lhs.get(), rhs.get());
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto operator>=(const not_null<T>& lhs,
|
||||
const not_null<U>& rhs) noexcept(noexcept(std::greater_equal<>{}(lhs.get(), rhs.get())))
|
||||
constexpr auto
|
||||
operator>=(const not_null<T>& lhs,
|
||||
const not_null<U>& rhs) noexcept(noexcept(std::greater_equal<>{}(lhs.get(), rhs.get())))
|
||||
-> decltype(std::greater_equal<>{}(lhs.get(), rhs.get()))
|
||||
{
|
||||
return std::greater_equal<>{}(lhs.get(), rhs.get());
|
||||
@@ -230,11 +241,13 @@ not_null<T> operator+(const not_null<T>&, std::ptrdiff_t) = delete;
|
||||
template <class T>
|
||||
not_null<T> operator+(std::ptrdiff_t, const not_null<T>&) = delete;
|
||||
|
||||
|
||||
template <class T, class U = decltype(std::declval<const T&>().get()), bool = std::is_default_constructible<std::hash<U>>::value>
|
||||
// T is conceptually a pointer so we don't have to worry about it being a reference and violating
|
||||
// std::hash requirements
|
||||
template <class T, class U = typename T::element_type,
|
||||
bool = std::is_default_constructible<std::hash<U>>::value>
|
||||
struct not_null_hash
|
||||
{
|
||||
std::size_t operator()(const T& value) const { return std::hash<U>{}(value.get()); }
|
||||
std::size_t operator()(const T& value) const noexcept { return std::hash<U>{}(value.get()); }
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
@@ -281,24 +294,32 @@ class strict_not_null : public not_null<T>
|
||||
{
|
||||
public:
|
||||
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||
constexpr explicit strict_not_null(U&& u) noexcept(std::is_nothrow_move_constructible<T>::value) : not_null<T>(std::forward<U>(u))
|
||||
constexpr explicit strict_not_null(U&& u) noexcept(std::is_nothrow_move_constructible<T>::value)
|
||||
: not_null<T>(std::forward<U>(u))
|
||||
{}
|
||||
|
||||
template <typename = std::enable_if_t<!std::is_same<std::nullptr_t, T>::value>>
|
||||
constexpr explicit strict_not_null(T u) noexcept(std::is_nothrow_move_constructible<T>::value) : not_null<T>(std::move(u))
|
||||
constexpr explicit strict_not_null(T u) noexcept(std::is_nothrow_move_constructible<T>::value)
|
||||
: not_null<T>(std::move(u))
|
||||
{}
|
||||
|
||||
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||
constexpr strict_not_null(const not_null<U>& other) noexcept(std::is_nothrow_move_constructible<T>::value) : not_null<T>(other)
|
||||
constexpr strict_not_null(const not_null<U>& other) noexcept(
|
||||
std::is_nothrow_move_constructible<T>::value)
|
||||
: not_null<T>(other)
|
||||
{}
|
||||
|
||||
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||
constexpr strict_not_null(const strict_not_null<U>& other) noexcept(std::is_nothrow_move_constructible<T>::value) : not_null<T>(other)
|
||||
constexpr strict_not_null(const strict_not_null<U>& other) noexcept(
|
||||
std::is_nothrow_move_constructible<T>::value)
|
||||
: not_null<T>(other)
|
||||
{}
|
||||
|
||||
// To avoid invalidating the "not null" invariant, the contained pointer is actually copied
|
||||
// instead of moved. If it is a custom pointer, its constructor could in theory throw exceptions.
|
||||
strict_not_null(strict_not_null&& other) noexcept(std::is_nothrow_copy_constructible<T>::value) = default;
|
||||
// instead of moved. If it is a custom pointer, its constructor could in theory throw
|
||||
// exceptions.
|
||||
strict_not_null(strict_not_null&& other) noexcept(
|
||||
std::is_nothrow_copy_constructible<T>::value) = default;
|
||||
strict_not_null(const strict_not_null& other) = default;
|
||||
strict_not_null& operator=(const strict_not_null& other) = default;
|
||||
strict_not_null& operator=(const not_null<T>& other)
|
||||
@@ -337,7 +358,7 @@ auto make_strict_not_null(T&& t) noexcept
|
||||
return strict_not_null<std::remove_cv_t<std::remove_reference_t<T>>>{std::forward<T>(t)};
|
||||
}
|
||||
|
||||
#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L))
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
|
||||
// deduction guides to prevent the ctad-maybe-unsupported warning
|
||||
template <class T>
|
||||
@@ -345,7 +366,7 @@ not_null(T) -> not_null<T>;
|
||||
template <class T>
|
||||
strict_not_null(T) -> strict_not_null<T>;
|
||||
|
||||
#endif // ( defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) )
|
||||
#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
|
||||
} // namespace gsl
|
||||
|
||||
|
||||
+28
-48
@@ -1,3 +1,4 @@
|
||||
// -*- C++ -*-
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
||||
@@ -163,9 +164,7 @@ namespace details
|
||||
constexpr span_iterator& operator++() noexcept
|
||||
{
|
||||
Expects(current_ != end_);
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(bounds.1)
|
||||
++current_;
|
||||
return *this;
|
||||
}
|
||||
@@ -196,9 +195,7 @@ namespace details
|
||||
if (n != 0) Expects(begin_ && current_ && end_);
|
||||
if (n > 0) Expects(end_ - current_ >= n);
|
||||
if (n < 0) Expects(current_ - begin_ >= -n);
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(bounds.1)
|
||||
current_ += n;
|
||||
return *this;
|
||||
}
|
||||
@@ -221,7 +218,7 @@ namespace details
|
||||
if (n != 0) Expects(begin_ && current_ && end_);
|
||||
if (n > 0) Expects(current_ - begin_ >= n);
|
||||
if (n < 0) Expects(end_ - current_ >= -n);
|
||||
GSL_SUPPRESS(bounds .1)
|
||||
GSL_SUPPRESS(bounds.1)
|
||||
current_ -= n;
|
||||
return *this;
|
||||
}
|
||||
@@ -315,9 +312,7 @@ namespace details
|
||||
if (n < 0) Expects(current_ - begin_ >= -n);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(bounds.1)
|
||||
constexpr pointer _Unwrapped() const noexcept
|
||||
{ // after seeking *this to a high water mark, or using one of the
|
||||
// _Verify_xxx functions above, unwrap this span_iterator to a raw
|
||||
@@ -332,9 +327,7 @@ namespace details
|
||||
#else
|
||||
static constexpr bool _Unwrap_when_unverified = false;
|
||||
#endif
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(con.3) // NO-FORMAT: attribute // TODO: false positive
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(con.3) // TODO: false positive
|
||||
constexpr void _Seek_to(const pointer p) noexcept
|
||||
{ // adjust the position of *this to previously verified location p
|
||||
// after _Unwrapped
|
||||
@@ -349,7 +342,8 @@ namespace details
|
||||
template <typename Ptr>
|
||||
friend struct std::pointer_traits;
|
||||
};
|
||||
}} // namespace gsl::details
|
||||
} // namespace details
|
||||
} // namespace gsl
|
||||
|
||||
namespace std
|
||||
{
|
||||
@@ -364,7 +358,10 @@ struct pointer_traits<::gsl::details::span_iterator<Type>>
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
namespace gsl { namespace details {
|
||||
namespace gsl
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
template <std::size_t Ext>
|
||||
class extent_type
|
||||
{
|
||||
@@ -589,10 +586,7 @@ public:
|
||||
}
|
||||
|
||||
template <std::size_t Count>
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
constexpr span<element_type, Count> last() const noexcept
|
||||
GSL_SUPPRESS(bounds.1) constexpr span<element_type, Count> last() const noexcept
|
||||
{
|
||||
static_assert(Extent == dynamic_extent || Count <= Extent,
|
||||
"last() cannot extract more elements from a span than it contains.");
|
||||
@@ -601,10 +595,7 @@ public:
|
||||
}
|
||||
|
||||
template <std::size_t Offset, std::size_t Count = dynamic_extent>
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
constexpr auto subspan() const noexcept ->
|
||||
GSL_SUPPRESS(bounds.1) constexpr auto subspan() const noexcept ->
|
||||
typename details::calculate_subspan_type<ElementType, Extent, Offset, Count>::type
|
||||
{
|
||||
static_assert(Extent == dynamic_extent || (Extent >= Offset && (Count == dynamic_extent ||
|
||||
@@ -642,9 +633,7 @@ public:
|
||||
constexpr bool empty() const noexcept { return size() == 0; }
|
||||
|
||||
// [span.elem], span element access
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(bounds.1)
|
||||
constexpr reference operator[](size_type idx) const noexcept
|
||||
{
|
||||
Expects(idx < size());
|
||||
@@ -669,18 +658,14 @@ public:
|
||||
constexpr iterator begin() const noexcept
|
||||
{
|
||||
const auto data = storage_.data();
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(bounds.1)
|
||||
return {data, data + size(), data};
|
||||
}
|
||||
|
||||
constexpr iterator end() const noexcept
|
||||
{
|
||||
const auto data = storage_.data();
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(bounds.1)
|
||||
const auto endData = data + storage_.size();
|
||||
return {data, endData, endData};
|
||||
}
|
||||
@@ -693,9 +678,7 @@ public:
|
||||
constexpr pointer _Unchecked_begin() const noexcept { return data(); }
|
||||
constexpr pointer _Unchecked_end() const noexcept
|
||||
{
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(bounds.1)
|
||||
return data() + size();
|
||||
}
|
||||
#endif // _MSC_VER
|
||||
@@ -752,9 +735,7 @@ private:
|
||||
return tmp.subspan(offset, count);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(bounds.1)
|
||||
constexpr span<element_type, dynamic_extent>
|
||||
make_subspan(size_type offset, size_type count, subspan_selector<dynamic_extent>) const noexcept
|
||||
{
|
||||
@@ -767,7 +748,7 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L))
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
|
||||
// Deduction Guides
|
||||
template <class Type, std::size_t Extent>
|
||||
@@ -787,12 +768,14 @@ template <class Container,
|
||||
class Element = std::remove_pointer_t<decltype(std::declval<const Container&>().data())>>
|
||||
span(const Container&) -> span<Element>;
|
||||
|
||||
#endif // ( defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) )
|
||||
#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
|
||||
#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND)
|
||||
#if defined(__clang__) && defined(_MSC_VER) && defined(__cplusplus) && (__cplusplus < 201703L)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated" // Bug in clang-cl.exe which raises a C++17 -Wdeprecated warning about this static constexpr workaround in C++14 mode.
|
||||
#pragma clang diagnostic ignored \
|
||||
"-Wdeprecated" // Bug in clang-cl.exe which raises a C++17 -Wdeprecated warning about this
|
||||
// static constexpr workaround in C++14 mode.
|
||||
#endif // defined(__clang__) && defined(_MSC_VER) && defined(__cplusplus) && (__cplusplus < 201703L)
|
||||
template <class ElementType, std::size_t Extent>
|
||||
constexpr const typename span<ElementType, Extent>::size_type span<ElementType, Extent>::extent;
|
||||
@@ -827,11 +810,10 @@ template <class ElementType, std::size_t Extent>
|
||||
span<const gsl::impl::byte, details::calculate_byte_size<ElementType, Extent>::value>
|
||||
as_bytes(span<ElementType, Extent> s) noexcept
|
||||
{
|
||||
using type = span<const gsl::impl::byte, details::calculate_byte_size<ElementType, Extent>::value>;
|
||||
using type =
|
||||
span<const gsl::impl::byte, details::calculate_byte_size<ElementType, Extent>::value>;
|
||||
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(type.1)
|
||||
return type{reinterpret_cast<const gsl::impl::byte*>(s.data()), s.size_bytes()};
|
||||
}
|
||||
|
||||
@@ -842,9 +824,7 @@ as_writable_bytes(span<ElementType, Extent> s) noexcept
|
||||
{
|
||||
using type = span<gsl::impl::byte, details::calculate_byte_size<ElementType, Extent>::value>;
|
||||
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(type.1)
|
||||
return type{reinterpret_cast<gsl::impl::byte*>(s.data()), s.size_bytes()};
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ constexpr ElementType& at(span<ElementType, Extent> s, index i)
|
||||
template <class ElementType, std::size_t Extent>
|
||||
constexpr std::ptrdiff_t ssize(const span<ElementType, Extent>& s) noexcept
|
||||
{
|
||||
return static_cast<std::ptrdiff_t>(s.size());
|
||||
return gsl::narrow_cast<std::ptrdiff_t>(s.size());
|
||||
}
|
||||
|
||||
// [span.iter] Free functions for begin/end functions
|
||||
|
||||
+61
-29
@@ -1,3 +1,4 @@
|
||||
// -*- C++ -*-
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
||||
@@ -19,10 +20,10 @@
|
||||
|
||||
#include "./assert" // for Expects
|
||||
|
||||
#include <array>
|
||||
#include <cstddef> // for ptrdiff_t, size_t
|
||||
#include <limits> // for numeric_limits
|
||||
#include <initializer_list> // for initializer_list
|
||||
#include <iterator> // for iterator_traits
|
||||
#include <limits> // for numeric_limits
|
||||
#include <type_traits> // for is_signed, integral_constant
|
||||
#include <utility> // for exchange, forward
|
||||
|
||||
@@ -86,6 +87,13 @@
|
||||
#define GSL_DEPRECATED(msg)
|
||||
#endif // !defined(GSL_DEPRECATED)
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
#define GSL_CONSTEXPR_SINCE_CPP20 constexpr
|
||||
#else // ^^^ since C++20 /// before C++20 vvv
|
||||
#define GSL_BEFORE_CPP20
|
||||
#define GSL_CONSTEXPR_SINCE_CPP20
|
||||
#endif // __cplusplus >= 202002L
|
||||
|
||||
namespace gsl
|
||||
{
|
||||
//
|
||||
@@ -95,23 +103,55 @@ namespace gsl
|
||||
// index type for all container indexes/subscripts/sizes
|
||||
using index = std::ptrdiff_t;
|
||||
|
||||
namespace details
|
||||
{
|
||||
template <typename...>
|
||||
using void_t = void;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct is_iterator : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_iterator<T, void_t<typename std::iterator_traits<T>::value_type>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct is_fwd_iterator : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_fwd_iterator<T, void_t<typename std::iterator_traits<T>::iterator_category>>
|
||||
: std::integral_constant<
|
||||
bool, std::is_base_of<std::forward_iterator_tag,
|
||||
typename std::iterator_traits<T>::iterator_category>::value>
|
||||
{
|
||||
};
|
||||
} // namespace details
|
||||
|
||||
// final_action allows you to ensure something gets run at the end of a scope
|
||||
template <class F>
|
||||
class final_action
|
||||
{
|
||||
public:
|
||||
explicit final_action(const F& ff) noexcept : f{ff} { }
|
||||
explicit final_action(F&& ff) noexcept : f{std::move(ff)} { }
|
||||
explicit final_action(const F& ff) noexcept : f{ff} {}
|
||||
explicit final_action(F&& ff) noexcept : f{std::move(ff)} {}
|
||||
|
||||
~final_action() noexcept { if (invoke) f(); }
|
||||
~final_action() noexcept
|
||||
{
|
||||
if (invoke) f();
|
||||
}
|
||||
|
||||
final_action(final_action&& other) noexcept
|
||||
: f(std::move(other.f)), invoke(std::exchange(other.invoke, false))
|
||||
{ }
|
||||
{}
|
||||
|
||||
final_action(const final_action&) = delete;
|
||||
final_action(const final_action&) = delete;
|
||||
void operator=(const final_action&) = delete;
|
||||
void operator=(final_action&&) = delete;
|
||||
void operator=(final_action&&) = delete;
|
||||
|
||||
private:
|
||||
F f;
|
||||
@@ -127,10 +167,7 @@ GSL_NODISCARD auto finally(F&& f) noexcept
|
||||
|
||||
// narrow_cast(): a searchable way to do narrowing casts of values
|
||||
template <class T, class U>
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
constexpr T narrow_cast(U&& u) noexcept
|
||||
GSL_SUPPRESS(type.1) constexpr T narrow_cast(U&& u) noexcept
|
||||
{
|
||||
return static_cast<T>(std::forward<U>(u));
|
||||
}
|
||||
@@ -139,23 +176,17 @@ GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
|
||||
// at() - Bounds-checked way of accessing builtin arrays, std::array, std::vector
|
||||
//
|
||||
template <class T, std::size_t N>
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
|
||||
GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
constexpr T& at(T (&arr)[N], const index i)
|
||||
GSL_SUPPRESS(bounds.4) GSL_SUPPRESS(bounds.2) constexpr T& at(T (&arr)[N], const index i)
|
||||
{
|
||||
static_assert(N <= static_cast<std::size_t>((std::numeric_limits<std::ptrdiff_t>::max)()), "We only support arrays up to PTRDIFF_MAX bytes.");
|
||||
static_assert(N <= static_cast<std::size_t>((std::numeric_limits<std::ptrdiff_t>::max)()),
|
||||
"We only support arrays up to PTRDIFF_MAX bytes.");
|
||||
Expects(i >= 0 && i < narrow_cast<index>(N));
|
||||
return arr[narrow_cast<std::size_t>(i)];
|
||||
}
|
||||
|
||||
template <class Cont>
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
|
||||
GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
constexpr auto at(Cont& cont, const index i) -> decltype(cont[cont.size()])
|
||||
GSL_SUPPRESS(bounds.4) GSL_SUPPRESS(bounds.2) constexpr auto at(Cont& cont, const index i)
|
||||
-> decltype(cont[cont.size()])
|
||||
{
|
||||
Expects(i >= 0 && i < narrow_cast<index>(cont.size()));
|
||||
using size_type = decltype(cont.size());
|
||||
@@ -163,17 +194,18 @@ GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute
|
||||
}
|
||||
|
||||
template <class T>
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
constexpr T at(const std::initializer_list<T> cont, const index i)
|
||||
GSL_SUPPRESS(bounds.1) constexpr T at(const std::initializer_list<T> cont, const index i)
|
||||
{
|
||||
Expects(i >= 0 && i < narrow_cast<index>(cont.size()));
|
||||
return *(cont.begin() + i);
|
||||
}
|
||||
|
||||
template <class T, std::enable_if_t<std::is_move_assignable<T>::value && std::is_move_constructible<T>::value>>
|
||||
void swap(T& a, T& b) { std::swap(a, b); }
|
||||
template <class T, std::enable_if_t<std::is_move_assignable<T>::value &&
|
||||
std::is_move_constructible<T>::value>>
|
||||
void swap(T& a, T& b)
|
||||
{
|
||||
std::swap(a, b);
|
||||
}
|
||||
|
||||
#if defined(__cpp_lib_span) && __cpp_lib_span >= 202002L
|
||||
template <class T, std::size_t extent = std::dynamic_extent>
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "./span_ext" // for dynamic_extent
|
||||
|
||||
#include <cstddef> // for size_t, nullptr_t
|
||||
#include <cstddef> // for size_t, nullptr_t
|
||||
|
||||
namespace gsl
|
||||
{
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
"%VCINSTALLDIR%Tools\Llvm\bin\clang-format" -version
|
||||
if %errorlevel% neq 0 (
|
||||
echo [ERROR] clang-format not found, script should be called from a visual studio developer command prompt.
|
||||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
for %%f in (include\gsl\* tests\*.h tests\*.cpp) do (
|
||||
echo formatting %%f
|
||||
"%VCINSTALLDIR%Tools\Llvm\bin\clang-format" -i --assume-filename x.cpp "%%f"
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
cf=clang-format-$(grep -i 'CLANG_VERSION:' .github/workflows/clang-format.yml | sed -E 's/.*"([^"]+)".*/\1/')
|
||||
readonly cf
|
||||
|
||||
if ! "${cf}" -version; then
|
||||
echo "[ERROR] clang-format not found. Please install it using: sudo apt install ${cf}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
{
|
||||
find include/gsl -type f
|
||||
find tests -type f \( -name '*.cpp' -o -name '*.h' \)
|
||||
} | xargs "${cf}" -i --assume-filename=x.cpp --verbose
|
||||
|
||||
find scripts -type f -name '*.sh' -print -exec shfmt -w {} \;
|
||||
+14
-18
@@ -151,6 +151,7 @@ else()
|
||||
-Wno-unknown-attributes
|
||||
-Wno-used-but-marked-unused # GTest EXPECT_DEATH
|
||||
-Wno-weak-vtables
|
||||
-Wno-poison-system-directories
|
||||
$<$<EQUAL:${GSL_CXX_STANDARD},14>: # no support for [[maybe_unused]]
|
||||
-Wno-unused-member-function
|
||||
-Wno-unused-variable
|
||||
@@ -199,28 +200,22 @@ target_include_directories(gsl_tests_config SYSTEM INTERFACE
|
||||
googletest/googletest/include
|
||||
)
|
||||
|
||||
add_executable(gsl_tests
|
||||
algorithm_tests.cpp
|
||||
assertion_tests.cpp
|
||||
at_tests.cpp
|
||||
byte_tests.cpp
|
||||
notnull_tests.cpp
|
||||
owner_tests.cpp
|
||||
pointers_tests.cpp
|
||||
span_compatibility_tests.cpp
|
||||
span_ext_tests.cpp
|
||||
span_tests.cpp
|
||||
strict_notnull_tests.cpp
|
||||
|
||||
utils_tests.cpp
|
||||
)
|
||||
# Individually build and register each test source (except no_exception_ensure_tests.cpp)
|
||||
# no_exception_ensure_tests.cpp is built separately with exceptions disabled
|
||||
file(GLOB GSL_TEST_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
|
||||
list(FILTER GSL_TEST_SOURCES EXCLUDE REGEX "no_exception_ensure_tests\\.cpp$")
|
||||
|
||||
target_link_libraries(gsl_tests
|
||||
foreach(src IN LISTS GSL_TEST_SOURCES)
|
||||
get_filename_component(test_name "${src}" NAME_WE)
|
||||
add_executable(${test_name} ${src})
|
||||
target_link_libraries(${test_name}
|
||||
Microsoft.GSL::GSL
|
||||
gsl_tests_config
|
||||
${GTestMain_LIBRARIES}
|
||||
)
|
||||
add_test(gsl_tests gsl_tests)
|
||||
)
|
||||
add_test(NAME ${test_name} COMMAND ${test_name})
|
||||
set_target_properties(${test_name} PROPERTIES FOLDER "tests")
|
||||
endforeach()
|
||||
|
||||
# No exception tests
|
||||
|
||||
@@ -286,6 +281,7 @@ else()
|
||||
-Wno-missing-prototypes
|
||||
-Wno-unknown-attributes
|
||||
-Wno-weak-vtables
|
||||
-Wno-poison-system-directories
|
||||
>
|
||||
$<$<CXX_COMPILER_ID:GNU>:
|
||||
-Wdouble-promotion # float implicit to double
|
||||
|
||||
+2
-2
@@ -32,7 +32,7 @@
|
||||
TEST(at_tests, static_array)
|
||||
{
|
||||
int a[4] = {1, 2, 3, 4};
|
||||
const int(&c_a)[4] = a;
|
||||
const int (&c_a)[4] = a;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
@@ -152,7 +152,7 @@ TEST(at_tests, std_span)
|
||||
static constexpr bool test_constexpr()
|
||||
{
|
||||
int a1[4] = {1, 2, 3, 4};
|
||||
const int(&c_a1)[4] = a1;
|
||||
const int (&c_a1)[4] = a1;
|
||||
std::array<int, 4> a2 = {1, 2, 3, 4};
|
||||
const std::array<int, 4>& c_a2 = a2;
|
||||
|
||||
|
||||
@@ -171,8 +171,8 @@ static_assert(!RShiftAssignCompilesFor<float>, "!RShiftAssignCompilesFor<float>"
|
||||
template <typename U, typename = void>
|
||||
static constexpr bool ToIntegerCompilesFor = false;
|
||||
template <typename U>
|
||||
static constexpr bool
|
||||
ToIntegerCompilesFor<U, void_t<decltype(gsl::to_integer<U>(gsl::byte{}))>> = true;
|
||||
static constexpr bool ToIntegerCompilesFor<U, void_t<decltype(gsl::to_integer<U>(gsl::byte{}))>> =
|
||||
true;
|
||||
static_assert(!ToIntegerCompilesFor<float>, "!ToIntegerCompilesFor<float>");
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2025 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 <gsl/pointers> // for not_null
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <type_traits> // for declval
|
||||
|
||||
using namespace gsl;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr bool comparison_test(const int* ptr1, const int* ptr2)
|
||||
{
|
||||
const not_null<const int*> p1(ptr1);
|
||||
const not_null<const int*> p1_same(ptr1);
|
||||
const not_null<const int*> p2(ptr2);
|
||||
|
||||
// Testing operator==
|
||||
const bool eq_result = (p1 == p1_same); // Should be true
|
||||
const bool neq_result = (p1 != p2); // Should be true
|
||||
|
||||
// Testing operator<= and operator>=
|
||||
const bool le_result = (p1 <= p1_same); // Should be true
|
||||
const bool ge_result = (p1 >= p1_same); // Should be true
|
||||
|
||||
// The exact comparison results will depend on pointer ordering,
|
||||
// but we can verify that the basic equality checks work as expected
|
||||
return eq_result && neq_result && le_result && ge_result;
|
||||
}
|
||||
|
||||
constexpr bool workaround_test(const int* ptr1, const int* ptr2)
|
||||
{
|
||||
const not_null<const int*> p1(ptr1);
|
||||
const not_null<const int*> p1_same(ptr1);
|
||||
const not_null<const int*> p2(ptr2);
|
||||
|
||||
// Using .get() to compare
|
||||
const bool eq_result = (p1.get() == p1_same.get()); // Should be true
|
||||
const bool neq_result = (p1.get() != p2.get()); // Should be true
|
||||
|
||||
return eq_result && neq_result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
constexpr int test_value1 = 1;
|
||||
constexpr int test_value2 = 2;
|
||||
|
||||
static_assert(comparison_test(&test_value1, &test_value2),
|
||||
"not_null comparison operators should be constexpr");
|
||||
static_assert(workaround_test(&test_value1, &test_value2),
|
||||
"not_null .get() comparison workaround should work");
|
||||
|
||||
TEST(notnull_constexpr_tests, TestNotNullConstexprComparison)
|
||||
{
|
||||
// This test simply verifies that the constexpr functions compile and run
|
||||
// If we got here, it means the constexpr comparison operators are working
|
||||
static const int value1 = 1;
|
||||
static const int value2 = 2;
|
||||
EXPECT_TRUE(comparison_test(&value1, &value2));
|
||||
EXPECT_TRUE(workaround_test(&value1, &value2));
|
||||
}
|
||||
@@ -0,0 +1,684 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "deathTestCommon.h"
|
||||
#include "gsl/dyn_array"
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <gsl/dyn_array>
|
||||
#include <gsl/util>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
// Despite using <algorithm> and <ranges> utilities in this test, they
|
||||
// are not being included directly by this file as a test to ensure
|
||||
// transitive inclusion via <gsl/dyn_array>.
|
||||
|
||||
static_assert(sizeof(gsl::dyn_array<int>) == 2 * sizeof(void*),
|
||||
"gsl::dyn_array (with the default allocator) should be 16 bytes");
|
||||
|
||||
#if defined(__cpp_lib_concepts) && (__cpp_lib_concepts >= 202002L)
|
||||
static_assert(std::input_iterator<gsl::dyn_array<int>::iterator>,
|
||||
"gsl::dyn_array should expose a valid input_iterator");
|
||||
#endif /* __cpp_lib_concepts >= 202002L */
|
||||
|
||||
#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L)
|
||||
static_assert(std::ranges::input_range<gsl::dyn_array<int>>,
|
||||
"gsl::dyn_array should be a valid input range");
|
||||
#endif /* __cpp_lib_ranges >= 201911L */
|
||||
|
||||
TEST(dyn_array_tests, default_ctor)
|
||||
{
|
||||
gsl::dyn_array<char> diamondbacks;
|
||||
EXPECT_TRUE(diamondbacks.empty());
|
||||
EXPECT_EQ(diamondbacks.size(), 0);
|
||||
EXPECT_EQ(diamondbacks.data(), nullptr);
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, count_ctor)
|
||||
{
|
||||
gsl::dyn_array<char> athletics(10);
|
||||
EXPECT_FALSE(athletics.empty());
|
||||
EXPECT_EQ(athletics.size(), 10);
|
||||
EXPECT_NE(athletics.data(), nullptr);
|
||||
|
||||
gsl::dyn_array<char> braves(0);
|
||||
EXPECT_TRUE(braves.empty());
|
||||
EXPECT_EQ(braves.size(), 0);
|
||||
EXPECT_EQ(braves.data(), nullptr);
|
||||
EXPECT_TRUE(std::all_of(braves.begin(), braves.end(), [](char c) { return c == char{}; }));
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, count_value_ctor)
|
||||
{
|
||||
gsl::dyn_array<char> orioles(10, 'c');
|
||||
EXPECT_FALSE(orioles.empty());
|
||||
EXPECT_EQ(orioles.size(), 10);
|
||||
EXPECT_NE(orioles.data(), nullptr);
|
||||
EXPECT_TRUE(std::all_of(orioles.begin(), orioles.end(), [](char c) { return c == 'c'; }));
|
||||
|
||||
gsl::dyn_array<int> redsox(10, 42);
|
||||
EXPECT_FALSE(redsox.empty());
|
||||
EXPECT_EQ(redsox.size(), 10);
|
||||
EXPECT_NE(redsox.data(), nullptr);
|
||||
EXPECT_TRUE(std::all_of(redsox.begin(), redsox.end(), [](int i) { return i == 42; }));
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, inputit_ctor)
|
||||
{
|
||||
std::vector<char> cubs(10, 'c');
|
||||
gsl::dyn_array<char> whitesox(cubs.begin(), cubs.end());
|
||||
EXPECT_FALSE(whitesox.empty());
|
||||
EXPECT_EQ(whitesox.size(), cubs.size());
|
||||
EXPECT_NE(whitesox.data(), nullptr);
|
||||
EXPECT_TRUE(std::all_of(whitesox.begin(), whitesox.end(), [](char c) { return c == 'c'; }));
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, copy_ctor)
|
||||
{
|
||||
gsl::dyn_array<char> reds(10, 'c');
|
||||
gsl::dyn_array<char> guardians(reds);
|
||||
EXPECT_FALSE(guardians.empty());
|
||||
EXPECT_EQ(guardians.size(), reds.size());
|
||||
EXPECT_NE(guardians.data(), nullptr);
|
||||
EXPECT_TRUE(std::all_of(guardians.begin(), guardians.end(), [](char c) { return c == 'c'; }));
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, access_operator)
|
||||
{
|
||||
gsl::dyn_array<char> rockies(10, 'c');
|
||||
using ST = typename decltype(rockies)::size_type;
|
||||
for (int i = 0; i < gsl::narrow<int>(rockies.size()); i++)
|
||||
EXPECT_EQ(rockies[gsl::narrow<ST>(i)], 'c');
|
||||
for (int i = 0; i < gsl::narrow<int>(rockies.size()); i++) rockies[gsl::narrow<ST>(i)] = 'r';
|
||||
for (int i = 0; i < gsl::narrow<int>(rockies.size()); i++)
|
||||
EXPECT_EQ(rockies[gsl::narrow<ST>(i)], 'r');
|
||||
gsl::dyn_array<int> tigers(10);
|
||||
for (int i = 0; i < gsl::narrow<int>(tigers.size()); i++) tigers[gsl::narrow<ST>(i)] = i;
|
||||
for (int i = 0; i < gsl::narrow<int>(tigers.size()); i++)
|
||||
EXPECT_EQ(tigers[gsl::narrow<ST>(i)], i);
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, iterators)
|
||||
{
|
||||
gsl::dyn_array<char> astros(10, 'c');
|
||||
for (auto it = astros.begin(); it != astros.end(); it++) EXPECT_EQ(*it, 'c');
|
||||
for (auto it = astros.begin(); it != astros.end(); it++) *it = 'r';
|
||||
for (auto it = astros.begin(); it != astros.end(); it++) EXPECT_EQ(*it, 'r');
|
||||
EXPECT_TRUE(std::all_of(astros.begin(), astros.end(), [](char c) { return c == 'r'; }));
|
||||
|
||||
gsl::dyn_array<char> royals(10, 'c');
|
||||
for (auto it = royals.begin(); it != royals.end(); ++it) EXPECT_EQ(*it, 'c');
|
||||
for (auto it = royals.begin(); it != royals.end(); ++it) *it = 'r';
|
||||
for (auto it = royals.begin(); it != royals.end(); ++it) EXPECT_EQ(*it, 'r');
|
||||
EXPECT_TRUE(std::all_of(royals.begin(), royals.end(), [](char c) { return c == 'r'; }));
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, range_for)
|
||||
{
|
||||
gsl::dyn_array<char> angels(10, 'c');
|
||||
for (auto x : angels) EXPECT_EQ(x, 'c');
|
||||
for (auto& x : angels) x = 'r';
|
||||
for (auto x : angels) EXPECT_EQ(x, 'r');
|
||||
EXPECT_TRUE(std::all_of(angels.begin(), angels.end(), [](char c) { return c == 'r'; }));
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, use_std_algorithms)
|
||||
{
|
||||
gsl::dyn_array<char> dodgers(26);
|
||||
std::generate(dodgers.begin(), dodgers.end(), [i = 0]() mutable { return 'a' + i++; });
|
||||
char ch = 'a';
|
||||
for (auto x : dodgers) EXPECT_EQ(x, ch++);
|
||||
EXPECT_EQ(std::find(dodgers.begin(), dodgers.end(), 'a'), dodgers.begin());
|
||||
{
|
||||
auto it = std::find(dodgers.begin(), dodgers.end(), 'c');
|
||||
EXPECT_EQ(std::distance(dodgers.begin(), it), 'c' - 'a');
|
||||
EXPECT_EQ(std::distance(it, dodgers.begin()), 'a' - 'c');
|
||||
}
|
||||
{
|
||||
auto it = std::lower_bound(dodgers.begin(), dodgers.end(), 'j');
|
||||
EXPECT_EQ(*it, 'j');
|
||||
EXPECT_EQ(std::distance(dodgers.begin(), it), 'j' - 'a');
|
||||
EXPECT_EQ(std::distance(it, dodgers.begin()), 'a' - 'j');
|
||||
}
|
||||
EXPECT_EQ(dodgers.begin(), std::begin(dodgers));
|
||||
EXPECT_EQ(dodgers.end(), std::end(dodgers));
|
||||
}
|
||||
|
||||
#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L)
|
||||
constexpr auto default_constructed_count_dyn_array_is_constexpr()
|
||||
{
|
||||
gsl::dyn_array<int> values(3);
|
||||
return values.size() == 3 && values[0] == 0 && values[1] == 0 && values[2] == 0;
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, constexprness)
|
||||
{
|
||||
constexpr gsl::dyn_array<char> marlins;
|
||||
static_assert(marlins == marlins);
|
||||
static_assert(marlins.empty());
|
||||
static_assert(marlins.size() == 0);
|
||||
static_assert(marlins.data() == nullptr);
|
||||
static_assert(marlins.begin() == marlins.end());
|
||||
static_assert(std::distance(marlins.begin(), marlins.end()) == 0);
|
||||
static_assert(default_constructed_count_dyn_array_is_constexpr());
|
||||
}
|
||||
#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */
|
||||
|
||||
#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L)
|
||||
TEST(dyn_array_tests, ranges)
|
||||
{
|
||||
gsl::dyn_array<char> brewers(26);
|
||||
std::ranges::generate(brewers, [c = 'a']() mutable { return c++; });
|
||||
char ch = 'a';
|
||||
for (auto x : brewers) EXPECT_EQ(x, ch++);
|
||||
EXPECT_EQ(std::ranges::find(brewers, 'a'), std::ranges::begin(brewers));
|
||||
{
|
||||
auto it = std::ranges::find(brewers, 'c');
|
||||
EXPECT_EQ(std::ranges::distance(std::ranges::begin(brewers), it), 'c' - 'a');
|
||||
EXPECT_EQ(std::ranges::distance(it, std::ranges::begin(brewers)), 'a' - 'c');
|
||||
}
|
||||
|
||||
#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L)
|
||||
std::vector<char> twins(10, 'c');
|
||||
gsl::dyn_array<char> mets(std::from_range, twins);
|
||||
EXPECT_EQ(twins.size(), mets.size());
|
||||
EXPECT_TRUE(std::ranges::all_of(mets, [](char c) { return c == 'c'; }));
|
||||
#endif /* __cpp_lib_containers_ranges >= 202202L */
|
||||
}
|
||||
#endif /* __cpp_lib_ranges >= 201911L */
|
||||
|
||||
#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L)
|
||||
template <typename T, unsigned N>
|
||||
struct ConstexprAllocator
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
T buf[N]{};
|
||||
std::size_t sz{};
|
||||
|
||||
constexpr ConstexprAllocator() = default;
|
||||
|
||||
template <typename U>
|
||||
constexpr ConstexprAllocator(const ConstexprAllocator<U, N>&) noexcept : buf{}, sz{}
|
||||
{}
|
||||
|
||||
template <typename U>
|
||||
struct rebind
|
||||
{
|
||||
using other = ConstexprAllocator<U, N>;
|
||||
};
|
||||
|
||||
constexpr auto allocate(std::size_t n) -> value_type*
|
||||
{
|
||||
auto addr = &buf[sz];
|
||||
sz += n;
|
||||
return addr;
|
||||
}
|
||||
|
||||
constexpr void deallocate(value_type*, std::size_t) noexcept {}
|
||||
};
|
||||
|
||||
template <typename T1, unsigned N1, typename T2, unsigned N2>
|
||||
constexpr auto operator==(const ConstexprAllocator<T1, N1>& lhs,
|
||||
const ConstexprAllocator<T2, N2>& rhs) noexcept
|
||||
{
|
||||
return std::addressof(lhs) == std::addressof(rhs);
|
||||
}
|
||||
|
||||
template <typename T1, unsigned N1, typename T2, unsigned N2>
|
||||
constexpr auto operator!=(const ConstexprAllocator<T1, N1>& lhs,
|
||||
const ConstexprAllocator<T2, N2>& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */
|
||||
|
||||
template <typename T>
|
||||
static int AllocCounter = 0;
|
||||
|
||||
template <typename T>
|
||||
static int DeallocCounter = 0;
|
||||
|
||||
struct LifetimeCounter
|
||||
{
|
||||
static int alive_count;
|
||||
|
||||
int value{};
|
||||
|
||||
explicit LifetimeCounter(int v = 0) : value(v) { ++alive_count; }
|
||||
|
||||
LifetimeCounter(const LifetimeCounter& other) : value(other.value) { ++alive_count; }
|
||||
|
||||
~LifetimeCounter() { --alive_count; }
|
||||
};
|
||||
|
||||
int LifetimeCounter::alive_count = 0;
|
||||
|
||||
struct DefaultConstructionCounter
|
||||
{
|
||||
static int default_constructor_count;
|
||||
static int copy_constructor_count;
|
||||
|
||||
int value{};
|
||||
|
||||
DefaultConstructionCounter() { ++default_constructor_count; }
|
||||
|
||||
DefaultConstructionCounter(const DefaultConstructionCounter& other) : value(other.value)
|
||||
{
|
||||
++copy_constructor_count;
|
||||
}
|
||||
|
||||
static void reset()
|
||||
{
|
||||
default_constructor_count = 0;
|
||||
copy_constructor_count = 0;
|
||||
}
|
||||
};
|
||||
|
||||
int DefaultConstructionCounter::default_constructor_count = 0;
|
||||
int DefaultConstructionCounter::copy_constructor_count = 0;
|
||||
|
||||
struct DefaultOnlyElement
|
||||
{
|
||||
DefaultOnlyElement() = default;
|
||||
DefaultOnlyElement(const DefaultOnlyElement&) = delete;
|
||||
DefaultOnlyElement& operator=(const DefaultOnlyElement&) = delete;
|
||||
DefaultOnlyElement(DefaultOnlyElement&&) = delete;
|
||||
DefaultOnlyElement& operator=(DefaultOnlyElement&&) = delete;
|
||||
};
|
||||
|
||||
struct ThrowOnCopy
|
||||
{
|
||||
static int alive_count;
|
||||
static int copy_count;
|
||||
static int throw_on_copy_index;
|
||||
|
||||
int value{};
|
||||
|
||||
explicit ThrowOnCopy(int v = 0) : value(v) { ++alive_count; }
|
||||
|
||||
ThrowOnCopy(const ThrowOnCopy& other) : value(other.value)
|
||||
{
|
||||
if (copy_count == throw_on_copy_index)
|
||||
{
|
||||
++copy_count;
|
||||
throw 42;
|
||||
}
|
||||
++copy_count;
|
||||
++alive_count;
|
||||
}
|
||||
|
||||
~ThrowOnCopy() { --alive_count; }
|
||||
};
|
||||
|
||||
int ThrowOnCopy::alive_count = 0;
|
||||
int ThrowOnCopy::copy_count = 0;
|
||||
int ThrowOnCopy::throw_on_copy_index = -1;
|
||||
|
||||
template <typename T>
|
||||
class Newocator
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
Newocator() = default;
|
||||
|
||||
template <typename U>
|
||||
Newocator(const Newocator<U>&) noexcept
|
||||
{}
|
||||
|
||||
static void init()
|
||||
{
|
||||
AllocCounter<Newocator<T>> = 0;
|
||||
DeallocCounter<Newocator<T>> = 0;
|
||||
}
|
||||
|
||||
static void check() { EXPECT_EQ(AllocCounter<Newocator<T>>, DeallocCounter<Newocator<T>>); }
|
||||
|
||||
auto allocate(std::size_t n) -> value_type*
|
||||
{
|
||||
AllocCounter<Newocator<T>> ++;
|
||||
return static_cast<value_type*>(::operator new(n * sizeof(value_type)));
|
||||
}
|
||||
|
||||
void deallocate(value_type* p, std::size_t) noexcept
|
||||
{
|
||||
DeallocCounter<Newocator<T>> ++;
|
||||
::operator delete(p);
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
struct rebind
|
||||
{
|
||||
using other = Newocator<U>;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr auto operator==(const Newocator<T>&, const Newocator<U>&) noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr auto operator!=(const Newocator<T>& lhs, const Newocator<U>& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class OwnershipTrackingAllocator
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
OwnershipTrackingAllocator() noexcept : owner_id(next_owner_id()) { ++next_owner_id(); }
|
||||
|
||||
explicit OwnershipTrackingAllocator(int owner) noexcept : owner_id(owner) {}
|
||||
|
||||
template <typename U>
|
||||
OwnershipTrackingAllocator(const OwnershipTrackingAllocator<U>& other) noexcept
|
||||
: owner_id(other.owner())
|
||||
{}
|
||||
|
||||
auto allocate(std::size_t count) -> value_type*
|
||||
{
|
||||
static_assert(alignof(value_type) <= alignof(int),
|
||||
"test allocator only supports types with int-or-smaller alignment");
|
||||
auto raw =
|
||||
static_cast<unsigned char*>(::operator new(sizeof(int) + count * sizeof(value_type)));
|
||||
*reinterpret_cast<int*>(raw) = owner_id;
|
||||
++allocation_count();
|
||||
return reinterpret_cast<value_type*>(raw + sizeof(int));
|
||||
}
|
||||
|
||||
void deallocate(value_type* pointer, std::size_t) noexcept
|
||||
{
|
||||
auto raw = reinterpret_cast<unsigned char*>(pointer) - sizeof(int);
|
||||
if (*reinterpret_cast<int*>(raw) != owner_id) { ++mismatched_deallocation_count(); }
|
||||
++deallocation_count();
|
||||
::operator delete(raw);
|
||||
}
|
||||
|
||||
auto owner() const noexcept { return owner_id; }
|
||||
|
||||
static void reset()
|
||||
{
|
||||
next_owner_id() = 1;
|
||||
allocation_count() = 0;
|
||||
deallocation_count() = 0;
|
||||
mismatched_deallocation_count() = 0;
|
||||
}
|
||||
|
||||
static auto mismatched_deallocations() { return mismatched_deallocation_count(); }
|
||||
|
||||
private:
|
||||
int owner_id;
|
||||
|
||||
static auto next_owner_id() -> int&
|
||||
{
|
||||
static int value = 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
static auto allocation_count() -> int&
|
||||
{
|
||||
static int value = 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
static auto deallocation_count() -> int&
|
||||
{
|
||||
static int value = 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
static auto mismatched_deallocation_count() -> int&
|
||||
{
|
||||
static int value = 0;
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr auto operator==(const OwnershipTrackingAllocator<T>& lhs,
|
||||
const OwnershipTrackingAllocator<U>& rhs) noexcept
|
||||
{
|
||||
return lhs.owner() == rhs.owner();
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr auto operator!=(const OwnershipTrackingAllocator<T>& lhs,
|
||||
const OwnershipTrackingAllocator<U>& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, custom_allocator_models_allocator)
|
||||
{
|
||||
using traits = std::allocator_traits<Newocator<char>>;
|
||||
using ptr = traits::pointer;
|
||||
|
||||
static_assert(std::is_same<traits::value_type, char>::value, "allocator trait type mismatch");
|
||||
static_assert(std::is_same<ptr, char*>::value, "allocator trait type mismatch");
|
||||
|
||||
Newocator<char> alloc;
|
||||
auto p = traits::allocate(alloc, 1);
|
||||
traits::deallocate(alloc, p, 1);
|
||||
|
||||
#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L)
|
||||
using constexpr_traits = std::allocator_traits<ConstexprAllocator<char, 10>>;
|
||||
static_assert(std::is_same<constexpr_traits::value_type, char>::value,
|
||||
"allocator trait type mismatch");
|
||||
#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, custom_allocator)
|
||||
{
|
||||
#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L)
|
||||
static constexpr gsl::dyn_array<char, ConstexprAllocator<char, 10>> mets(10, 'c');
|
||||
static_assert(mets.size() == 10);
|
||||
static_assert(mets[0] == 'c');
|
||||
static_assert(std::all_of(std::begin(mets), std::end(mets), [](char c) { return c == 'c'; }));
|
||||
#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */
|
||||
|
||||
Newocator<char>::init();
|
||||
{
|
||||
gsl::dyn_array<char, Newocator<char>> yankees(10, 'c');
|
||||
EXPECT_EQ(yankees.size(), 10);
|
||||
EXPECT_TRUE(
|
||||
std::all_of(std::begin(yankees), std::end(yankees), [](char c) { return c == 'c'; }));
|
||||
yankees[0] = 'a';
|
||||
yankees[1] = 'b';
|
||||
EXPECT_EQ(yankees[0], 'a');
|
||||
EXPECT_EQ(yankees[1], 'b');
|
||||
EXPECT_EQ(yankees[2], 'c');
|
||||
yankees.get_allocator().deallocate(yankees.get_allocator().allocate(1), 1);
|
||||
}
|
||||
Newocator<char>::check();
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, non_trivial_elements_are_destroyed)
|
||||
{
|
||||
LifetimeCounter::alive_count = 0;
|
||||
|
||||
{
|
||||
gsl::dyn_array<LifetimeCounter> values(5, LifetimeCounter{7});
|
||||
EXPECT_EQ(values.size(), 5);
|
||||
EXPECT_EQ(LifetimeCounter::alive_count, 5);
|
||||
}
|
||||
|
||||
EXPECT_EQ(LifetimeCounter::alive_count, 0);
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, count_constructor_default_constructs_each_element)
|
||||
{
|
||||
DefaultConstructionCounter::reset();
|
||||
|
||||
{
|
||||
gsl::dyn_array<DefaultConstructionCounter> values(4);
|
||||
EXPECT_EQ(values.size(), 4);
|
||||
}
|
||||
|
||||
EXPECT_EQ(DefaultConstructionCounter::default_constructor_count, 4);
|
||||
EXPECT_EQ(DefaultConstructionCounter::copy_constructor_count, 0);
|
||||
}
|
||||
|
||||
#ifdef GSL_DYN_ARRAY_COMPILE_FAILURE_TESTS
|
||||
TEST(dyn_array_compile_failure_tests, count_constructor_accepts_default_constructible_only_elements)
|
||||
{
|
||||
gsl::dyn_array<DefaultOnlyElement> values(4);
|
||||
EXPECT_EQ(values.size(), 4);
|
||||
}
|
||||
#endif /* GSL_DYN_ARRAY_COMPILE_FAILURE_TESTS */
|
||||
|
||||
TEST(dyn_array_tests, failed_element_construction_rolls_back)
|
||||
{
|
||||
ThrowOnCopy::alive_count = 0;
|
||||
ThrowOnCopy::copy_count = 0;
|
||||
ThrowOnCopy::throw_on_copy_index = 2;
|
||||
|
||||
EXPECT_THROW((gsl::dyn_array<ThrowOnCopy>(5, ThrowOnCopy{1})), int);
|
||||
EXPECT_EQ(ThrowOnCopy::alive_count, 0);
|
||||
|
||||
ThrowOnCopy::throw_on_copy_index = -1;
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, init_list)
|
||||
{
|
||||
gsl::dyn_array<char> phillies = {'a', 'b', 'c'};
|
||||
EXPECT_EQ(phillies.size(), 3);
|
||||
EXPECT_EQ(phillies[0], 'a');
|
||||
EXPECT_EQ(phillies[1], 'b');
|
||||
EXPECT_EQ(phillies[2], 'c');
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, const_operations)
|
||||
{
|
||||
const gsl::dyn_array<char> pirates{'a', 'b', 'c', 'd'};
|
||||
EXPECT_EQ(pirates.size(), 4);
|
||||
EXPECT_EQ(pirates[0], 'a');
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, reverse_iterator)
|
||||
{
|
||||
const gsl::dyn_array<char> padres{'a', 'b', 'c'};
|
||||
auto it = std::rbegin(padres);
|
||||
EXPECT_EQ(*it++, 'c');
|
||||
EXPECT_EQ(*it++, 'b');
|
||||
EXPECT_EQ(*it++, 'a');
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, random_access_iterator_arithmetic)
|
||||
{
|
||||
gsl::dyn_array<char> bluejays{'a', 'b', 'c', 'd'};
|
||||
|
||||
auto first = bluejays.begin();
|
||||
auto third = first + 2;
|
||||
|
||||
EXPECT_EQ(*third, 'c');
|
||||
EXPECT_EQ(third - first, 2);
|
||||
EXPECT_EQ(*(third - 1), 'b');
|
||||
EXPECT_EQ(*std::prev(third), 'b');
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, random_access_iterator_arithmetic_accepts_negative_offsets)
|
||||
{
|
||||
gsl::dyn_array<char> bluejays{'a', 'b', 'c', 'd'};
|
||||
|
||||
auto third = bluejays.begin() + 2;
|
||||
char previous{};
|
||||
char next{};
|
||||
|
||||
EXPECT_NO_THROW(previous = *(third + -1));
|
||||
EXPECT_EQ(previous, 'b');
|
||||
|
||||
EXPECT_NO_THROW(next = *(third - -1));
|
||||
EXPECT_EQ(next, 'd');
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, input_iterator_constructor)
|
||||
{
|
||||
std::istringstream stream{"n a t s"};
|
||||
std::istream_iterator<char> first{stream};
|
||||
const std::istream_iterator<char> last{};
|
||||
|
||||
gsl::dyn_array<char> nationals(first, last);
|
||||
|
||||
ASSERT_EQ(nationals.size(), 4);
|
||||
EXPECT_EQ(nationals[0], 'n');
|
||||
EXPECT_EQ(nationals[1], 'a');
|
||||
EXPECT_EQ(nationals[2], 't');
|
||||
EXPECT_EQ(nationals[3], 's');
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, contract_violations)
|
||||
{
|
||||
const auto terminateHandler = std::set_terminate([] {
|
||||
std::cerr << "Expected Death. dyn_array_contract_violations";
|
||||
std::abort();
|
||||
});
|
||||
const auto expected = GetExpectedDeathString(terminateHandler);
|
||||
|
||||
gsl::dyn_array<char> values(3, 'v');
|
||||
gsl::dyn_array<char> other(3, 'o');
|
||||
|
||||
EXPECT_DEATH(values[values.size()], expected);
|
||||
EXPECT_DEATH((void) *values.end(), expected);
|
||||
EXPECT_DEATH(++values.end(), expected);
|
||||
EXPECT_DEATH(--values.begin(), expected);
|
||||
EXPECT_DEATH((void) (values.begin() == other.begin()), expected);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
TEST(dyn_array_tests, unchecked_iterators)
|
||||
{
|
||||
gsl::dyn_array<char> values;
|
||||
const gsl::dyn_array<char> const_values(3, 'v');
|
||||
|
||||
EXPECT_TRUE((std::is_same<decltype(const_values._Unchecked_begin()), const char*>::value));
|
||||
EXPECT_TRUE((std::is_same<decltype(const_values._Unchecked_end()), const char*>::value));
|
||||
|
||||
std::size_t count = 0;
|
||||
for (const auto value : const_values)
|
||||
{
|
||||
EXPECT_EQ(value, 'v');
|
||||
++count;
|
||||
}
|
||||
EXPECT_EQ(count, const_values.size());
|
||||
|
||||
EXPECT_EQ(values._Unchecked_begin(), nullptr);
|
||||
EXPECT_EQ(values._Unchecked_end(), nullptr);
|
||||
}
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
TEST(DynArrayTests, TypeConsistency)
|
||||
{
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::value_type, int>::value, "Value type mismatch");
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::reference, int&>::value,
|
||||
"Reference type mismatch");
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::const_reference, const int&>::value,
|
||||
"Const reference type mismatch");
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::iterator::value_type, int>::value,
|
||||
"Iterator value type mismatch");
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::iterator::reference, int&>::value,
|
||||
"Iterator reference type mismatch");
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::iterator::const_reference, const int&>::value,
|
||||
"Iterator const reference type mismatch");
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::size_type, std::size_t>::value,
|
||||
"Size type mismatch");
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::difference_type, std::ptrdiff_t>::value,
|
||||
"Difference type mismatch");
|
||||
}
|
||||
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
TEST(dyn_array_tests, deduction_guides)
|
||||
{
|
||||
std::vector<char> giants{10};
|
||||
#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L)
|
||||
gsl::dyn_array mariners(std::from_range, giants);
|
||||
#endif /* __cpp_lib_containers_ranges >= 202202L */
|
||||
gsl::dyn_array cardinals(std::begin(giants), std::end(giants));
|
||||
}
|
||||
#endif /* __cpp_deduction_guides >= 201703L */
|
||||
+22
-24
@@ -69,9 +69,7 @@ struct CustomPtr
|
||||
template <typename T, typename U>
|
||||
std::string operator==(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
|
||||
{
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(type.1)
|
||||
return reinterpret_cast<const void*>(lhs.p_) == reinterpret_cast<const void*>(rhs.p_) ? "true"
|
||||
: "false";
|
||||
}
|
||||
@@ -79,9 +77,7 @@ std::string operator==(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
|
||||
template <typename T, typename U>
|
||||
std::string operator!=(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
|
||||
{
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(type.1)
|
||||
return reinterpret_cast<const void*>(lhs.p_) != reinterpret_cast<const void*>(rhs.p_) ? "true"
|
||||
: "false";
|
||||
}
|
||||
@@ -89,9 +85,7 @@ std::string operator!=(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
|
||||
template <typename T, typename U>
|
||||
std::string operator<(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
|
||||
{
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(type.1)
|
||||
return reinterpret_cast<const void*>(lhs.p_) < reinterpret_cast<const void*>(rhs.p_) ? "true"
|
||||
: "false";
|
||||
}
|
||||
@@ -99,9 +93,7 @@ std::string operator<(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
|
||||
template <typename T, typename U>
|
||||
std::string operator>(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
|
||||
{
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(type.1)
|
||||
return reinterpret_cast<const void*>(lhs.p_) > reinterpret_cast<const void*>(rhs.p_) ? "true"
|
||||
: "false";
|
||||
}
|
||||
@@ -109,9 +101,7 @@ std::string operator>(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
|
||||
template <typename T, typename U>
|
||||
std::string operator<=(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
|
||||
{
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(type.1)
|
||||
return reinterpret_cast<const void*>(lhs.p_) <= reinterpret_cast<const void*>(rhs.p_) ? "true"
|
||||
: "false";
|
||||
}
|
||||
@@ -119,9 +109,7 @@ std::string operator<=(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
|
||||
template <typename T, typename U>
|
||||
std::string operator>=(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
|
||||
{
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(type.1)
|
||||
return reinterpret_cast<const void*>(lhs.p_) >= reinterpret_cast<const void*>(rhs.p_) ? "true"
|
||||
: "false";
|
||||
}
|
||||
@@ -137,13 +125,9 @@ struct NonCopyableNonMovable
|
||||
|
||||
namespace
|
||||
{
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(f .4) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(f.4)
|
||||
bool helper(not_null<int*> p) { return *p == 12; }
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(f .4) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(f.4)
|
||||
bool helper_const(not_null<const int*> p) { return *p == 12; }
|
||||
|
||||
int* return_pointer() { return nullptr; }
|
||||
@@ -735,4 +719,18 @@ TEST(notnull_tests, TestStdHash)
|
||||
EXPECT_FALSE(hash_nn(nn) == hash_intptr(&y));
|
||||
EXPECT_FALSE(hash_nn(nn) == hash_intptr(nullptr));
|
||||
}
|
||||
|
||||
{
|
||||
auto x = std::make_shared<int>(42);
|
||||
auto y = std::make_shared<int>(99);
|
||||
not_null<std::shared_ptr<int>> nn{x};
|
||||
const not_null<std::shared_ptr<int>> cnn{x};
|
||||
|
||||
std::hash<not_null<std::shared_ptr<int>>> hash_nn;
|
||||
std::hash<std::shared_ptr<int>> hash_sharedptr;
|
||||
|
||||
EXPECT_TRUE(hash_nn(nn) == hash_sharedptr(x));
|
||||
EXPECT_FALSE(hash_nn(nn) == hash_sharedptr(y));
|
||||
EXPECT_TRUE(hash_nn(cnn) == hash_sharedptr(x));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
using namespace gsl;
|
||||
|
||||
GSL_SUPPRESS(f .23) // NO-FORMAT: attribute
|
||||
GSL_SUPPRESS(f.23)
|
||||
void f(int* i) { *i += 1; }
|
||||
|
||||
TEST(owner_tests, basic_test)
|
||||
@@ -43,8 +43,7 @@ using void_t = void;
|
||||
template <typename U, typename = void>
|
||||
static constexpr bool OwnerCompilesFor = false;
|
||||
template <typename U>
|
||||
static constexpr bool OwnerCompilesFor<U, void_t<decltype(gsl::owner<U>{})>> =
|
||||
true;
|
||||
static constexpr bool OwnerCompilesFor<U, void_t<decltype(gsl::owner<U>{})>> = true;
|
||||
static_assert(OwnerCompilesFor<int*>, "OwnerCompilesFor<int*>");
|
||||
static_assert(!OwnerCompilesFor<int>, "!OwnerCompilesFor<int>");
|
||||
static_assert(!OwnerCompilesFor<std::shared_ptr<int>>, "!OwnerCompilesFor<std::shared_ptr<int>>");
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <gsl/pointers>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -43,6 +44,9 @@ TEST(pointers_test, swap)
|
||||
gsl::not_null<std::unique_ptr<int>> a(std::make_unique<int>(0));
|
||||
gsl::not_null<std::unique_ptr<int>> b(std::make_unique<int>(1));
|
||||
|
||||
static_assert(noexcept(gsl::swap(a, b)),
|
||||
"not null unique_ptr should be noexcept-swappable");
|
||||
|
||||
EXPECT_TRUE(*a == 0);
|
||||
EXPECT_TRUE(*b == 1);
|
||||
|
||||
@@ -62,6 +66,9 @@ TEST(pointers_test, swap)
|
||||
gsl::strict_not_null<std::unique_ptr<int>> a{std::make_unique<int>(0)};
|
||||
gsl::strict_not_null<std::unique_ptr<int>> b{std::make_unique<int>(1)};
|
||||
|
||||
static_assert(noexcept(gsl::swap(a, b)),
|
||||
"strict not null unique_ptr should be noexcept-swappable");
|
||||
|
||||
EXPECT_TRUE(*a == 0);
|
||||
EXPECT_TRUE(*b == 1);
|
||||
|
||||
@@ -94,4 +101,19 @@ TEST(pointers_test, member_types)
|
||||
"check member type: element_type");
|
||||
}
|
||||
|
||||
TEST(pointers_test, hash_noexcept_compiles)
|
||||
{
|
||||
{
|
||||
using Key = gsl::not_null<std::shared_ptr<int>>;
|
||||
static_assert(noexcept(std::hash<Key>{}(std::declval<Key>())),
|
||||
"gsl::not_null hash operator must be noexcept");
|
||||
}
|
||||
|
||||
{
|
||||
using Key = gsl::strict_not_null<std::shared_ptr<int>>;
|
||||
static_assert(noexcept(std::hash<Key>{}(std::declval<Key>())),
|
||||
"gsl::strict_not_null hash operator must be noexcept");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -61,10 +61,10 @@ void ArrayConvertibilityCheck()
|
||||
|
||||
static_assert(std::is_same<decltype(gsl::span{stl_nullptr}), gsl::span<T*, 3>>::value,
|
||||
"std::is_same< decltype(span{stl_nullptr}), span<T*, 3>>::value");
|
||||
static_assert(
|
||||
std::is_same<decltype(gsl::span{std::as_const(stl_nullptr)}), gsl::span<T* const, 3>>::value,
|
||||
"std::is_same< decltype(span{std::as_const(stl_nullptr)}), span<T* const, "
|
||||
"3>>::value");
|
||||
static_assert(std::is_same<decltype(gsl::span{std::as_const(stl_nullptr)}),
|
||||
gsl::span<T* const, 3>>::value,
|
||||
"std::is_same< decltype(span{std::as_const(stl_nullptr)}), span<T* const, "
|
||||
"3>>::value");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ TEST(span_ext_test, make_span_from_array_constructor)
|
||||
|
||||
TEST(span_ext_test, make_span_from_dynamic_array_constructor)
|
||||
{
|
||||
double(*arr)[3][4] = new double[100][3][4];
|
||||
double (*arr)[3][4] = new double[100][3][4];
|
||||
|
||||
{
|
||||
auto s = make_span(&arr[0][0][0], 10);
|
||||
|
||||
+15
-13
@@ -32,14 +32,14 @@
|
||||
#include <vector> // for vector
|
||||
|
||||
// the string_view include and macro are used in the deduction guide verification
|
||||
#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L))
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
#ifdef __has_include
|
||||
#if __has_include(<string_view>)
|
||||
#include <string_view>
|
||||
#define HAS_STRING_VIEW
|
||||
#endif // __has_include(<string_view>)
|
||||
#endif // __has_include
|
||||
#endif // (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L))
|
||||
#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
#if defined(__cplusplus) && __cplusplus >= 202002L
|
||||
#include <span>
|
||||
#endif // __cplusplus >= 202002L
|
||||
@@ -257,7 +257,9 @@ TEST(span_test, from_pointer_pointer_construction)
|
||||
EXPECT_TRUE(s.data() == &arr[0]);
|
||||
}
|
||||
|
||||
//{ // this test succeeds on all platforms, gsl::span is more relaxed than std::span where this would be UB
|
||||
// this test succeeds on all platforms, gsl::span is more relaxed than std::span where this
|
||||
// would be UB
|
||||
//{
|
||||
// auto workaround_macro = [&]() { span<int> s{&arr[1], &arr[0]}; };
|
||||
// EXPECT_DEATH(workaround_macro(), expected);
|
||||
//}
|
||||
@@ -342,7 +344,7 @@ TEST(span_test, from_array_constructor)
|
||||
|
||||
TEST(span_test, from_dynamic_array_constructor)
|
||||
{
|
||||
double(*arr)[3][4] = new double[100][3][4];
|
||||
double (*arr)[3][4] = new double[100][3][4];
|
||||
|
||||
{
|
||||
span<double> s(&arr[0][0][0], 10);
|
||||
@@ -412,8 +414,8 @@ TEST(span_test, from_std_array_constructor)
|
||||
static_assert(!CtorCompilesFor<span<int, 5>, std::array<int, 4>&>,
|
||||
"!CtorCompilesFor<span<int, 5>, std::array<int, 4>&>");
|
||||
|
||||
#if !defined(_MSC_VER) || (_MSC_VER > 1943) || (__cplusplus >= 201703L)
|
||||
// Fails on "Visual Studio 16 2019/Visual Studio 17 2022, windows-2019/2022, Debug/Release, 14".
|
||||
#if !defined(_MSC_VER) || (__cplusplus >= 201703L)
|
||||
// Fails on MSVC. TODO: report a feedback bug.
|
||||
static_assert(!ConversionCompilesFor<span<int>, std::array<int, 4>>,
|
||||
"!ConversionCompilesFor<span<int>, std::array<int, 4>>");
|
||||
#endif
|
||||
@@ -529,8 +531,8 @@ TEST(span_test, from_container_constructor)
|
||||
EXPECT_TRUE(cs.data() == cstr.data());
|
||||
}
|
||||
|
||||
#if !defined(_MSC_VER) || (_MSC_VER > 1943) || (__cplusplus >= 201703L)
|
||||
// Fails on "Visual Studio 16 2019/Visual Studio 17 2022, windows-2019/2022, Debug/Release, 14".
|
||||
#if !defined(_MSC_VER) || (__cplusplus >= 201703L)
|
||||
// Fails on MSVC. TODO: report a feedback bug.
|
||||
static_assert(!ConversionCompilesFor<span<int>, std::vector<int>>,
|
||||
"!ConversionCompilesFor<span<int>, std::vector<int>>");
|
||||
#endif // !defined(_MSC_VER) || (_MSC_VER > 1942) || (__cplusplus >= 201703L)
|
||||
@@ -1209,7 +1211,7 @@ TEST(span_test, default_constructible)
|
||||
|
||||
TEST(span_test, std_container_ctad)
|
||||
{
|
||||
#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L))
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
// this test is just to verify that these compile
|
||||
{
|
||||
std::vector<int> v{1, 2, 3, 4};
|
||||
@@ -1228,7 +1230,7 @@ TEST(span_test, std_container_ctad)
|
||||
static_assert(std::is_same<decltype(sp), gsl::span<const char>>::value);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
}
|
||||
|
||||
TEST(span_test, front_back)
|
||||
@@ -1294,13 +1296,13 @@ TEST(span_test, conversions)
|
||||
{
|
||||
int arr[5] = {1, 2, 3, 4, 5};
|
||||
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L)
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
span s = arr;
|
||||
span cs = s;
|
||||
#else
|
||||
#else // ^^^ deduction guides /// no deduction guides vvv
|
||||
span<int, 5> s = arr;
|
||||
span<int, 5> cs = s;
|
||||
#endif
|
||||
#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
|
||||
EXPECT_TRUE(cs.size() == s.size());
|
||||
EXPECT_TRUE(cs.data() == s.data());
|
||||
|
||||
@@ -41,24 +41,16 @@ struct RefCounted
|
||||
|
||||
namespace
|
||||
{
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(f.4)
|
||||
bool helper(not_null<int*> p) { return *p == 12; }
|
||||
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(f.4)
|
||||
bool helper_const(not_null<const int*> p) { return *p == 12; }
|
||||
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(f.4)
|
||||
bool strict_helper(strict_not_null<int*> p) { return *p == 12; }
|
||||
|
||||
// clang-format off
|
||||
GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
|
||||
// clang-format on
|
||||
GSL_SUPPRESS(f.4)
|
||||
bool strict_helper_const(strict_not_null<const int*> p) { return *p == 12; }
|
||||
|
||||
int* return_pointer() { return nullptr; }
|
||||
@@ -195,7 +187,6 @@ template <typename U>
|
||||
static constexpr bool
|
||||
StrictHelperCompilesFor<U, void_t<decltype(strict_helper(std::declval<U>()))>> = true;
|
||||
|
||||
|
||||
template <typename U, typename = void>
|
||||
static constexpr bool StrictHelperConstCompilesFor = false;
|
||||
template <typename U>
|
||||
@@ -203,7 +194,6 @@ static constexpr bool
|
||||
StrictHelperConstCompilesFor<U, void_t<decltype(strict_helper_const(std::declval<U>()))>> =
|
||||
true;
|
||||
|
||||
|
||||
template <typename U, typename = void>
|
||||
static constexpr bool HelperCompilesFor = false;
|
||||
template <typename U>
|
||||
@@ -219,13 +209,12 @@ TEST(strict_notnull_tests, TestStrictNotNull)
|
||||
strict_not_null<int*> snn = &x;
|
||||
#endif
|
||||
static_assert(!StrictHelperCompilesFor<int*>, "!StrictHelperCompilesFor<int*>");
|
||||
static_assert(!StrictHelperConstCompilesFor<int*>,
|
||||
"!StrictHelperCompilesFor<int*>");
|
||||
static_assert(!StrictHelperConstCompilesFor<int*>, "!StrictHelperCompilesFor<int*>");
|
||||
|
||||
const strict_not_null<int*> snn1{&x};
|
||||
|
||||
static_assert(StrictHelperCompilesFor<const strict_not_null<int*>>,
|
||||
"StrictHelperCompilesFor<const strict_not_null<int*>>");
|
||||
"StrictHelperCompilesFor<const strict_not_null<int*>>");
|
||||
helper(snn1);
|
||||
helper_const(snn1);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm> // for move
|
||||
#include <algorithm> // for move
|
||||
#include <complex>
|
||||
#include <cstddef> // for std::ptrdiff_t
|
||||
#include <cstdint> // for uint32_t, int32_t
|
||||
@@ -156,8 +156,8 @@ TEST(utils_tests, narrow)
|
||||
n = -42;
|
||||
EXPECT_THROW(narrow<unsigned>(n), narrowing_error);
|
||||
|
||||
EXPECT_TRUE(
|
||||
narrow<std::complex<float>>(std::complex<double>(4, 2)) == std::complex<float>(4, 2));
|
||||
EXPECT_TRUE(narrow<std::complex<float>>(std::complex<double>(4, 2)) ==
|
||||
std::complex<float>(4, 2));
|
||||
EXPECT_THROW(narrow<std::complex<float>>(std::complex<double>(4.2)), narrowing_error);
|
||||
|
||||
EXPECT_TRUE(narrow<int>(float(1)) == 1);
|
||||
|
||||
Reference in New Issue
Block a user