Compare commits

...

43 Commits

Author SHA1 Message Date
Carson Radtke a75212b9f3 feat: implementation of gsl::dyn_array (#1228)
* feat: implementation of gsl::dyn_array

Implement gsl::dyn_array<T, Allocator> as specified by the
CppCoreGuidlines here:
https://github.com/isocpp/CppCoreGuidelines/blob/master/docs/dyn_array.md

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-07-13 14:53:09 -06:00
Werner Henze b2f6bec48e clang-format improvements (#1251)
* clang-format improvements

- Add a clang-format linter check to the PR pipeline
- Apply clang-format to files where the linter initially failed
- Remove `CommentPragmas` from `.clang-format`
- Remove all `// clang-format off` and `// NO-FORMAT` as they are not needed
- Remove a commented out `GSL_SUPPRESS`

* clang-format 20

* pipeline fail

* Update .github/workflows/clang-format.yml

Co-authored-by: Carson Radtke <nosrac925@gmail.com>

* output used clang-format version

* installed version is 18 which replaces "GSL_SUPPRESS(bounds.1)" with "GSL_SUPPRESS(bounds .1)"

* only include/gsl, not include

to prevent formatting of include/CMakeLists.txt

* apply clang-format[-20]

In a VS2026 developer command prompt I ran `clang-format -i include\gsl\* --assume-filename x.cpp`. This was necessary because VS GUI does not format files without an extension :(. Please note that `--assume-filename` is necessary here, otherwise the files will not be formatted. Surprisingly the behaviour for the formatter differs from the behaviour of `lang-format(-20) --dry-run --Werror include/gsl/*` where clang-format recognizes that the files is C++.

* change "#if 0" back to original version with comments only

* formatting scripts for windows and linux

for linux with linter (shfmt and shellcheck)

* add WhitespaceSensitiveMacros: [GSL_SUPPRESS]

* provide path for clang-format (Windows)

Currently not clear to me:
On my personal computer at home, when I start "Developer Command Prompt for VS18", I can run `clang-format` without providing the path.
On my managed (domain) company computer, when I start "Developer Command Prompt for VS18", I can NOT run `clang-format` without providing the path. I need to call `"%VCINSTALLDIR%Tools\Llvm\bin\clang-format"`.
I have no idea what the difference is. At least the version `"%VCINSTALLDIR%Tools\Llvm\bin\clang-format"` works on both computers, so I add the path.

---------

Co-authored-by: Werner Henze <w.henze@avm.de>
Co-authored-by: Carson Radtke <nosrac925@gmail.com>
Co-authored-by: Werner Henze <werner.henze+gitcommits@posteo.de>
2026-06-05 06:45:10 -06:00
Werner Henze f3c5967126 remove space in GSL_SUPPRESS (#1248)
As VS2026 wants a string literal in `[[gsl::suppress(...)]]` it is a difference, if you `GSL_SUPPRESS(a.1)` without space or `GSL_SUPPRESS(a .1)` with space. The version with the space does not work, so this commit removes the spaces.

Co-authored-by: Werner Henze <werner.henze+gitcommits@posteo.de>
2026-05-27 12:04:16 -06:00
maflcko 4524208d32 ci: Bump actions/setup-java@v5 (#1249)
Co-authored-by: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz>
2026-05-27 11:15:45 -06:00
Werner Henze 66ca39d9c8 narrow_cast instead of static_cast (#1250)
This removes the "warning C26472: Don't use a static_cast for arithmetic conversions. Use brace initialization, gsl::narrow_cast or gsl::narrow (type.1)." issued by VS2026.

Co-authored-by: Werner Henze <w.henze@avm.de>
2026-05-27 11:15:07 -06:00
maflcko 0249144ad1 ci: Use microsoft/setup-msbuild@v3 (#1240)
Co-authored-by: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz>
2026-03-24 11:59:58 -06:00
maflcko 688ffcde90 ci: Bump to actions/checkout@v6 (#1237)
Co-authored-by: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz>
2026-03-23 12:23:07 -06:00
maflcko fcf3fe37c6 gsl::not_null: C.89: Make a hash noexcept (#1236)
Without this change, the guideline
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c89-make-a-hash-noexcept
would be violated.

The test fails before the changes here:

```
tests/pointers_tests.cpp:102:23: error: static assertion failed due to requirement 'noexcept(std::hash<gsl::not_null<std::shared_ptr<int>>>{}(std::declval()))': gsl::not_null hash operator must be noexcept
  102 |         static_assert(noexcept(std::hash<Key>{}(std::declval<Key>())),
      |                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tests/pointers_tests.cpp:108:23: error: static assertion failed due to requirement 'noexcept(std::hash<gsl::strict_not_null<std::shared_ptr<int>>>{}(std::declval()))': gsl::strict_not_null hash operator must be noexcept
  108 |         static_assert(noexcept(std::hash<Key>{}(std::declval<Key>())),
      |                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Co-authored-by: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz>
2026-03-23 12:18:30 -06:00
maflcko 9f9f65c7e2 Mark gsl::not_null swap noexcept (#1235)
Co-authored-by: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz>
2026-03-23 12:16:36 -06:00
maflcko 5ca9c77666 doc: Fix to use lower-case anchors in links to C++ Core Guidelines (#1234)
Co-authored-by: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz>
2026-03-20 07:38:12 -06:00
maflcko bf9d5e1aef doc: Update URL in pointers to F.16 C++ Core Guidelines (#1232)
The anchor ID in the target is now lower-case.
2026-03-18 10:14:34 -06:00
Carson Radtke 756c91ab89 infra: individual test executables (#1212)
* infra: individual test executables

We used have tests contained in a single executable. This was fine for
testing, but it would be more convient to separate tests into indivudal
modules so targeted changes could have targeted tests.

This change associates each test file with its own executable. We now
have 14 tests for GSL each of which testing a different component.

* revert -Wno-reserved-identifier

* Update tests/span_tests.cpp

thanks copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* be sure to include build type in ctest command

* [VS] make sure we are building the correct configuration

* restore tests/span_tests.cpp

* fix build break after merge conflicts

* fix build break after merge conflicts #2

* another try at fixing a build break

* fix silly typo. build break pt 4

* Use file globbing for test sources instead of manual list (#1227)

* Initial plan

* Use file globbing for test sources instead of manual list

Replace the manually maintained list of test sources with file(GLOB) to
automatically discover all .cpp files in the tests directory, excluding
no_exception_ensure_tests.cpp which needs special compilation flags.

This approach:
- Automatically picks up new test files without CMake updates
- Still correctly excludes no_exception_ensure_tests.cpp
- Maintains the same test build configuration
- Works with both C++14 and C++20

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
2025-12-05 13:24:15 -07:00
Copilot 1883887359 Fix std::hash compilation error for gsl::not_null<std::shared_ptr<T>> (#1210)
* Initial plan for issue

* Fix hash compilation issue for gsl::not_null with shared_ptr

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* Add comment explaining safe usage of typename T::element_type in not_null_hash

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* Initial plan for issue

* Remove upgrade_checklist.md file as requested

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* restore docs/upgrade_checklist.md

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>
Co-authored-by: Carson Radtke <carsonradtke@microsoft.com>
2025-12-05 10:00:06 -07:00
Carson Radtke c31617f56a use cmake presets instead of cmake settings (#1219)
* use cmake presets instead of cmake settings

This change uses a cmake presets file in an effort allow for agentic AI
to better switch between project settings. It replaces the old
CMakeSettings.json for the more flexible CMakePresets.json.

* Update GitHub Actions workflows to use CMake presets (#1223)

* Initial plan

* Update GitHub Actions to use CMake presets for compilers workflow

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* remove unused cmake options

* address PR feedback from copilot

* fix build break

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
2025-12-05 09:59:18 -07:00
Werner Henze 4fb59125d1 Revert unintended change to GSL_SUPPRESS (#1226)
PR #1213 changed this line. According to https://github.com/microsoft/GSL/pull/1213/files#r2586073058 the change was unintended. This PR reverts the change to the previous implementation.

Co-authored-by: Werner Henze <w.henze@avm.de>
2025-12-04 12:44:26 -07:00
Carson Radtke 543d0dd3fe export proper syntax for GSL_SUPPRESS for new VS (#1213)
A new Visual Studio version will soon be available that deprecates the
old syntax for gsl::suppress. Customers will now get a C4875 diagnostic
on suppressions that look like `gsl::suppress(x)` urging them to use
`gsl::suppress("x")` instead.

This change updates the `GSL_SUPPRESS` macro to preprocess
GSL_SUPPRESS(x) to gsl::suppress("x") on clang and new versions of MSVC.
2025-10-02 08:47:16 -06:00
Carson Radtke 494e6e988c fix: all pipeline failures (#1221)
* fix: update conditional static assertion
There is a static assertion that spuriously fails on MSVC that was
version checked. Unfortunately it fires every time there is a new
compiler update, so for now we will remove the version check and add it
back once the compiler bug is fixed.

* fix: ios pipeline failure

Looks like somewhere along the line, the iOS simulator changed the range
of support iOS versions. This changes bumps the version from 9 to 12.0.

Also noticed that the GSL OSX bundle version was quite out of date. I
bumped this from 3.1.0 to 4.2.0 and created an upgrade checklist file in
docs/ so we don't forget these types of tasks in the future.

* bump xcode version from 15.4 to 16.4

* fix compiler warning when building gtest for ios

* allow for missing include dirs on command line

* replace windows-2019 with windows-2025

* update visual studio versions after runner bump

* PR feedback: make sure markdown links are syntactically correct
2025-09-30 15:10:46 -06:00
Copilot 7e0943d20d Add constexpr to not_null comparison operators (#1208)
* Initial plan for issue

* Add test and plan to make not_null comparison functions constexpr

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* Add constexpr to not_null comparison operators

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* Fix copyright year in constexpr_notnull_tests.cpp

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* Fix constexpr tests for better compiler compatibility

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* Remove build artifacts and update .gitignore

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* Fix constexpr tests to be compatible with more compilers

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* copilot: Provide more project context for the Copilot coding agent (#1207)

* copilot: create .github/copilot-instructions.md

This file provides additional context and instructions to GitHub
Copilot so it can better understand the codebase and coding conventions.

More can be found about this file at the following links:
 - [Best practices for using Copilot to work on tasks](https://docs.github.com/en/enterprise-cloud@latest/copilot/using-github-copilot/using-copilot-coding-agent-to-work-on-tasks/best-practices-for-using-copilot-to-work-on-taskshttps://docs.github.com/en/enterprise-cloud@latest/copilot/using-github-copilot/using-copilot-coding-agent-to-work-on-tasks/best-practices-for-using-copilot-to-work-on-tasks)
 - [Adding repository custom instructions for GitHub Copilot](https://docs.github.com/en/enterprise-cloud@latest/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot?tool=webuihttps://docs.github.com/en/enterprise-cloud@latest/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot)

* copilot: add copilot-setup-steps.yml

This new workflow is done when copilot loads into an environment and
enables copilot to be sure it has the proper dependencies before working
on changes. Also included in the change are explicit instructions on
what to do before reporting back "done".

* Initial plan for issue

* Rebase onto main and verify changes meet project guidelines

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* Update .gitignore to exclude build-cxx* directories

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* Fix newline at end of constexpr_notnull_tests.cpp and update .gitignore

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* Remove C++14 feature check that is redundant since C++14 is the minimum supported standard

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>
Co-authored-by: Carson Radtke <carsonradtke@microsoft.com>
2025-05-22 11:52:47 -06:00
Copilot 2d343b0440 Document differences between hardened std::span and gsl::span (#1206)
* Initial plan for issue

* Document differences between hardened std::span and gsl::span

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* Simplify documentation for span differences

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* Enhance span documentation with comparison table and links

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

* Fix library hardening documentation links

Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com>
2025-05-22 11:34:22 -06:00
Carson Radtke c21970972b copilot: Provide more project context for the Copilot coding agent (#1207)
* copilot: create .github/copilot-instructions.md

This file provides additional context and instructions to GitHub
Copilot so it can better understand the codebase and coding conventions.

More can be found about this file at the following links:
 - [Best practices for using Copilot to work on tasks](https://docs.github.com/en/enterprise-cloud@latest/copilot/using-github-copilot/using-copilot-coding-agent-to-work-on-tasks/best-practices-for-using-copilot-to-work-on-taskshttps://docs.github.com/en/enterprise-cloud@latest/copilot/using-github-copilot/using-copilot-coding-agent-to-work-on-tasks/best-practices-for-using-copilot-to-work-on-tasks)
 - [Adding repository custom instructions for GitHub Copilot](https://docs.github.com/en/enterprise-cloud@latest/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot?tool=webuihttps://docs.github.com/en/enterprise-cloud@latest/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot)

* copilot: add copilot-setup-steps.yml

This new workflow is done when copilot loads into an environment and
enables copilot to be sure it has the proper dependencies before working
on changes. Also included in the change are explicit instructions on
what to do before reporting back "done".
2025-05-22 10:32:01 -06:00
ozaktash 466e4ebaa5 Update return by value threshold in gsl::not_null (#1205)
- Allow returning by value for types that are not greater than two pointers in size
2025-05-12 11:02:03 -06:00
Werner Henze 3325bbd33d Fix version info (#1202)
Just tagging as 4.2.0 is not enough, the version number must be incremented for cmake and in the readme. Compare https://github.com/microsoft/GSL/pull/1163.

Co-authored-by: Werner Henze <w.henze@avm.de>
2025-03-27 11:00:24 -05:00
Werner Henze 2828399820 replace BYTE_TYPE with a solution based on namespaces (#1201)
- A macro with the very generic name `BYTE_TYPE` is likely to collide with existing code, so get rid of the macro.
- The new solution is to provide a non-deprecated `byte` in the namespace `gsl::impl`.
  - Users of GSL should use `gsl::byte`, which is still deprecated when mapped to a `std::std::byte`.
  - GSL types and functions need to use `gsl::impl::byte` so they do not trigger the deprecation warning.
- The `gsl::impl::byte` return type in an exported function is not nice, it might mislead users to use that type in their own declarations. But the `BYTE_TYPE` solution is not better in this respect.

Co-authored-by: Werner Henze <w.henze@avm.de>
2025-02-28 08:53:25 -06:00
Werner Henze c5fbb81ad4 documentation (#1200)
- reintroduce documentation internal links
- remove `gsl::` prefix in table so it is the same as in the other tables
- fix documentation for span::element_type

Co-authored-by: Werner Henze <w.henze@avm.de>
2025-02-28 08:49:53 -06:00
Carson Radtke 7fabaa499d deprecate features adopted into C++ (#1198)
* deprecated features adopted into C++

1) Mark the following GSL features as deprecated:
 - gsl::unique_ptr (always)
 - gsl::shared_ptr (always)
 - gsl::byte (since c++17)
 - gsl::joining_thread (never implemented)
2) Refactor existing deprecations to use the new GSL_DEPRECATED(msg) macro.
3) Create a section in the README for deprecated features in the
   standard.

* do not deprecate gsl::to_integer because we never claim to implement it.

* do not use gsl::byte if it is deprecated
2025-02-14 11:24:51 -06:00
Carson Radtke 4742bc192a use relative path in # include (#1194)
Reverts commit that changes #include "assert" -> #include "gsl/assert".
This change is necessary in order to comply with CppCoreGuideline's
SF.12. Now we do #include "./assert".
2025-02-12 09:16:45 -06:00
Carson Radtke ec729d63a7 introduce gsl::not_null<T>::element_type (#1196)
* introduce gsl::not_null<T>::element_type

* use std::is_same instead of is_same_v

* fix: cannot put a non-pointer in a gsl::not_null
2025-02-12 09:16:05 -06:00
Carson Radtke 7f4fc9388b Add more gsl::span tests [copilot] (#1189)
* Add more gsl::span tests [copilot]

This PR adds comprehensive unit tests for `gsl::span` to ensure its correctness and consistency. The following tests have been added:

1. **Empty Span Tests**:
   - Tests to verify the behavior of an empty `gsl::span` and `gsl::span<const int>`.

2. **Conversion Tests**:
   - Tests to check the conversion between different types of `gsl::span`.

3. **Comparison Operator Tests**:
   - Tests to verify the comparison operators for `gsl::span`.

4. **Deduction Guide Tests**:
   - Tests to compare the behavior of `gsl::span` and `std::span` deduction guides for various types of arrays and containers.

These tests help ensure that `gsl::span` behaves correctly in various scenarios and is consistent with `std::span`.

This PR was created with the help of GitHub Copilot.

**Changes**:
- Added tests for empty span.
- Added tests for conversions.
- Added tests for comparison operators.
- Added tests for deduction guides.

**Testing**:
- All new tests have been added to the existing test suite.
- Run the test suite using `ctest` to ensure all tests pass.This PR adds comprehensive unit tests for `gsl::span` to ensure its correctness and consistency. The following tests have been added:

1. **Empty Span Tests**:
   - Tests to verify the behavior of an empty `gsl::span` and `gsl::span<const int>`.

2. **Conversion Tests**:
   - Tests to check the conversion between different types of `gsl::span`.

3. **Comparison Operator Tests**:
   - Tests to verify the comparison operators for `gsl::span`.

4. **Deduction Guide Tests**:
   - Tests to compare the behavior of `gsl::span` and `std::span` deduction guides for various types of arrays and containers.

These tests help ensure that `gsl::span` behaves correctly in various scenarios and is consistent with `std::span`.

This PR was created with the help of GitHub Copilot.

**Changes**:
- Added tests for empty span.
- Added tests for conversions.
- Added tests for comparison operators.
- Added tests for deduction guides.

**Testing**:
- All new tests have been added to the existing test suite.
- Run the test suite using `ctest` to ensure all tests pass.

* fix tests for pre-C++17
2025-02-12 09:15:43 -06:00
Tiago 355982daf6 Delete gsl/string_span (#1185)
This has been deprecated for 2+ years.
2025-01-07 08:58:59 -06:00
Werner Henze 50aaf2efbb remove documentation for deprecated make_span overloads (#1183)
Co-authored-by: Werner Henze <werner.henze+gitcommits@posteo.de>
2025-01-07 08:50:49 -06:00
Tiago 7b6b8013b1 Add "gsl" to #includes (#1184)
Office is seeing build breaks due to `#include "span"` including
C++20 span instead of gsl/span. Most likely we want all headers
includes qualified with "gsl/" to avoid similar issues.
2025-01-07 08:42:38 -06:00
Werner Henze 1cdb8d295e More checks for non-compilable code, plus fix for span (#1180) 2025-01-04 11:50:31 -06:00
Carson Radtke c832885f15 React to ubuntu-latest changing to 24.04 (#1181)
* React to ubuntu-latest changing to 24.04
Reacting to https://github.com/actions/runner-images/issues/10636
* update clang to 16,17,18
* update gcc to 12,13,14
2025-01-03 10:23:50 -06:00
Werner Henze 16a60199df strict_not_null for unique_ptr (#1179)
- `strict_not_null<std::unique_ptr<int>>{ std::make_unique<int>()}` failed to compile
  - `strict_not_null` ctor needs to move the passed `unique_ptr`, not copy
  - Copied `not_null` `TestNotNullConstructors` for `strict_not_null`
- The `noexcept` specifiers on the `strict_not_null` and `not_null` constructors were out of sync.
- Added unit test for `not_null<unique_ptr<T>>` and for `strict_not_null<unique_ptr<T>>`
- Added unit test for `gsl::swap` for two `strict_not_null`
- Added unit test for `gsl::swap` for `not_null` and `strict_not_null`

Co-authored-by: Werner Henze <w.henze@avm.de>
2024-12-26 11:00:36 -06:00
Werner Henze fcd55ee924 Better use of std::enable_if (#1177)
* this commits adds tests that should fail, but don't

* Better use of std::enable_if

Replace the occurances of `class = std::enable_if_t<Cond>` and `typename = std::enable_if_t<Cond>` that have been identified in the previous commit with `std::enable_if_t<Cond, bool> = true`.

This commit is inspired by #1174, which changed one occurance in the owner header. This commit is aimed to fix all remaining occurances.

* fix failing checks

- core.cxx_gsl aktualisiert auf [](https://gitlab.avm.de/fos/repos/core.cxx_gsl/-/commit/)
- plc.access_lib aktualisiert auf [](https://gitlab.avm.de/fos/repos/plc.access_lib/-/commit/)
- plc.common aktualisiert auf [](https://gitlab.avm.de/fos/repos/plc.common/-/commit/)
- plc.daemon aktualisiert auf [](https://gitlab.avm.de/fos/repos/plc.daemon/-/commit/)
-

Test Plan:
-

---------

Co-authored-by: Werner Henze <werner.henze+gitcommits@posteo.de>
Co-authored-by: Werner Henze <w.henze@avm.de>
2024-12-23 10:40:22 -06:00
Werner Henze b8ac820fe1 Force unit tests for byte to use GSL implementation (#1176)
When setting `GSL_USE_STD_BYTE` to zero, then the unit test does not compile because `byte` is ambiguous (could be `std::byte` or `gsl::byte`). So `gsl::` prefix is needed for `byte`.
It looks like the unit tests never ran on a platform where `gsl::byte` is not based on `std::byte`.
It does not make much sense to test `std::byte` for compliance, so make the unit tests based on the GSL implementation of `gsl::byte` (`#define GSL_USE_STD_BYTE 0`).

Co-authored-by: Werner Henze <werner.henze+gitcommits@posteo.de>
2024-12-18 10:16:36 -06:00
Carson Radtke 272463043e introduce gsl::swap for swapping gsl::not_null (#1160)
fixes: https://github.com/microsoft/GSL/issues/1129

* create gsl::swap<T>(T&, T&) which wraps std::swap
* specialize gsl::swap<T>(gsl::not_null<T>&, gsl::not_null<T>&)
* add tests
2024-12-13 15:16:53 -06:00
Carson Radtke 8a0e3d8a9b fix: direct-init const ref instead of list-init (#1175)
fixes: https://github.com/microsoft/GSL/issues/1162

gcc has a bug that generates a call to the copy constructor when list
initializing a const reference. This PR offers a workaround which is to
direct initialize the value.

see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117900
2024-12-13 10:22:16 -06:00
Alberto La Rocca aed09c41b6 Fix SFINAE on gsl::owner. (#1174)
`std::enable_if_t` must not be used as a default template argument, otherwise the instantiator will be able to override it freely with something that doesn't fail substitution. Instead, `std::enable_if_t` itself must be the type of the template argument.

More information in the examples here: https://en.cppreference.com/w/cpp/types/enable_if
2024-12-13 09:56:28 -06:00
Carson Radtke 4b190d2e2a Create issue templates (#1173) 2024-12-02 14:34:38 -06:00
Carson Radtke ddae9d72b6 fix stale badge links in README (#1170)
* update pipeline badge links
* add link to compilers pipeline
* add badge to vcpkg version
2024-11-25 13:06:02 -06:00
Carson Radtke 74d2bb79d4 fix various pipeline failures (#1172)
* fix failing pipeline tests
* upgrade to googletest v1.14.0 so it works with newer cmake versions
* fix android pipeline to permit new cmake versions (short-term fix)
2024-11-25 12:58:45 -06:00
Carson Radtke f8ec309118 improve performance of span_iterator w/ clang (#1166)
* improve performance of span_iterator w/ clang

Issue: #1165

Before this PR, the range-for loop was ~3300x slower. After this PR, it
is ~1.005x slower

The clang optimizer is very good at optimizing `current != end`, so
we changed to this idiom. This moves the Expects assertion into the
constructor instead of on the hot-path which is called whenever either
operator++ or operator* is called.

Note: The codegen for the assertion is still a missed optimization,
but less worrisome as it only happens once per iterator.

Note: benchmarks on M1 Macbook Pro w/ Apple Clang 16.0.0
2024-11-12 15:41:21 -06:00
47 changed files with 3467 additions and 840 deletions
+1 -1
View File
@@ -31,4 +31,4 @@ AlignConsecutiveAssignments: false
AlignTrailingComments: true
SpaceAfterCStyleCast: true
CommentPragmas: '^ NO-FORMAT:'
WhitespaceSensitiveMacros: [GSL_SUPPRESS]
+29
View File
@@ -0,0 +1,29 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'Status: Open, Type: Bug'
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
```c++
#include <gsl>
// your repro here: ...
```
**Expected behavior**
A clear and concise description of what you expected to happen.
**Spec (please complete the following information):**
- OS: [e.g. Windows]
- Compiler: [e.g. MSVC]
- C++ Version: [e.g. C++20]
**Additional context**
Add any other context about the problem here.
+84
View File
@@ -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.
+3 -3
View File
@@ -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
@@ -38,7 +38,7 @@ jobs:
echo "Emulator starting in background"
- name: Configure
run: cmake -Werror=dev -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_LATEST_HOME/build/cmake/android.toolchain.cmake -DANDROID_PLATFORM=16 -DANDROID_ABI=x86_64 -DCMAKE_BUILD_TYPE=Debug ..
run: cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_LATEST_HOME/build/cmake/android.toolchain.cmake -DANDROID_PLATFORM=16 -DANDROID_ABI=x86_64 -DCMAKE_BUILD_TYPE=Debug ..
- name: Build
run: cmake --build . --parallel
+39
View File
@@ -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
+16 -39
View File
@@ -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' }}
+1 -1
View File
@@ -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
+39 -37
View File
@@ -17,49 +17,56 @@ jobs:
gcc:
strategy:
matrix:
gcc_version: [ 10, 11, 12 ]
gcc_version: [ 12, 13, 14 ]
build_type: [ Debug, Release ]
cxx_version: [ 14, 17, 20, 23 ]
exclude:
- gcc_version: 10
# https://github.com/google/googletest/issues/4232
# Looks like GoogleTest is not interested in making version 1.14
# work with gcc-12.
- gcc_version: 12
cxx_version: 20
- gcc_version: 12
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:
matrix:
clang_version: [ 13, 14, 15 ]
clang_version: [ 16, 17, 18 ]
build_type: [ Debug, Release ]
cxx_version: [ 14, 17, 20, 23 ]
exclude:
# https://github.com/llvm/llvm-project/issues/93734
# Looks like clang fixed this issue in clang-18, but won't backport
# the fix.
- clang_version: 17
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: [ '14.3.1', '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
@@ -67,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) || '' }}
+21
View File
@@ -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
+5 -4
View File
@@ -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
+32
View File
@@ -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
View File
@@ -1,5 +1,5 @@
CMakeFiles
build
build*/
tests/CMakeFiles
tests/Debug
*.opensdf
+1 -1
View File
@@ -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)
+459
View File
@@ -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"
}
]
}
-18
View File
@@ -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"
}
]
}
+17 -12
View File
@@ -1,5 +1,6 @@
# GSL: Guidelines Support Library
[![Build Status](https://dev.azure.com/cppstat/GSL/_apis/build/status/microsoft.GSL?branchName=main)](https://dev.azure.com/cppstat/GSL/_build/latest?definitionId=1&branchName=main)
[![CI](https://github.com/Microsoft/GSL/actions/workflows/compilers.yml/badge.svg)](https://github.com/microsoft/GSL/actions/workflows/compilers.yml?query=branch%3Amain)
[![vcpkg](https://img.shields.io/vcpkg/v/ms-gsl)](https://vcpkg.io/en/package/ms-gsl)
The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the
[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](https://isocpp.org).
@@ -39,22 +40,18 @@ span_p | &#x26
[u32zstring](docs/headers.md#user-content-H-zstring) | &#x2611; | An alias to `basic_zstring` with dynamic extent and a char type of `char32_t`
[cu32zstring](docs/headers.md#user-content-H-zstring) | &#x2611; | An alias to `basic_zstring` with dynamic extent and a char type of `const char32_t`
[**2. Owners**][cg-owners] | |
[unique_ptr](docs/headers.md#user-content-H-pointers-unique_ptr) | &#x2611; | An alias to `std::unique_ptr`
[shared_ptr](docs/headers.md#user-content-H-pointers-shared_ptr) | &#x2611; | An alias to `std::shared_ptr`
stack_array | &#x2610; | A stack-allocated array
dyn_array | &#x2610; | A heap-allocated array
dyn_array | &#x2611; | A heap-allocated array
[**3. Assertions**][cg-assertions] | |
[Expects](docs/headers.md#user-content-H-assert-expects) | &#x2611; | A precondition assertion; on failure it terminates
[Ensures](docs/headers.md#user-content-H-assert-ensures) | &#x2611; | A postcondition assertion; on failure it terminates
[**4. Utilities**][cg-utilities] | |
move_owner | &#x2610; | A helper function that moves one `owner` to the other
[byte](docs/headers.md#user-content-H-byte-byte) | &#x2611; | Either an alias to `std::byte` or a byte type
[final_action](docs/headers.md#user-content-H-util-final_action) | &#x2611; | A RAII style class that invokes a functor on its destruction
[finally](docs/headers.md#user-content-H-util-finally) | &#x2611; | 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) | &#x2611; | 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) | &#x2611; | A macro that takes an argument and turns it into `[[gsl::suppress(x)]]` or `[[gsl::suppress("x")]]` depending on the compiler.
[[implicit]] | &#x2610; | A "marker" to put on single-argument constructors to explicitly make them non-explicit
[index](docs/headers.md#user-content-H-util-index) | &#x2611; | A type to use for all container and array indexing (currently an alias for `std::ptrdiff_t`)
joining_thread | &#x2610; | A RAII style version of `std::thread` that joins
[narrow](docs/headers.md#user-content-H-narrow-narrow) | &#x2611; | A checked version of `narrow_cast`; it can throw [narrowing_error](docs/headers.md#user-content-H-narrow-narrowing_error)
[narrow_cast](docs/headers.md#user-content-H-util-narrow_cast) | &#x2611; | A narrowing cast for values and a synonym for `static_cast`
[narrowing_error](docs/headers.md#user-content-H-narrow-narrowing_error) | &#x2611; | A custom exception type thrown by [narrow](docs/headers.md#user-content-H-narrow-narrow)
@@ -76,6 +73,14 @@ cu16string_span | &#x2610; | Deprecated. An alias to `basic
u32string_span | &#x2610; | Deprecated. An alias to `basic_string_span` with a char type of `char32_t`
cu32string_span | &#x2610; | Deprecated. An alias to `basic_string_span` with a char type of `const char32_t`
## The following features have been adopted by WG21. They are deprecated in GSL.
Feature | Deprecated Since | Notes
------------------------------------------------------------------|------------------|------
[unique_ptr](docs/headers.md#user-content-H-pointers-unique_ptr) | C++11 | Use std::unique_ptr instead.
[shared_ptr](docs/headers.md#user-content-H-pointers-shared_ptr) | C++11 | Use std::shared_ptr instead.
[byte](docs/headers.md#user-content-H-byte-byte) | C++17 | Use std::byte instead.
joining_thread | C++20 (Note: Not yet implemented in GSL) | Use std::jthread instead.
This is based on [CppCoreGuidelines semi-specification](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#gsl-guidelines-support-library).
[cg-views]: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#gslview-views
@@ -92,9 +97,9 @@ Below is a table showing the versions currently being tested (also see [.github/
Compiler |Toolset Versions Currently Tested
:------- |--:
GCC | 10, 11, 12
GCC | 12, 13, 14
XCode | 14.3.1, 15.4
Clang | 13, 14, 15
Clang | 16, 17, 18
Visual Studio with MSVC | VS2019, VS2022
Visual Studio with LLVM | VS2019, VS2022
@@ -106,8 +111,8 @@ If you successfully port GSL to another platform, we would love to hear from you
Target | CI/CD Status
:------- | -----------:
iOS | ![CI_iOS](https://github.com/microsoft/GSL/workflows/CI_iOS/badge.svg)
Android | ![CI_Android](https://github.com/microsoft/GSL/workflows/CI_Android/badge.svg)
iOS | [![CI_iOS](https://github.com/microsoft/GSL/workflows/CI_iOS/badge.svg?branch=main)](https://github.com/microsoft/GSL/actions/workflows/ios.yml?query=branch%3Amain)
Android | [![CI_Android](https://github.com/microsoft/GSL/workflows/CI_Android/badge.svg?branch=main)](https://github.com/microsoft/GSL/actions/workflows/android.yml?query=branch%3Amain)
Note: These CI/CD steps are run with each pull request, however failures in them are non-blocking.
@@ -197,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
)
+250 -39
View File
@@ -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,21 +87,21 @@ 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
```cpp
template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
template <class IntegerType, std::enable_if_t<std::is_integral<IntegerType>::value, bool> = true>
constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept;
template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
template <class IntegerType, std::enable_if_t<std::is_integral<IntegerType>::value, bool> = true>
constexpr byte operator<<(byte b, IntegerType shift) noexcept;
template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
template <class IntegerType, std::enable_if_t<std::is_integral<IntegerType>::value, bool> = true>
constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept;
template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
template <class IntegerType, std::enable_if_t<std::is_integral<IntegerType>::value, bool> = true>
constexpr byte operator>>(byte b, IntegerType shift) noexcept;
```
@@ -134,7 +135,7 @@ constexpr byte operator~(byte b) noexcept;
Bitwise negation of a `byte`. Flips all bits. Zeroes become ones, ones become zeroes.
```cpp
template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
template <class IntegerType, std::enable_if_t<std::is_integral<IntegerType>::value, bool> = true>
constexpr IntegerType to_integer(byte b) noexcept;
```
@@ -155,6 +156,178 @@ 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 between assignments.
- [`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 until the object is copy-assigned.
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 replaces the contents with copies of the elements in `other`.
Move construction and move assignment 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.
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 +337,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 +353,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 +371,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 +386,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 classs 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 classs destructor](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rc-dtor-release).
### <a name="H-pointers-not_null" />`gsl::not_null`
@@ -222,7 +395,15 @@ 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
```cpp
using element_type = T;
```
The type of the pointer or smart pointer that is managed by this object.
#### Member functions
@@ -270,7 +451,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
@@ -292,7 +473,13 @@ 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_); }
```
Swaps contents with another `gsl::not_null` object.
#### Non-member functions
@@ -303,6 +490,13 @@ auto make_not_null(T&& t) noexcept;
Creates a `gsl::not_null` object, deducing the target type from the type of the argument.
```cpp
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);
```
Swaps the contents of two `gsl::not_null` objects.
```cpp
template <class T, class U>
auto operator==(const not_null<T>& lhs,
@@ -354,7 +548,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
@@ -391,22 +585,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
@@ -663,10 +871,6 @@ template <class Container>
constexpr span<typename Container::value_type> make_span(Container& cont);
template <class Container>
constexpr span<const typename Container::value_type> make_span(const Container& cont);
template <class Ptr>
constexpr span<typename Ptr::element_type> make_span(Ptr& cont, std::size_t count);
template <class Ptr>
constexpr span<typename Ptr::element_type> make_span(Ptr& cont);
```
Utility function for creating a `span` with [`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent) from
@@ -753,14 +957,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)
@@ -776,7 +980,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`
@@ -787,7 +991,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
@@ -827,7 +1031,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>
@@ -858,3 +1062,10 @@ constexpr auto at(std::span<T, extent> sp, const index i) -> decltype(sp[sp.size
This overload returns a reference to the `i`s element of the `std::span` `sp`. It [`Expects`](#user-content-H-assert-expects) that the provided index is within the bounds of the array.
For [`gsl::at`](#user-content-H-span_ext-at) for [`gsl::span`](#user-content-H-span-span) see header [`span_ext`](#user-content-H-span_ext).
```cpp
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);
```
Swaps the contents of two objects. Exists only to specialize `gsl::swap<T>(gsl::not_null<T>&, gsl::not_null<T>&)`.
+19
View File
@@ -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.
+3 -5
View File
@@ -17,8 +17,8 @@
#ifndef GSL_ALGORITHM_H
#define GSL_ALGORITHM_H
#include "assert" // for Expects
#include "span" // for dynamic_extent, span
#include "./assert" // for Expects
#include "./span" // for dynamic_extent, span
#include <algorithm> // for copy_n
#include <cstddef> // for ptrdiff_t
@@ -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
View File
@@ -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);
+33 -15
View File
@@ -17,6 +17,8 @@
#ifndef GSL_BYTE_H
#define GSL_BYTE_H
#include "./util" // for GSL_DEPRECATED
#include <type_traits>
#ifdef _MSC_VER
@@ -80,7 +82,16 @@ namespace gsl
{
#if GSL_USE_STD_BYTE
using 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;
using std::to_integer;
#else // GSL_USE_STD_BYTE
@@ -91,25 +102,33 @@ enum class byte_may_alias byte : unsigned char
{
};
template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
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
{
return b = byte(static_cast<unsigned char>(b) << shift);
}
template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
template <class IntegerType, std::enable_if_t<std::is_integral<IntegerType>::value, bool> = true>
constexpr byte operator<<(byte b, IntegerType shift) noexcept
{
return byte(static_cast<unsigned char>(b) << shift);
}
template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
template <class IntegerType, std::enable_if_t<std::is_integral<IntegerType>::value, bool> = true>
constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept
{
return b = byte(static_cast<unsigned char>(b) >> shift);
}
template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
template <class IntegerType, std::enable_if_t<std::is_integral<IntegerType>::value, bool> = true>
constexpr byte operator>>(byte b, IntegerType shift) noexcept
{
return byte(static_cast<unsigned char>(b) >> shift);
@@ -147,7 +166,7 @@ constexpr byte operator^(byte l, byte r) noexcept
constexpr byte operator~(byte b) noexcept { return byte(~static_cast<unsigned char>(b)); }
template <class IntegerType, class = std::enable_if_t<std::is_integral<IntegerType>::value>>
template <class IntegerType, std::enable_if_t<std::is_integral<IntegerType>::value, bool> = true>
constexpr IntegerType to_integer(byte b) noexcept
{
return static_cast<IntegerType>(b);
@@ -156,22 +175,21 @@ 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 byte to_byte(T t) noexcept
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.");
return byte(t);
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);
}
template <int I>
constexpr byte to_byte() noexcept
constexpr gsl::impl::byte to_byte() noexcept
{
static_assert(I >= 0 && I <= 255,
"gsl::byte only has 8 bits of storage, values must be in range 0-255");
return static_cast<byte>(I);
return static_cast<gsl::impl::byte>(I);
}
} // namespace gsl
+439
View File
@@ -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) */
+8 -8
View File
@@ -18,16 +18,16 @@
#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()
#include "./narrow" // narrow()
#endif
// IWYU pragma: end_exports
+20 -26
View File
@@ -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;
}
+83 -48
View File
@@ -1,3 +1,4 @@
// -*- C++ -*-
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
@@ -17,13 +18,14 @@
#ifndef GSL_POINTERS_H
#define GSL_POINTERS_H
#include "assert" // for Ensures, Expects
#include "./assert" // for Ensures, Expects
#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
@@ -47,35 +49,40 @@ 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
//
// GSL.owner: ownership pointers
//
using std::shared_ptr;
using std::unique_ptr;
template <typename... Ts>
using shared_ptr GSL_DEPRECATED("Use std::shared_ptr instead") = std::shared_ptr<Ts...>;
template <typename... Ts>
using unique_ptr GSL_DEPRECATED("Use std::unique_ptr instead") = std::unique_ptr<Ts...>;
//
// 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
//
template <class T, class = std::enable_if_t<std::is_pointer<T>::value>>
template <class T, std::enable_if_t<std::is_pointer<T>::value, bool> = true>
using owner = T;
//
@@ -98,26 +105,32 @@ class not_null
public:
static_assert(details::is_comparable_to_nullptr<T>::value, "T cannot be compared to nullptr.");
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;
not_null& operator=(const not_null& other) = default;
constexpr details::value_or_reference_return_t<T> get() const
noexcept(noexcept(details::value_or_reference_return_t<T>{std::declval<T&>()}))
noexcept(noexcept(details::value_or_reference_return_t<T>(std::declval<T&>())))
{
return ptr_;
}
@@ -139,10 +152,20 @@ public:
not_null& operator-=(std::ptrdiff_t) = delete;
void operator[](std::ptrdiff_t) const = delete;
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) noexcept
{
a.swap(b);
}
template <class T>
auto make_not_null(T&& t) noexcept
{
@@ -159,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());
@@ -216,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>
@@ -267,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) : 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) : not_null<T>(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) : 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) : 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)
@@ -323,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>
@@ -331,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
+51 -66
View File
@@ -1,3 +1,4 @@
// -*- C++ -*-
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
@@ -17,10 +18,10 @@
#ifndef GSL_SPAN_H
#define GSL_SPAN_H
#include "assert" // for Expects
#include "byte" // for byte
#include "span_ext" // for span specialization of gsl::at and other span-related extensions
#include "util" // for narrow_cast
#include "./assert" // for Expects
#include "./byte" // for gsl::impl::byte
#include "./span_ext" // for span specialization of gsl::at and other span-related extensions
#include "./util" // for narrow_cast
#include <array> // for array
#include <cstddef> // for ptrdiff_t, size_t, nullptr_t
@@ -140,7 +141,9 @@ namespace details
constexpr span_iterator(pointer begin, pointer end, pointer current)
: begin_(begin), end_(end), current_(current)
{}
{
Expects(begin_ <= current_ && current <= end_);
}
constexpr operator span_iterator<const Type>() const noexcept
{
@@ -149,24 +152,19 @@ namespace details
constexpr reference operator*() const noexcept
{
Expects(begin_ && end_);
Expects(begin_ <= current_ && current_ < end_);
Expects(current_ != end_);
return *current_;
}
constexpr pointer operator->() const noexcept
{
Expects(begin_ && end_);
Expects(begin_ <= current_ && current_ < end_);
Expects(current_ != end_);
return current_;
}
constexpr span_iterator& operator++() noexcept
{
Expects(begin_ && current_ && end_);
Expects(current_ < end_);
// clang-format off
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
// clang-format on
Expects(current_ != end_);
GSL_SUPPRESS(bounds.1)
++current_;
return *this;
}
@@ -180,8 +178,7 @@ namespace details
constexpr span_iterator& operator--() noexcept
{
Expects(begin_ && end_);
Expects(begin_ < current_);
Expects(begin_ != current_);
--current_;
return *this;
}
@@ -198,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;
}
@@ -223,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;
}
@@ -317,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
@@ -334,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
@@ -351,7 +342,8 @@ namespace details
template <typename Ptr>
friend struct std::pointer_traits;
};
}} // namespace gsl::details
} // namespace details
} // namespace gsl
namespace std
{
@@ -366,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
{
@@ -584,27 +579,28 @@ public:
template <std::size_t Count>
constexpr span<element_type, Count> first() const noexcept
{
static_assert(Extent == dynamic_extent || Count <= Extent,
"first() cannot extract more elements from a span than it contains.");
Expects(Count <= size());
return span<element_type, Count>{data(), Count};
}
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.");
Expects(Count <= size());
return span<element_type, Count>{data() + (size() - Count), Count};
}
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 ||
Count <= Extent - Offset)),
"subspan() cannot extract more elements from a span than it contains.");
Expects((size() >= Offset) && (Count == dynamic_extent || (Count <= size() - Offset)));
using type =
typename details::calculate_subspan_type<ElementType, Extent, Offset, Count>::type;
@@ -637,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());
@@ -664,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};
}
@@ -688,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
@@ -747,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
{
@@ -762,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>
@@ -782,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;
@@ -819,28 +807,25 @@ namespace details
// [span.objectrep], views of object representation
template <class ElementType, std::size_t Extent>
span<const byte, details::calculate_byte_size<ElementType, Extent>::value>
span<const gsl::impl::byte, details::calculate_byte_size<ElementType, Extent>::value>
as_bytes(span<ElementType, Extent> s) noexcept
{
using type = span<const 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
return type{reinterpret_cast<const byte*>(s.data()), s.size_bytes()};
GSL_SUPPRESS(type.1)
return type{reinterpret_cast<const gsl::impl::byte*>(s.data()), s.size_bytes()};
}
template <class ElementType, std::size_t Extent,
std::enable_if_t<!std::is_const<ElementType>::value, int> = 0>
span<byte, details::calculate_byte_size<ElementType, Extent>::value>
span<gsl::impl::byte, details::calculate_byte_size<ElementType, Extent>::value>
as_writable_bytes(span<ElementType, Extent> s) noexcept
{
using type = span<byte, details::calculate_byte_size<ElementType, Extent>::value>;
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
return type{reinterpret_cast<byte*>(s.data()), s.size_bytes()};
GSL_SUPPRESS(type.1)
return type{reinterpret_cast<gsl::impl::byte*>(s.data()), s.size_bytes()};
}
} // namespace gsl
+5 -5
View File
@@ -27,8 +27,8 @@
//
///////////////////////////////////////////////////////////////////////////////
#include "assert" // GSL_KERNEL_MODE
#include "util" // for narrow_cast, narrow
#include "./assert" // GSL_KERNEL_MODE
#include "./util" // for narrow_cast, narrow
#include <cstddef> // for ptrdiff_t, size_t
#include <utility>
@@ -123,14 +123,14 @@ constexpr span<const typename Container::value_type> make_span(const Container&
}
template <class Ptr>
[[deprecated("This function is deprecated. See GSL issue #1092.")]]
GSL_DEPRECATED("This function is deprecated. See GSL issue #1092.")
constexpr span<typename Ptr::element_type> make_span(Ptr& cont, std::size_t count)
{
return span<typename Ptr::element_type>(cont, count);
}
template <class Ptr>
[[deprecated("This function is deprecated. See GSL issue #1092.")]]
GSL_DEPRECATED("This function is deprecated. See GSL issue #1092.")
constexpr span<typename Ptr::element_type> make_span(Ptr& cont)
{
return span<typename Ptr::element_type>(cont);
@@ -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
-4
View File
@@ -1,4 +0,0 @@
#pragma once
#pragma message( \
"This header will soon be removed. Use <gsl/zstring> instead of <gsl/string_span>")
#include "zstring"
+89 -28
View File
@@ -1,3 +1,4 @@
// -*- C++ -*-
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
@@ -17,12 +18,12 @@
#ifndef GSL_UTIL_H
#define GSL_UTIL_H
#include "assert" // for Expects
#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
@@ -60,6 +61,39 @@
#define GSL_INLINE
#endif
#if defined(__has_cpp_attribute)
#if __has_cpp_attribute(deprecated)
#define GSL_DEPRECATED(msg) [[deprecated(msg)]]
#endif // __has_cpp_attribute(deprecated)
#endif // defined(__has_cpp_attribute)
#if !defined(GSL_DEPRECATED)
#if defined(__cplusplus)
#if __cplusplus >= 201309L
#define GSL_DEPRECATED(msg) [[deprecated(msg)]]
#endif // __cplusplus >= 201309L
#endif // defined(__cplusplus)
#endif // !defined(GSL_DEPRECATED)
#if !defined(GSL_DEPRECATED)
#if defined(_MSC_VER)
#define GSL_DEPRECATED(msg) __declspec(deprecated(msg))
#elif defined(__GNUC__)
#define GSL_DEPRECATED(msg) __attribute__((deprecated(msg)))
#endif // defined(_MSC_VER)
#endif // !defined(GSL_DEPRECATED)
#if !defined(GSL_DEPRECATED)
#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
{
//
@@ -69,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;
@@ -101,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));
}
@@ -113,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());
@@ -137,15 +194,19 @@ 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);
}
#if defined(__cpp_lib_span) && __cpp_lib_span >= 202002L
template <class T, std::size_t extent = std::dynamic_extent>
constexpr auto at(std::span<T, extent> sp, const index i) -> decltype(sp[sp.size()])
+2 -2
View File
@@ -17,9 +17,9 @@
#ifndef GSL_ZSTRING_H
#define GSL_ZSTRING_H
#include "span_ext" // for dynamic_extent
#include "./span_ext" // for dynamic_extent
#include <cstddef> // for size_t, nullptr_t
#include <cstddef> // for size_t, nullptr_t
namespace gsl
{
+13
View File
@@ -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"
)
+18
View File
@@ -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 {} \;
+15 -17
View File
@@ -110,6 +110,7 @@ if(MSVC) # MSVC or simulating MSVC
-Wno-shift-sign-overflow # GTest gtest-port.h
-Wno-undef # GTest
-Wno-used-but-marked-unused # GTest EXPECT_DEATH
-Wno-switch-default # GTest EXPECT_DEATH
$<$<EQUAL:${GSL_CXX_STANDARD},14>: # no support for [[maybe_unused]]
-Wno-unused-member-function
-Wno-unused-variable
@@ -150,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
@@ -198,27 +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
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
@@ -284,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
View File
@@ -1,10 +1,10 @@
cmake_minimum_required(VERSION 3.0.2)
cmake_minimum_required(VERSION 3.13)
project(googletest-download NONE)
include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG 1b18723e874b256c1e39378c6774a90701d70f7a
GIT_TAG v1.14.0
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
CONFIGURE_COMMAND ""
+1 -1
View File
@@ -188,7 +188,7 @@ TEST(algorithm_tests, incompatible_type)
span<int> src_span_dyn(src);
span<int, 4> src_span_static(src);
span<int*> dst_span_dyn(dst);
span<int*, 4> dst_span_static(dst);
span<int*, 4> dst_span_static(gsl::make_span(dst));
// every line should produce a compilation error
copy(src_span_dyn, dst_span_dyn);
+2 -2
View File
@@ -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;
+57 -13
View File
@@ -16,8 +16,12 @@
#include <gtest/gtest.h>
#define GSL_USE_STD_BYTE 0
#include <gsl/byte> // for to_byte, to_integer, byte, operator&, ope...
#include <type_traits>
#include <utility>
using namespace std;
using namespace gsl;
@@ -33,28 +37,28 @@ int modify_both(gsl::byte& b, int& i)
TEST(byte_tests, construction)
{
{
const byte b = static_cast<byte>(4);
const gsl::byte b = static_cast<gsl::byte>(4);
EXPECT_TRUE(static_cast<unsigned char>(b) == 4);
}
{
const byte b = byte(12);
const gsl::byte b = gsl::byte(12);
EXPECT_TRUE(static_cast<unsigned char>(b) == 12);
}
{
const byte b = to_byte<12>();
const gsl::byte b = to_byte<12>();
EXPECT_TRUE(static_cast<unsigned char>(b) == 12);
}
{
const unsigned char uc = 12;
const byte b = to_byte(uc);
const gsl::byte b = to_byte(uc);
EXPECT_TRUE(static_cast<unsigned char>(b) == 12);
}
#if defined(__cplusplus) && (__cplusplus >= 201703L)
{
const byte b{14};
const gsl::byte b{14};
EXPECT_TRUE(static_cast<unsigned char>(b) == 14);
}
#endif
@@ -63,14 +67,16 @@ TEST(byte_tests, construction)
to_byte(char{});
to_byte(3);
to_byte(3u);
to_byte<-1>();
to_byte<256u>();
#endif
}
TEST(byte_tests, bitwise_operations)
{
const byte b = to_byte<0xFF>();
const gsl::byte b = to_byte<0xFF>();
byte a = to_byte<0x00>();
gsl::byte a = to_byte<0x00>();
EXPECT_TRUE((b | a) == to_byte<0xFF>());
EXPECT_TRUE(a == to_byte<0x00>());
@@ -104,7 +110,7 @@ TEST(byte_tests, bitwise_operations)
TEST(byte_tests, to_integer)
{
const byte b = to_byte<0x12>();
const gsl::byte b = to_byte<0x12>();
EXPECT_TRUE(0x12 == gsl::to_integer<char>(b));
EXPECT_TRUE(0x12 == gsl::to_integer<short>(b));
@@ -123,12 +129,50 @@ TEST(byte_tests, to_integer)
TEST(byte_tests, aliasing)
{
int i{0};
const int res = modify_both(reinterpret_cast<byte&>(i), i);
const int res = modify_both(reinterpret_cast<gsl::byte&>(i), i);
EXPECT_TRUE(res == i);
}
} // namespace
#if __cplusplus >= 201703l
using std::void_t;
#else // __cplusplus >= 201703l
template <class...>
using void_t = void;
#endif // __cplusplus < 201703l
#ifdef CONFIRM_COMPILATION_ERRORS
copy(src_span_static, dst_span_static);
#endif
template <typename U, typename = void>
static constexpr bool LShiftCompilesFor = false;
template <typename U>
static constexpr bool LShiftCompilesFor<
U, void_t<decltype(gsl::operator<< <float>(declval<gsl::byte>(), declval<U>()))>> = true;
static_assert(!LShiftCompilesFor<float>, "!LShiftCompilesFor<float>");
template <typename U, typename = void>
static constexpr bool RShiftCompilesFor = false;
template <typename U>
static constexpr bool RShiftCompilesFor<
U, void_t<decltype(gsl::operator>> <U>(declval<gsl::byte>(), declval<U>()))>> = true;
static_assert(!RShiftCompilesFor<float>, "!RShiftCompilesFor<float>");
template <typename U, typename = void>
static constexpr bool LShiftAssignCompilesFor = false;
template <typename U>
static constexpr bool LShiftAssignCompilesFor<
U, void_t<decltype(gsl::operator<<= <U>(declval<gsl::byte&>(), declval<U>()))>> = true;
static_assert(!LShiftAssignCompilesFor<float>, "!LShiftAssignCompilesFor<float>");
template <typename U, typename = void>
static constexpr bool RShiftAssignCompilesFor = false;
template <typename U>
static constexpr bool RShiftAssignCompilesFor<
U, void_t<decltype(gsl::operator>>= <U>(declval<gsl::byte&>(), declval<U>()))>> = true;
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_assert(!ToIntegerCompilesFor<float>, "!ToIntegerCompilesFor<float>");
} // namespace
+75
View File
@@ -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));
}
+684
View File
@@ -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 */
+149 -61
View File
@@ -18,17 +18,25 @@
#include <gsl/pointers> // for not_null, operator<, operator<=, operator>
#include <algorithm> // for addressof
#include <cstdint> // for uint16_t
#include <memory> // for shared_ptr, make_shared, operator<, opera...
#include <sstream> // for operator<<, ostringstream, basic_ostream:...
#include <string> // for basic_string, operator==, string, operator<<
#include <typeinfo> // for type_info
#include <variant> // for variant, monostate, get
#include <algorithm> // for addressof
#include <cstdint> // for uint16_t
#include <memory> // for shared_ptr, make_shared, operator<, opera...
#include <sstream> // for operator<<, ostringstream, basic_ostream:...
#include <string> // for basic_string, operator==, string, operator<<
#include <type_traits> // for declval
#include <typeinfo> // for type_info
#include <variant> // for variant, monostate, get
#include "deathTestCommon.h"
using namespace gsl;
#if __cplusplus >= 201703l
using std::void_t;
#else // __cplusplus >= 201703l
template <class...>
using void_t = void;
#endif // __cplusplus < 201703l
struct MyBase
{
};
@@ -61,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";
}
@@ -71,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";
}
@@ -81,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";
}
@@ -91,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";
}
@@ -101,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";
}
@@ -111,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";
}
@@ -129,28 +125,47 @@ 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; }
} // namespace
template <typename U, typename = void>
static constexpr bool CtorCompilesFor_A = false;
template <typename U>
static constexpr bool
CtorCompilesFor_A<U, void_t<decltype(gsl::not_null<void*>{std::declval<U>()})>> = true;
template <typename U, int N, typename = void>
static constexpr bool CtorCompilesFor_B = false;
template <typename U, int N>
static constexpr bool CtorCompilesFor_B<U, N, void_t<decltype(gsl::not_null<U>{N})>> = true;
template <typename U, typename = void>
static constexpr bool DefaultCtorCompilesFor = false;
template <typename U>
static constexpr bool DefaultCtorCompilesFor<U, void_t<decltype(gsl::not_null<U>{})>> = true;
template <typename U, typename = void>
static constexpr bool CtorCompilesFor_C = false;
template <typename U>
static constexpr bool
CtorCompilesFor_C<U, void_t<decltype(gsl::not_null<U*>{std::declval<std::unique_ptr<U>>()})>> =
true;
TEST(notnull_tests, TestNotNullConstructors)
{
{
#ifdef CONFIRM_COMPILATION_ERRORS
not_null<int*> p = nullptr; // yay...does not compile!
not_null<std::vector<char>*> p1 = 0; // yay...does not compile!
not_null<int*> p2; // yay...does not compile!
std::unique_ptr<int> up = std::make_unique<int>(120);
not_null<int*> p3 = up;
static_assert(CtorCompilesFor_A<void*>, "CtorCompilesFor_A<void*>");
static_assert(!CtorCompilesFor_A<std::nullptr_t>, "!CtorCompilesFor_A<std::nullptr_t>");
static_assert(!CtorCompilesFor_B<void*, 0>, "!CtorCompilesFor_B<void*, 0>");
static_assert(!DefaultCtorCompilesFor<void*>, "!DefaultCtorCompilesFor<void*>");
static_assert(!CtorCompilesFor_C<int>, "CtorCompilesFor_C<int>");
#ifdef CONFIRM_COMPILATION_ERRORS
// Forbid non-nullptr assignable types
not_null<std::vector<int>> f(std::vector<int>{1});
not_null<int> z(10);
@@ -178,6 +193,14 @@ TEST(notnull_tests, TestNotNullConstructors)
EXPECT_DEATH((not_null<decltype(pi)>(pi)), expected);
}
{
// from unique pointer
not_null<std::unique_ptr<int>> x(
std::make_unique<int>(10)); // unique_ptr<int> is nullptr assignable
EXPECT_DEATH((not_null<std::unique_ptr<int>>(std::unique_ptr<int>{})), expected);
}
{
// from pointer to local
int t = 42;
@@ -268,6 +291,27 @@ TEST(notnull_tests, TestNotNullostream)
ostream_helper<std::string>("string");
}
template <typename U, typename V, typename = void>
static constexpr bool AssignmentCompilesFor = false;
template <typename U, typename V>
static constexpr bool
AssignmentCompilesFor<U, V,
void_t<decltype(std::declval<gsl::not_null<U*>&>().operator=(
std::declval<gsl::not_null<V*>&>()))>> = true;
template <typename U, typename V, typename = void>
static constexpr bool SCastCompilesFor = false;
template <typename U, typename V>
static constexpr bool
SCastCompilesFor<U, V, void_t<decltype(static_cast<U*>(std::declval<gsl::not_null<V*>&>()))>> =
true;
template <typename U, typename V, typename = void>
static constexpr bool RCastCompilesFor = false;
template <typename U, typename V>
static constexpr bool RCastCompilesFor<
U, V, void_t<decltype(reinterpret_cast<U*>(std::declval<gsl::not_null<V*>&>()))>> = true;
TEST(notnull_tests, TestNotNullCasting)
{
MyBase base;
@@ -280,15 +324,30 @@ TEST(notnull_tests, TestNotNullCasting)
q = p; // allowed with heterogeneous copy ctor
EXPECT_TRUE(q == p);
#ifdef CONFIRM_COMPILATION_ERRORS
q = u; // no viable conversion possible between MyBase* and Unrelated*
p = q; // not possible to implicitly convert MyBase* to MyDerived*
static_assert(AssignmentCompilesFor<MyBase, MyDerived>,
"AssignmentCompilesFor<MyBase, MyDerived>");
static_assert(!AssignmentCompilesFor<MyBase, Unrelated>,
"!AssignmentCompilesFor<MyBase, Unrelated>");
static_assert(!AssignmentCompilesFor<Unrelated, MyDerived>,
"!AssignmentCompilesFor<Unrelated, MyDerived>");
static_assert(!AssignmentCompilesFor<MyDerived, MyBase>,
"!AssignmentCompilesFor<MyDerived, MyBase>");
static_assert(SCastCompilesFor<MyDerived, MyDerived>, "SCastCompilesFor<MyDerived, MyDerived>");
static_assert(SCastCompilesFor<MyBase, MyDerived>, "SCastCompilesFor<MyBase, MyDerived>");
static_assert(!SCastCompilesFor<MyDerived, MyBase>, "!SCastCompilesFor<MyDerived, MyBase>");
static_assert(!SCastCompilesFor<Unrelated, MyDerived>,
"!SCastCompilesFor<Unrelated, MyDerived>");
static_assert(!RCastCompilesFor<MyDerived, MyDerived>,
"!SCastCompilesFor<MyDerived, MyDerived>");
static_assert(!RCastCompilesFor<Unrelated, MyDerived>,
"!SCastCompilesFor<Unrelated, MyDerived>");
not_null<Unrelated*> r = p;
not_null<Unrelated*> s = reinterpret_cast<Unrelated*>(p);
#endif
not_null<Unrelated*> t(reinterpret_cast<Unrelated*>(p.get()));
EXPECT_TRUE(reinterpret_cast<void*>(p.get()) == reinterpret_cast<void*>(t.get()));
(void) static_cast<MyDerived*>(p);
(void) static_cast<MyBase*>(p);
}
TEST(notnull_tests, TestNotNullAssignment)
@@ -430,6 +489,18 @@ TEST(notnull_tests, TestNotNullCustomPtrComparison)
#if defined(__cplusplus) && (__cplusplus >= 201703L)
template <typename U, typename = void>
static constexpr bool TypeDeductionCtorCompilesFor = false;
template <typename U>
static constexpr bool
TypeDeductionCtorCompilesFor<U, void_t<decltype(not_null{std::declval<U>()})>> = true;
template <typename U, typename = void>
static constexpr bool TypeDeductionHelperCompilesFor = false;
template <typename U>
static constexpr bool
TypeDeductionHelperCompilesFor<U, void_t<decltype(helper(not_null{std::declval<U>()}))>> = true;
TEST(notnull_tests, TestNotNullConstructorTypeDeduction)
{
{
@@ -446,9 +517,9 @@ TEST(notnull_tests, TestNotNullConstructorTypeDeduction)
const int i = 42;
not_null x{&i};
#ifdef CONFIRM_COMPILATION_ERRORS
helper(not_null{&i});
#endif
static_assert(TypeDeductionHelperCompilesFor<int*>, "TypeDeductionHelperCompilesFor<int*>");
static_assert(!TypeDeductionHelperCompilesFor<const int*>,
"!TypeDeductionHelperCompilesFor<const int*>");
helper_const(not_null{&i});
EXPECT_TRUE(*x == 42);
@@ -470,9 +541,6 @@ TEST(notnull_tests, TestNotNullConstructorTypeDeduction)
const int* p = &i;
not_null x{p};
#ifdef CONFIRM_COMPILATION_ERRORS
helper(not_null{p});
#endif
helper_const(not_null{p});
EXPECT_TRUE(*x == 42);
@@ -507,12 +575,15 @@ TEST(notnull_tests, TestNotNullConstructorTypeDeduction)
EXPECT_DEATH(helper_const(not_null{p}), expected);
}
#ifdef CONFIRM_COMPILATION_ERRORS
{
not_null x{nullptr};
helper(not_null{nullptr});
helper_const(not_null{nullptr});
}
static_assert(TypeDeductionCtorCompilesFor<void*>, "TypeDeductionCtorCompilesFor<void*>");
#if defined(_MSC_VER) && !defined(__clang__)
// Fails on gcc, clang, xcode, VS clang with
// "error : no type named 'type' in 'std::enable_if<false>'; 'enable_if' cannot be used to
// disable this declaration"
static_assert(!TypeDeductionCtorCompilesFor<std::nullptr_t>,
"!TypeDeductionCtorCompilesFor<std::nullptr_t>");
static_assert(!TypeDeductionHelperCompilesFor<std::nullptr_t>,
"!TypeDeductionHelperCompilesFor<std::nullptr_t>");
#endif
}
@@ -528,6 +599,11 @@ TEST(notnull_tests, TestVariantEmplace)
}
#endif // #if defined(__cplusplus) && (__cplusplus >= 201703L)
template <typename U, typename = void>
static constexpr bool HelperCompilesFor = false;
template <typename U>
static constexpr bool HelperCompilesFor<U, void_t<decltype(helper(std::declval<U>()))>> = true;
TEST(notnull_tests, TestMakeNotNull)
{
{
@@ -544,9 +620,8 @@ TEST(notnull_tests, TestMakeNotNull)
const int i = 42;
const auto x = make_not_null(&i);
#ifdef CONFIRM_COMPILATION_ERRORS
helper(make_not_null(&i));
#endif
static_assert(HelperCompilesFor<gsl::not_null<int*>>,
"HelperCompilesFor<gsl::not_null<int*>>");
helper_const(make_not_null(&i));
EXPECT_TRUE(*x == 42);
@@ -568,9 +643,8 @@ TEST(notnull_tests, TestMakeNotNull)
const int* p = &i;
const auto x = make_not_null(p);
#ifdef CONFIRM_COMPILATION_ERRORS
helper(make_not_null(p));
#endif
static_assert(!HelperCompilesFor<gsl::not_null<const int*>>,
"!HelperCompilesFor<gsl::not_null<const int*>>");
helper_const(make_not_null(p));
EXPECT_TRUE(*x == 42);
@@ -645,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));
}
}
+16 -10
View File
@@ -17,10 +17,11 @@
#include <gtest/gtest.h>
#include <gsl/pointers> // for owner
#include <type_traits> // for declval
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)
@@ -32,12 +33,17 @@ TEST(owner_tests, basic_test)
delete p;
}
TEST(owner_tests, check_pointer_constraint)
{
#ifdef CONFIRM_COMPILATION_ERRORS
{
owner<int> integerTest = 10;
owner<std::shared_ptr<int>> sharedPtrTest(new int(10));
}
#endif
}
#if __cplusplus >= 201703l
using std::void_t;
#else // __cplusplus >= 201703l
template <class...>
using void_t = void;
#endif // __cplusplus < 201703l
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_assert(OwnerCompilesFor<int*>, "OwnerCompilesFor<int*>");
static_assert(!OwnerCompilesFor<int>, "!OwnerCompilesFor<int>");
static_assert(!OwnerCompilesFor<std::shared_ptr<int>>, "!OwnerCompilesFor<std::shared_ptr<int>>");
+119
View File
@@ -0,0 +1,119 @@
#include <gtest/gtest.h>
#include <gsl/pointers>
#include <functional>
#include <memory>
#include <type_traits>
#include <utility>
#if __cplusplus >= 201703l
using std::void_t;
#else // __cplusplus >= 201703l
template <class...>
using void_t = void;
#endif // __cplusplus < 201703l
namespace
{
// Custom pointer type that can be used for gsl::not_null, but for which these cannot be swapped.
struct NotMoveAssignableCustomPtr
{
NotMoveAssignableCustomPtr() = default;
NotMoveAssignableCustomPtr(const NotMoveAssignableCustomPtr&) = default;
NotMoveAssignableCustomPtr& operator=(const NotMoveAssignableCustomPtr&) = default;
NotMoveAssignableCustomPtr(NotMoveAssignableCustomPtr&&) = default;
NotMoveAssignableCustomPtr& operator=(NotMoveAssignableCustomPtr&&) = delete;
bool operator!=(std::nullptr_t) const { return true; }
int dummy{}; // Without this clang warns, that NotMoveAssignableCustomPtr() is unneeded
};
template <typename U, typename = void>
static constexpr bool SwapCompilesFor = false;
template <typename U>
static constexpr bool
SwapCompilesFor<U, void_t<decltype(gsl::swap<U>(std::declval<gsl::not_null<U>&>(),
std::declval<gsl::not_null<U>&>()))>> = true;
TEST(pointers_test, swap)
{
// taken from gh-1129:
{
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);
gsl::swap(a, b);
EXPECT_TRUE(*a == 1);
EXPECT_TRUE(*b == 0);
// Make sure our custom ptr can be used with not_null. The shared_pr is to prevent "unused"
// compiler warnings.
const auto shared_custom_ptr{std::make_shared<NotMoveAssignableCustomPtr>()};
gsl::not_null<NotMoveAssignableCustomPtr> c{*shared_custom_ptr};
EXPECT_TRUE(c.get() != nullptr);
}
{
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);
gsl::swap(a, b);
EXPECT_TRUE(*a == 1);
EXPECT_TRUE(*b == 0);
}
{
gsl::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)};
EXPECT_TRUE(*a == 0);
EXPECT_TRUE(*b == 1);
gsl::swap(a, b);
EXPECT_TRUE(*a == 1);
EXPECT_TRUE(*b == 0);
}
static_assert(!SwapCompilesFor<NotMoveAssignableCustomPtr>,
"!SwapCompilesFor<NotMoveAssignableCustomPtr>");
}
TEST(pointers_test, member_types)
{
static_assert(std::is_same<gsl::not_null<int*>::element_type, int*>::value,
"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
+11 -6
View File
@@ -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
}
@@ -1005,12 +1005,18 @@ static_assert(std::is_convertible<const std::array<int, 3>&, gsl::span<const int
"std::is_convertible<const std::array<int, 3>&, gsl::span<const int>>");
#if __cplusplus >= 201703l
using std::void_t;
#else // __cplusplus >= 201703l
template <class...>
using void_t = void;
#endif // __cplusplus < 201703l
template <typename U, typename = void>
static constexpr bool AsWritableBytesCompilesFor = false;
template <typename U>
static constexpr bool
AsWritableBytesCompilesFor<U, void_t<decltype(as_writable_bytes(declval<U>()))>> = true;
AsWritableBytesCompilesFor<U, ::void_t<decltype(as_writable_bytes(declval<U>()))>> = true;
static_assert(AsWritableBytesCompilesFor<gsl::span<int>>,
"AsWritableBytesCompilesFor<gsl::span<int>>");
@@ -1020,4 +1026,3 @@ static_assert(!AsWritableBytesCompilesFor<gsl::span<const int>>,
"!AsWritableBytesCompilesFor<gsl::span<const int>>");
static_assert(!AsWritableBytesCompilesFor<gsl::span<const int, 9>>,
"!AsWritableBytesCompilesFor<gsl::span<const int, 9>>");
#endif // __cplusplus >= 201703l
+1 -1
View File
@@ -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);
+353 -308
View File
@@ -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
@@ -48,6 +48,13 @@
using namespace gsl;
#if __cplusplus >= 201703l
using std::void_t;
#else // __cplusplus >= 201703l
template <class...>
using void_t = void;
#endif // __cplusplus < 201703l
namespace
{
@@ -62,8 +69,7 @@ struct AddressOverloaded
#if (__cplusplus > 201402L)
[[maybe_unused]]
#endif
AddressOverloaded
operator&() const
AddressOverloaded operator&() const
{
return {};
}
@@ -216,6 +222,12 @@ TEST(span_test, from_pointer_length_constructor)
TEST(span_test, from_pointer_pointer_construction)
{
// const auto terminateHandler = std::set_terminate([] {
// std::cerr << "Expected Death. from_pointer_pointer_construction";
// std::abort();
// });
// const auto expected = GetExpectedDeathString(terminateHandler);
int arr[4] = {1, 2, 3, 4};
{
@@ -245,19 +257,13 @@ TEST(span_test, from_pointer_pointer_construction)
EXPECT_TRUE(s.data() == &arr[0]);
}
// this will fail the std::distance() precondition, which asserts on MSVC debug builds
// 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);
//}
// this will fail the std::distance() precondition, which asserts on MSVC debug builds
//{
// int* p = nullptr;
// auto workaround_macro = [&]() { span<int> s{&arr[0], p}; };
// EXPECT_DEATH(workaround_macro(), expected);
//}
{
int* p = nullptr;
span<int> s{p, p};
@@ -271,19 +277,21 @@ TEST(span_test, from_pointer_pointer_construction)
EXPECT_TRUE(s.size() == 0);
EXPECT_TRUE(s.data() == nullptr);
}
// this will fail the std::distance() precondition, which asserts on MSVC debug builds
//{
// int* p = nullptr;
// auto workaround_macro = [&]() { span<int> s{&arr[0], p}; };
// EXPECT_DEATH(workaround_macro(), expected);
//}
}
template <typename U, typename V, typename = void>
static constexpr bool CtorCompilesFor = false;
template <typename U, typename V>
static constexpr bool CtorCompilesFor<U, V, void_t<decltype(U{std::declval<V>()})>> = true;
TEST(span_test, from_array_constructor)
{
int arr[5] = {1, 2, 3, 4, 5};
static_assert(!CtorCompilesFor<span<int, 6>, int[5]>, "!CtorCompilesFor<span<int, 6>, int[5]>");
static_assert(!CtorCompilesFor<span<int, 0>, int[5]>, "!CtorCompilesFor<span<int, 0>, int[5]>");
static_assert(!CtorCompilesFor<span<int>, int[2][3]>, "!CtorCompilesFor<span<int>, int[2][3]>");
{
const span<int> s{arr};
EXPECT_TRUE(s.size() == 5);
@@ -298,70 +306,28 @@ TEST(span_test, from_array_constructor)
int arr2d[2][3] = {1, 2, 3, 4, 5, 6};
#ifdef CONFIRM_COMPILATION_ERRORS
{
span<int, 6> s{arr};
}
static_assert(!CtorCompilesFor<span<int, 0>, int[2][3]>,
"!CtorCompilesFor<span<int, 0>, int[2][3]>");
static_assert(!CtorCompilesFor<span<int, 6>, int[2][3]>,
"!CtorCompilesFor<span<int, 6>, int[2][3]>");
{
span<int, 0> s{arr};
EXPECT_TRUE(s.size() == 0);
EXPECT_TRUE(s.data() == &arr[0]);
}
{
span<int> s{arr2d};
EXPECT_TRUE(s.size() == 6);
EXPECT_TRUE(s.data() == &arr2d[0][0]);
EXPECT_TRUE(s[0] == 1);
EXPECT_TRUE(s[5] == 6);
}
{
span<int, 0> s{arr2d};
EXPECT_TRUE(s.size() == 0);
EXPECT_TRUE(s.data() == &arr2d[0][0]);
}
{
span<int, 6> s{arr2d};
}
#endif
{
const span<int[3]> s{std::addressof(arr2d[0]), 1};
EXPECT_TRUE(s.size() == 1);
EXPECT_TRUE(s.data() == std::addressof(arr2d[0]));
}
int arr3d[2][3][2] = { { {1, 2}, {3, 4}, {5, 6} }, { {7, 8}, {9, 10}, {11, 12} } };
int arr3d[2][3][2] = {{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}};
#ifdef CONFIRM_COMPILATION_ERRORS
{
span<int> s{arr3d};
EXPECT_TRUE(s.size() == 12);
EXPECT_TRUE(s.data() == &arr3d[0][0][0]);
EXPECT_TRUE(s[0] == 1);
EXPECT_TRUE(s[11] == 12);
}
static_assert(!CtorCompilesFor<span<int>, int[2][3][2]>,
"!CtorCompilesFor<span<int>, int[2][3][2]>");
static_assert(!CtorCompilesFor<span<int, 0>, int[2][3][2]>,
"!CtorCompilesFor<span<int, 0>, int[2][3][2]>");
static_assert(!CtorCompilesFor<span<int, 11>, int[2][3][2]>,
"!CtorCompilesFor<span<int, 11>, int[2][3][2]>");
static_assert(!CtorCompilesFor<span<int, 12>, int[2][3][2]>,
"!CtorCompilesFor<span<int, 12>, int[2][3][2]>");
{
span<int, 0> s{arr3d};
EXPECT_TRUE(s.size() == 0);
EXPECT_TRUE(s.data() == &arr3d[0][0][0]);
}
{
span<int, 11> s{arr3d};
}
{
span<int, 12> s{arr3d};
EXPECT_TRUE(s.size() == 12);
EXPECT_TRUE(s.data() == &arr3d[0][0][0]);
EXPECT_TRUE(s[0] == 1);
EXPECT_TRUE(s[5] == 6);
}
#endif
{
const span<int[3][2]> s{std::addressof(arr3d[0]), 1};
EXPECT_TRUE(s.size() == 1);
@@ -378,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);
@@ -389,6 +355,13 @@ TEST(span_test, from_dynamic_array_constructor)
delete[] arr;
}
template <typename U, typename V, typename = void>
static constexpr bool ConversionCompilesFor = false;
template <typename U, typename V>
static constexpr bool
ConversionCompilesFor<U, V, void_t<decltype(std::declval<void (*)(U)>()(std::declval<V>()))>> =
true;
TEST(span_test, from_std_array_constructor)
{
std::array<int, 4> arr = {1, 2, 3, 4};
@@ -428,43 +401,31 @@ TEST(span_test, from_std_array_constructor)
EXPECT_TRUE(ao_arr.data() == fs.data());
}
#ifdef CONFIRM_COMPILATION_ERRORS
{
span<int, 2> s{arr};
EXPECT_TRUE(s.size() == 2);
EXPECT_TRUE(s.data() == arr.data());
static_assert(!CtorCompilesFor<span<int, 2>, std::array<int, 4>&>,
"!CtorCompilesFor<span<int, 2>, std::array<int, 4>&>");
static_assert(!CtorCompilesFor<span<const int, 2>, std::array<int, 4>&>,
"!CtorCompilesFor<span<const int, 2>, std::array<int, 4>&>");
span<const int, 2> cs{arr};
EXPECT_TRUE(cs.size() == 2);
EXPECT_TRUE(cs.data() == arr.data());
}
static_assert(!CtorCompilesFor<span<int, 0>, std::array<int, 4>&>,
"!CtorCompilesFor<span<int, 0>, std::array<int, 4>&>");
static_assert(!CtorCompilesFor<span<const int, 0>, std::array<int, 4>&>,
"!CtorCompilesFor<span<const int, 0>, std::array<int, 4>&>");
{
span<int, 0> s{arr};
EXPECT_TRUE(s.size() == 0);
EXPECT_TRUE(s.data() == arr.data());
static_assert(!CtorCompilesFor<span<int, 5>, std::array<int, 4>&>,
"!CtorCompilesFor<span<int, 5>, std::array<int, 4>&>");
span<const int, 0> cs{arr};
EXPECT_TRUE(cs.size() == 0);
EXPECT_TRUE(cs.data() == arr.data());
}
{
span<int, 5> s{arr};
}
{
auto get_an_array = []() -> std::array<int, 4> { return {1, 2, 3, 4}; };
auto take_a_span = [](span<int> s) { static_cast<void>(s); };
// try to take a temporary std::array
take_a_span(get_an_array());
}
#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
{
auto get_an_array = []() -> std::array<int, 4> { return {1, 2, 3, 4}; };
auto take_a_span = [](span<const int> s) { static_cast<void>(s); };
auto take_a_span = [](span<const int>) {};
// try to take a temporary std::array
static_assert(ConversionCompilesFor<span<const int>, std::array<int, 4>>,
"ConversionCompilesFor<span<const int>, std::array<int, 4>>");
take_a_span(get_an_array());
}
}
@@ -493,23 +454,12 @@ TEST(span_test, from_const_std_array_constructor)
EXPECT_TRUE(s.data() == ao_arr.data());
}
#ifdef CONFIRM_COMPILATION_ERRORS
{
span<const int, 2> s{arr};
EXPECT_TRUE(s.size() == 2);
EXPECT_TRUE(s.data() == arr.data());
}
{
span<const int, 0> s{arr};
EXPECT_TRUE(s.size() == 0);
EXPECT_TRUE(s.data() == arr.data());
}
{
span<const int, 5> s{arr};
}
#endif
static_assert(!CtorCompilesFor<span<const int, 2>, std::array<int, 4>&>,
"!CtorCompilesFor<span<const int, 2>, std::array<int, 4>&>");
static_assert(!CtorCompilesFor<span<const int, 0>, std::array<int, 4>&>,
"!CtorCompilesFor<span<const int, 0>, std::array<int, 4>&>");
static_assert(!CtorCompilesFor<span<int, 5>, std::array<int, 4>&>,
"!CtorCompilesFor<span<int, 5>, std::array<int, 4>&>");
{
auto get_an_array = []() -> const std::array<int, 4> { return {1, 2, 3, 4}; };
@@ -535,27 +485,14 @@ TEST(span_test, from_std_array_const_constructor)
EXPECT_TRUE(s.data() == arr.data());
}
#ifdef CONFIRM_COMPILATION_ERRORS
{
span<const int, 2> s{arr};
EXPECT_TRUE(s.size() == 2);
EXPECT_TRUE(s.data() == arr.data());
}
{
span<const int, 0> s{arr};
EXPECT_TRUE(s.size() == 0);
EXPECT_TRUE(s.data() == arr.data());
}
{
span<const int, 5> s{arr};
}
{
span<int, 4> s{arr};
}
#endif
static_assert(!CtorCompilesFor<span<const int, 2>, const std::array<int, 4>&>,
"!CtorCompilesFor<span<const int, 2>, const std::array<int, 4>&>");
static_assert(!CtorCompilesFor<span<const int, 0>, const std::array<int, 4>&>,
"!CtorCompilesFor<span<const int, 0>, const std::array<int, 4>&>");
static_assert(!CtorCompilesFor<span<const int, 5>, const std::array<int, 4>&>,
"!CtorCompilesFor<span<const int, 5>, const std::array<int, 4>&>");
static_assert(!CtorCompilesFor<span<int, 5>, const std::array<int, 4>&>,
"!CtorCompilesFor<span<int, 5>, const std::array<int, 4>&>");
}
TEST(span_test, from_container_constructor)
@@ -577,32 +514,28 @@ TEST(span_test, from_container_constructor)
const std::string cstr = "hello";
{
#ifdef CONFIRM_COMPILATION_ERRORS
span<char> s{str};
EXPECT_TRUE(s.size() == str.size());
EXPECT_TRUE(s.data() == str.data()));
#endif
span<const char> cs{str};
EXPECT_TRUE(cs.size() == str.size());
EXPECT_TRUE(cs.data() == str.data());
static_assert(CtorCompilesFor<span<char>, std::string&> == (__cplusplus >= 201703L),
"CtorCompilesFor<span<char>, std::string&> == (__cplusplus >= 201703L)");
span<const char> cs{str};
EXPECT_TRUE(cs.size() == str.size());
EXPECT_TRUE(cs.data() == str.data());
}
{
#ifdef CONFIRM_COMPILATION_ERRORS
span<char> s{cstr};
#endif
static_assert(!CtorCompilesFor<span<char>, const std::string&>,
"!CtorCompilesFor<span<char>, const std::string&>");
span<const char> cs{cstr};
EXPECT_TRUE(cs.size() == cstr.size());
EXPECT_TRUE(cs.data() == cstr.data());
}
{
#ifdef CONFIRM_COMPILATION_ERRORS
auto get_temp_vector = []() -> std::vector<int> { return {}; };
auto use_span = [](span<int> s) { static_cast<void>(s); };
use_span(get_temp_vector());
#endif
}
#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)
{
auto get_temp_vector = []() -> std::vector<int> { return {}; };
@@ -610,13 +543,8 @@ TEST(span_test, from_container_constructor)
use_span(get_temp_vector());
}
{
#ifdef CONFIRM_COMPILATION_ERRORS
auto get_temp_string = []() -> std::string { return {}; };
auto use_span = [](span<char> s) { static_cast<void>(s); };
use_span(get_temp_string());
#endif
}
static_assert(!ConversionCompilesFor<span<char>, std::string>,
"!ConversionCompilesFor<span<char>, std::string>");
{
auto get_temp_string = []() -> std::string { return {}; };
@@ -624,13 +552,10 @@ TEST(span_test, from_container_constructor)
use_span(get_temp_string());
}
{
#ifdef CONFIRM_COMPILATION_ERRORS
auto get_temp_vector = []() -> const std::vector<int> { return {}; };
auto use_span = [](span<const char> s) { static_cast<void>(s); };
use_span(get_temp_vector());
#endif
}
static_assert(!ConversionCompilesFor<span<const char>, const std::vector<int>>,
"!ConversionCompilesFor<span<const char>, const std::vector<int>>");
static_assert(!ConversionCompilesFor<span<char>, const std::string>,
"!ConversionCompilesFor<span<char>, const std::string>");
{
auto get_temp_string = []() -> const std::string { return {}; };
@@ -638,12 +563,8 @@ TEST(span_test, from_container_constructor)
use_span(get_temp_string());
}
{
#ifdef CONFIRM_COMPILATION_ERRORS
std::map<int, int> m;
span<int> s{m};
#endif
}
static_assert(!CtorCompilesFor<span<int>, std::map<int, int>&>,
"!CtorCompilesFor<span<int>, std::map<int, int>&>");
}
TEST(span_test, from_convertible_span_constructor)
@@ -695,52 +616,20 @@ TEST(span_test, from_convertible_span_constructor)
EXPECT_DEATH(T{avd}, expected);
}
#ifdef CONFIRM_COMPILATION_ERRORS
{
std::array<DerivedClass, 2> arr{};
span<DerivedClass> avd{arr};
span<const DerivedClass, 2> avcd = avd;
static_cast<void>(avcd);
}
{
std::array<DerivedClass, 2> arr{};
span<DerivedClass, 2> avd{arr};
span<const DerivedClass, 1> avcd = avd;
static_cast<void>(avcd);
}
{
std::array<DerivedClass, 2> arr{};
span<DerivedClass, 2> avd{arr};
span<const DerivedClass, 3> avcd = avd;
static_cast<void>(avcd);
}
{
span<DerivedClass> avd;
span<BaseClass> avb = avd;
static_cast<void>(avb);
}
{
span<int> s;
span<unsigned int> s2 = s;
static_cast<void>(s2);
}
{
span<int> s;
span<const unsigned int> s2 = s;
static_cast<void>(s2);
}
{
span<int> s;
span<short> s2 = s;
static_cast<void>(s2);
}
#endif
static_assert(!ConversionCompilesFor<span<const DerivedClass, 2>, span<DerivedClass>&>,
"!ConversionCompilesFor<span<const DerivedClass, 2>, span<DerivedClass>&>");
static_assert(!ConversionCompilesFor<span<const DerivedClass, 1>, span<DerivedClass, 2>&>,
"!ConversionCompilesFor<span<const DerivedClass, 1>, span<DerivedClass, 2>&>");
static_assert(!ConversionCompilesFor<span<const DerivedClass, 3>, span<DerivedClass, 2>&>,
"!ConversionCompilesFor<span<const DerivedClass, 3>, span<DerivedClass, 2>&>");
static_assert(!ConversionCompilesFor<span<BaseClass, 3>, span<DerivedClass>&>,
"!ConversionCompilesFor<span<BaseClass, 3>, span<DerivedClass>&>");
static_assert(!ConversionCompilesFor<span<unsigned int>, span<int>&>,
"!ConversionCompilesFor<span<unsigned int>, span<int>&>");
static_assert(!ConversionCompilesFor<span<const unsigned int>, span<int>&>,
"!ConversionCompilesFor<span<const unsigned int>, span<int>&>");
static_assert(!ConversionCompilesFor<span<short>, span<int>&>,
"!ConversionCompilesFor<span<short>, span<int>&>");
}
TEST(span_test, copy_move_and_assignment)
@@ -800,10 +689,9 @@ TEST(span_test, first)
{
span<int, 5> av = arr;
#ifdef CONFIRM_COMPILATION_ERRORS
EXPECT_TRUE(av.first<6>().size() == 6);
EXPECT_TRUE(av.first<-1>().size() == -1);
(void) av.first<6>();
#endif
EXPECT_DEATH(av.first(6).size(), expected);
EXPECT_DEATH(av.first(6), expected);
}
{
@@ -844,9 +732,9 @@ TEST(span_test, last)
{
span<int, 5> av = arr;
#ifdef CONFIRM_COMPILATION_ERRORS
EXPECT_TRUE(av.last<6>().size() == 6);
(void) av.last<6>();
#endif
EXPECT_DEATH(av.last(6).size(), expected);
EXPECT_DEATH(av.last(6), expected);
}
{
@@ -871,6 +759,9 @@ TEST(span_test, subspan)
EXPECT_TRUE((av.subspan<2, 2>().size()) == 2);
EXPECT_TRUE(decltype(av.subspan<2, 2>())::extent == 2);
EXPECT_TRUE(av.subspan(2, 2).size() == 2);
EXPECT_TRUE((av.subspan<2, 3>().size()) == 3);
EXPECT_TRUE(decltype(av.subspan<2, 3>())::extent == 3);
EXPECT_TRUE(av.subspan(2, 3).size() == 3);
}
@@ -887,8 +778,12 @@ TEST(span_test, subspan)
EXPECT_TRUE(decltype(av.subspan<0, 5>())::extent == 5);
EXPECT_TRUE(av.subspan(0, 5).size() == 5);
EXPECT_DEATH(av.subspan(0, 6).size(), expected);
EXPECT_DEATH(av.subspan(1, 5).size(), expected);
#ifdef CONFIRM_COMPILATION_ERRORS
(void) av.subspan<0, 6>();
(void) av.subspan<1, 5>();
#endif
EXPECT_DEATH(av.subspan(0, 6), expected);
EXPECT_DEATH(av.subspan(1, 5), expected);
}
{
@@ -896,14 +791,22 @@ TEST(span_test, subspan)
EXPECT_TRUE((av.subspan<4, 0>().size()) == 0);
EXPECT_TRUE(decltype(av.subspan<4, 0>())::extent == 0);
EXPECT_TRUE(av.subspan(4, 0).size() == 0);
EXPECT_TRUE((av.subspan<5, 0>().size()) == 0);
EXPECT_TRUE(decltype(av.subspan<5, 0>())::extent == 0);
EXPECT_TRUE(av.subspan(5, 0).size() == 0);
EXPECT_DEATH(av.subspan(6, 0).size(), expected);
#ifdef CONFIRM_COMPILATION_ERRORS
(void) av.subspan<6, 0>();
#endif
EXPECT_DEATH(av.subspan(6, 0), expected);
}
{
span<int, 5> av = arr;
EXPECT_TRUE(av.subspan<1>().size() == 4);
EXPECT_TRUE(decltype(av.subspan<1>())::extent == 4);
EXPECT_TRUE(av.subspan(1).size() == 4);
}
{
@@ -911,35 +814,58 @@ TEST(span_test, subspan)
EXPECT_TRUE((av.subspan<0, 0>().size()) == 0);
EXPECT_TRUE(decltype(av.subspan<0, 0>())::extent == 0);
EXPECT_TRUE(av.subspan(0, 0).size() == 0);
EXPECT_DEATH((av.subspan<1, 0>().size()), expected);
EXPECT_DEATH((av.subspan<1, 0>()), expected);
EXPECT_DEATH((av.subspan(1, 0)), expected);
}
{
span<int> av;
EXPECT_TRUE((av.subspan<0>().size()) == 0);
EXPECT_TRUE(decltype(av.subspan<0>())::extent == dynamic_extent);
EXPECT_TRUE(av.subspan(0).size() == 0);
EXPECT_DEATH(av.subspan(1).size(), expected);
EXPECT_DEATH(av.subspan<1>(), expected);
EXPECT_TRUE(decltype(av.subspan<1>())::extent == dynamic_extent);
EXPECT_DEATH(av.subspan(1), expected);
}
{
span<int> av = arr;
EXPECT_TRUE(av.subspan(0).size() == 5);
EXPECT_TRUE(av.subspan<0>().size() == 5);
EXPECT_TRUE(av.subspan(1).size() == 4);
EXPECT_TRUE(av.subspan<1>().size() == 4);
EXPECT_TRUE(av.subspan(4).size() == 1);
EXPECT_TRUE(av.subspan<4>().size() == 1);
EXPECT_TRUE(av.subspan(5).size() == 0);
EXPECT_DEATH(av.subspan(6).size(), expected);
EXPECT_TRUE(av.subspan<5>().size() == 0);
EXPECT_DEATH(av.subspan(6), expected);
EXPECT_DEATH(av.subspan<6>(), expected);
const auto av2 = av.subspan(1);
for (std::size_t i = 0; i < 4; ++i) EXPECT_TRUE(av2[i] == static_cast<int>(i) + 2);
const auto av3 = av.subspan<1>();
for (std::size_t i = 0; i < 4; ++i) EXPECT_TRUE(av3[i] == static_cast<int>(i) + 2);
}
{
span<int, 5> av = arr;
EXPECT_TRUE(av.subspan(0).size() == 5);
EXPECT_TRUE(av.subspan<0>().size() == 5);
EXPECT_TRUE(av.subspan(1).size() == 4);
EXPECT_TRUE(av.subspan<1>().size() == 4);
EXPECT_TRUE(av.subspan(4).size() == 1);
EXPECT_TRUE(av.subspan<4>().size() == 1);
EXPECT_TRUE(av.subspan(5).size() == 0);
EXPECT_DEATH(av.subspan(6).size(), expected);
EXPECT_TRUE(av.subspan<5>().size() == 0);
EXPECT_DEATH(av.subspan(6), expected);
#ifdef CONFIRM_COMPILATION_ERRORS
EXPECT_DEATH(av.subspan<6>(), expected);
#endif
const auto av2 = av.subspan(1);
for (std::size_t i = 0; i < 4; ++i) EXPECT_TRUE(av2[i] == static_cast<int>(i) + 2);
const auto av3 = av.subspan<1>();
for (std::size_t i = 0; i < 4; ++i) EXPECT_TRUE(av3[i] == static_cast<int>(i) + 2);
}
}
@@ -1114,9 +1040,21 @@ TEST(span_test, rbegin_rend)
}
}
template <typename U, typename = void>
static constexpr bool AsWritableBytesCompilesFor = false;
template <typename U>
static constexpr bool
AsWritableBytesCompilesFor<U, void_t<decltype(as_writable_bytes(std::declval<U>()))>> = true;
TEST(span_test, as_bytes)
{
int a[] = {1, 2, 3, 4};
static_assert(AsWritableBytesCompilesFor<span<int>>, "AsWriteableBytesCompilesFor<span<int>>");
// you should not be able to get writeable bytes for const objects
static_assert(!AsWritableBytesCompilesFor<span<const int>>,
"!AsWriteableBytesCompilesFor<span<const int>>");
{
const span<const int> s = a;
EXPECT_TRUE(s.size() == 4);
@@ -1147,17 +1085,6 @@ TEST(span_test, as_writable_bytes)
{
int a[] = {1, 2, 3, 4};
{
#ifdef CONFIRM_COMPILATION_ERRORS
// you should not be able to get writeable bytes for const objects
span<const int> s = a;
EXPECT_TRUE(s.size() == 4);
span<const byte> bs = as_writable_bytes(s);
EXPECT_TRUE(static_cast<void*>(bs.data()) == static_cast<void*>(s.data()));
EXPECT_TRUE(bs.size() == s.size_bytes());
#endif
}
{
span<int> s;
const auto bs = as_writable_bytes(s);
@@ -1197,30 +1124,15 @@ TEST(span_test, fixed_size_conversions)
static_cast<void>(s);
}
// initialization or assignment to static span that REDUCES size is NOT ok
#ifdef CONFIRM_COMPILATION_ERRORS
{
span<int, 2> s = arr;
}
{
span<int, 2> s2 = s4;
static_cast<void>(s2);
}
#endif
// initialization or assignment to static span that REDUCES size is NOT ok
static_assert(!ConversionCompilesFor<span<int, 2>, int[4]>,
"!ConversionCompilesFor<span<int, 2>, int[4]>");
static_assert(!ConversionCompilesFor<span<int, 2>, span<int, 4>>,
"!ConversionCompilesFor<span<int, 2>, span<int, 4>>");
// even when done dynamically
{
/*
// this now results in a compile-time error, rather than runtime.
// There is no suitable conversion from dynamic span to fixed span.
span<int> s = arr;
auto f = [&]() {
const span<int, 2> s2 = s;
static_cast<void>(s2);
};
EXPECT_DEATH(f(), expected);
*/
}
static_assert(!ConversionCompilesFor<span<int, 2>, span<int>>,
"!ConversionCompilesFor<span<int, 2>, span<int>>");
// but doing so explicitly is ok
@@ -1234,32 +1146,24 @@ TEST(span_test, fixed_size_conversions)
static_cast<void>(s1);
}
/*
// this is not a legal operation in std::span, so we are no longer supporting it
// conversion from span<int, 4> to span<int, dynamic_extent> via call to `first`
// then convert from span<int, dynamic_extent> to span<int, 1>
// The dynamic to fixed extents are not supported in the standard
// to make this work, span<int, 1> would need to be span<int>.
{
// NB: implicit conversion to span<int,1> from span<int>
span<int, 1> s1 = s4.first(1);
static_cast<void>(s1);
}
*/
// this is not a legal operation in std::span, so we are no longer supporting it
// conversion from span<int, 4> to span<int, dynamic_extent> via call to `first`
// then convert from span<int, dynamic_extent> to span<int, 1>
// The dynamic to fixed extents are not supported in the standard
// to make this work, span<int, 1> would need to be span<int>.
static_assert(!ConversionCompilesFor<span<int, 1>, span<int>>,
"!ConversionCompilesFor<span<int, 1>, span<int>>");
// initialization or assignment to static span that requires size INCREASE is not ok.
int arr2[2] = {1, 2};
#ifdef CONFIRM_COMPILATION_ERRORS
{
span<int, 4> s3 = arr2;
}
{
span<int, 2> s2 = arr2;
span<int, 4> s4a = s2;
}
#endif
static_assert(!ConversionCompilesFor<span<int, 4>, int[2]>,
"!ConversionCompilesFor<span<int, 4>, int[2]>");
static_assert(!ConversionCompilesFor<span<int, 2>, int[2]>,
"!ConversionCompilesFor<span<int, 2>, int[2]>");
static_assert(!ConversionCompilesFor<span<int, 4>, span<int, 2>>,
"!ConversionCompilesFor<span<int, 4>, span<int, 2>>");
{
auto f = [&]() {
const span<int, 4> _s4{arr2, 2};
@@ -1268,16 +1172,11 @@ TEST(span_test, fixed_size_conversions)
EXPECT_DEATH(f(), expected);
}
/*
// This no longer compiles. There is no suitable conversion from dynamic span to a fixed size
span.
// this should fail - we are trying to assign a small dynamic span to a fixed_size larger one
span<int> av = arr2; auto f = [&]() {
const span<int, 4> _s4 = av;
static_cast<void>(_s4);
};
EXPECT_DEATH(f(), expected);
*/
// This no longer compiles. There is no suitable conversion from dynamic span to a fixed size
// span.
// this should fail - we are trying to assign a small dynamic span to a fixed_size larger one
static_assert(!ConversionCompilesFor<span<int, 4>, span<int>>,
"!ConversionCompilesFor<span<int, 4>, span<int>>");
}
TEST(span_test, interop_with_std_regex)
@@ -1312,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};
@@ -1331,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)
@@ -1376,8 +1275,154 @@ TEST(span_test, msvc_compile_error_PR1100)
int arr[]{1, 7, 2, 9};
gsl::span sp{arr, std::size(arr)};
std::ranges::sort(sp);
for (const auto& e : sp) {
(void)e;
}
for (const auto& e : sp) { (void) e; }
}
#endif // defined(__cpp_lib_span) && defined(__cpp_lib_ranges)
TEST(span_test, empty_span)
{
span<int> s{};
EXPECT_TRUE(s.empty());
EXPECT_TRUE(s.size() == 0);
EXPECT_TRUE(s.data() == nullptr);
span<const int> cs{};
EXPECT_TRUE(cs.empty());
EXPECT_TRUE(cs.size() == 0);
EXPECT_TRUE(cs.data() == nullptr);
}
TEST(span_test, conversions)
{
int arr[5] = {1, 2, 3, 4, 5};
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
span s = arr;
span cs = s;
#else // ^^^ deduction guides /// no deduction guides vvv
span<int, 5> s = arr;
span<int, 5> cs = s;
#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
EXPECT_TRUE(cs.size() == s.size());
EXPECT_TRUE(cs.data() == s.data());
span<int, 5> fs = s;
EXPECT_TRUE(fs.size() == s.size());
EXPECT_TRUE(fs.data() == s.data());
span<const int, 5> cfs = s;
EXPECT_TRUE(cfs.size() == s.size());
EXPECT_TRUE(cfs.data() == s.data());
}
TEST(span_test, comparison_operators)
{
int arr1[3] = {1, 2, 3};
int arr2[3] = {1, 2, 3};
int arr3[3] = {4, 5, 6};
span<int> s1 = arr1;
span<int> s2 = arr2;
span<int> s3 = arr3;
EXPECT_TRUE(s1 == s2);
EXPECT_FALSE(s1 != s2);
EXPECT_FALSE(s1 == s3);
EXPECT_TRUE(s1 != s3);
EXPECT_TRUE(s1 < s3);
EXPECT_FALSE(s3 < s1);
EXPECT_TRUE(s1 <= s2);
EXPECT_TRUE(s1 <= s3);
EXPECT_FALSE(s3 <= s1);
EXPECT_TRUE(s3 > s1);
EXPECT_FALSE(s1 > s3);
EXPECT_TRUE(s3 >= s1);
EXPECT_TRUE(s1 >= s2);
EXPECT_FALSE(s1 >= s3);
}
// ...existing code...
#if defined(__cpp_lib_span) && __cpp_lib_span >= 202002L
#include <span> // for std::span
TEST(span_test, compare_empty_span)
{
gsl::span<int> gsl_s{};
std::span<int> std_s{};
EXPECT_TRUE(gsl_s.empty());
EXPECT_TRUE(std_s.empty());
EXPECT_EQ(gsl_s.size(), std_s.size());
EXPECT_EQ(gsl_s.data(), std_s.data());
}
TEST(span_test, compare_subspan)
{
int arr[5] = {1, 2, 3, 4, 5};
gsl::span gsl_s = arr;
std::span std_s = arr;
auto gsl_sub1 = gsl_s.subspan(1);
auto std_sub1 = std_s.subspan(1);
EXPECT_EQ(gsl_sub1.size(), std_sub1.size());
EXPECT_EQ(gsl_sub1.data(), std_sub1.data());
auto gsl_sub2 = gsl_s.subspan(1, 2);
auto std_sub2 = std_s.subspan(1, 2);
EXPECT_EQ(gsl_sub2.size(), std_sub2.size());
EXPECT_EQ(gsl_sub2.data(), std_sub2.data());
}
TEST(span_test, compare_conversions)
{
int arr[5] = {1, 2, 3, 4, 5};
gsl::span gsl_s = arr;
std::span std_s = arr;
gsl::span gsl_cs = gsl_s;
std::span std_cs = std_s;
EXPECT_EQ(gsl_cs.size(), std_cs.size());
EXPECT_EQ(gsl_cs.data(), std_cs.data());
gsl::span<int, 5> gsl_fs = gsl_s;
std::span<int, 5> std_fs = std_s;
EXPECT_EQ(gsl_fs.size(), std_fs.size());
EXPECT_EQ(gsl_fs.data(), std_fs.data());
gsl::span<const int, 5> gsl_cfs = gsl_s;
std::span<const int, 5> std_cfs = std_s;
EXPECT_EQ(gsl_cfs.size(), std_cfs.size());
EXPECT_EQ(gsl_cfs.data(), std_cfs.data());
}
TEST(span_test, deduction_guides)
{
int arr[5] = {1, 2, 3, 4, 5};
std::array<int, 5> std_arr = {1, 2, 3, 4, 5};
std::vector<int> vec = {1, 2, 3, 4, 5};
// Test deduction guides for gsl::span
gsl::span gsl_s1 = arr;
gsl::span gsl_s2 = std_arr;
gsl::span gsl_s3 = vec;
// Test deduction guides for std::span (for sanity checks)
std::span std_s1 = arr;
std::span std_s2 = std_arr;
std::span std_s3 = vec;
// Compare sizes
EXPECT_EQ(gsl_s1.size(), std_s1.size());
EXPECT_EQ(gsl_s2.size(), std_s2.size());
EXPECT_EQ(gsl_s3.size(), std_s3.size());
// Compare data pointers
EXPECT_EQ(gsl_s1.data(), std_s1.data());
EXPECT_EQ(gsl_s2.data(), std_s2.data());
EXPECT_EQ(gsl_s3.data(), std_s3.data());
}
#endif // defined(__cpp_lib_span) && __cpp_lib_span >= 202002L
+192 -46
View File
@@ -17,38 +17,188 @@
#include <gsl/pointers> // for not_null, operator<, operator<=, operator>
#include <gtest/gtest.h>
#include <type_traits> // for declval
#include "deathTestCommon.h"
using namespace gsl;
#if __cplusplus >= 201703l
using std::void_t;
#else // __cplusplus >= 201703l
template <class...>
using void_t = void;
#endif // __cplusplus < 201703l
// stand-in for a user-defined ref-counted class
template <typename T>
struct RefCounted
{
RefCounted(T* p) : p_(p) {}
operator T*() { return p_; }
T* p_;
};
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; }
#ifdef CONFIRM_COMPILATION_ERRORS
int* return_pointer() { return nullptr; }
const int* return_pointer_const() { return nullptr; }
#endif
} // namespace
template <typename U, typename = void>
static constexpr bool CtorCompilesFor_A = false;
template <typename U>
static constexpr bool
CtorCompilesFor_A<U, void_t<decltype(gsl::strict_not_null<void*>{std::declval<U>()})>> = true;
template <typename U, int N, typename = void>
static constexpr bool CtorCompilesFor_B = false;
template <typename U, int N>
static constexpr bool CtorCompilesFor_B<U, N, void_t<decltype(gsl::strict_not_null<U>{N})>> = true;
template <typename U, typename = void>
static constexpr bool DefaultCtorCompilesFor = false;
template <typename U>
static constexpr bool DefaultCtorCompilesFor<U, void_t<decltype(gsl::strict_not_null<U>{})>> = true;
template <typename U, typename = void>
static constexpr bool CtorCompilesFor_C = false;
template <typename U>
static constexpr bool CtorCompilesFor_C<
U, void_t<decltype(gsl::strict_not_null<U*>{std::declval<std::unique_ptr<U>>()})>> = true;
TEST(strict_notnull_tests, TestStrictNotNullConstructors)
{
{
static_assert(CtorCompilesFor_A<void*>, "CtorCompilesFor_A<void*>");
static_assert(!CtorCompilesFor_A<std::nullptr_t>, "!CtorCompilesFor_A<std::nullptr_t>");
static_assert(!CtorCompilesFor_B<void*, 0>, "!CtorCompilesFor_B<void*, 0>");
static_assert(!DefaultCtorCompilesFor<void*>, "!DefaultCtorCompilesFor<void*>");
static_assert(!CtorCompilesFor_C<int>, "CtorCompilesFor_C<int>");
#ifdef CONFIRM_COMPILATION_ERRORS
// Forbid non-nullptr assignable types
strict_not_null<std::vector<int>> f(std::vector<int>{1});
strict_not_null<int> z(10);
strict_not_null<std::vector<int>> y({1, 2});
#endif
}
const auto terminateHandler = std::set_terminate([] {
std::cerr << "Expected Death. TestNotNullConstructors";
std::abort();
});
const auto expected = GetExpectedDeathString(terminateHandler);
{
// from shared pointer
int i = 12;
auto rp = RefCounted<int>(&i);
strict_not_null<int*> p(rp);
EXPECT_TRUE(p.get() == &i);
strict_not_null<std::shared_ptr<int>> x(
std::make_shared<int>(10)); // shared_ptr<int> is nullptr assignable
int* pi = nullptr;
EXPECT_DEATH((strict_not_null<decltype(pi)>(pi)), expected);
}
{
// from unique pointer
strict_not_null<std::unique_ptr<int>> x(
std::make_unique<int>(10)); // unique_ptr<int> is nullptr assignable
EXPECT_DEATH((strict_not_null<std::unique_ptr<int>>(std::unique_ptr<int>{})), expected);
}
{
// from pointer to local
int t = 42;
strict_not_null<int*> x{&t};
helper(&t);
helper_const(&t);
EXPECT_TRUE(*x == 42);
}
{
// from raw pointer
// from strict_not_null pointer
int t = 42;
int* p = &t;
strict_not_null<int*> x{p};
helper(p);
helper_const(p);
helper(x);
helper_const(x);
EXPECT_TRUE(*x == 42);
}
{
// from raw const pointer
// from strict_not_null const pointer
int t = 42;
const int* cp = &t;
strict_not_null<const int*> x{cp};
helper_const(cp);
helper_const(x);
EXPECT_TRUE(*x == 42);
}
{
// from strict_not_null const pointer, using auto
int t = 42;
const int* cp = &t;
auto x = strict_not_null<const int*>{cp};
EXPECT_TRUE(*x == 42);
}
{
// from returned pointer
EXPECT_DEATH(helper(return_pointer()), expected);
EXPECT_DEATH(helper_const(return_pointer()), expected);
}
}
template <typename U, typename = void>
static constexpr bool StrictHelperCompilesFor = false;
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>
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>
static constexpr bool HelperCompilesFor<U, void_t<decltype(helper(std::declval<U>()))>> = true;
TEST(strict_notnull_tests, TestStrictNotNull)
{
{
@@ -57,14 +207,14 @@ TEST(strict_notnull_tests, TestStrictNotNull)
#ifdef CONFIRM_COMPILATION_ERRORS
strict_not_null<int*> snn = &x;
strict_helper(&x);
strict_helper_const(&x);
strict_helper(return_pointer());
strict_helper_const(return_pointer_const());
#endif
static_assert(!StrictHelperCompilesFor<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*>>");
helper(snn1);
helper_const(snn1);
@@ -77,17 +227,17 @@ TEST(strict_notnull_tests, TestStrictNotNull)
#ifdef CONFIRM_COMPILATION_ERRORS
strict_not_null<int*> snn = &x;
strict_helper(&x);
strict_helper_const(&x);
strict_helper(return_pointer());
strict_helper_const(return_pointer_const());
#endif
static_assert(!StrictHelperCompilesFor<const int*>, "!StrictHelperFor<const int*>");
static_assert(!StrictHelperConstCompilesFor<const int*>,
"!StrictHelperCompilesFor<const int*>");
const strict_not_null<const int*> snn1{&x};
#ifdef CONFIRM_COMPILATION_ERRORS
helper(snn1);
#endif
static_assert(!HelperCompilesFor<const strict_not_null<const int*>>,
"!HelperCompilesFor<const strict_not_null<const int*>>");
static_assert(StrictHelperConstCompilesFor<const strict_not_null<const int*>>,
"StrictHelperCompilesFor<const strict_not_null<const int*>>");
helper_const(snn1);
EXPECT_TRUE(*snn1 == 42);
@@ -114,9 +264,8 @@ TEST(strict_notnull_tests, TestStrictNotNull)
strict_not_null<const int*> snn1{&x};
const strict_not_null<const int*> snn2{&x};
#ifdef CONFIRM_COMPILATION_ERRORS
strict_helper(snn1);
#endif
static_assert(!StrictHelperCompilesFor<strict_not_null<const int*>>,
"!StrictHelperCompilesFor<strict_not_null<const int*>>");
strict_helper_const(snn1);
strict_helper_const(snn2);
@@ -148,9 +297,8 @@ TEST(strict_notnull_tests, TestStrictNotNull)
const not_null<const int*> nn1 = snn;
const not_null<const int*> nn2{snn};
#ifdef CONFIRM_COMPILATION_ERRORS
helper(snn);
#endif
static_assert(!HelperCompilesFor<strict_not_null<const int*>>,
"!HelperCompilesFor<strict_not_null<const int*>>");
helper_const(snn);
EXPECT_TRUE(snn == nn1);
@@ -190,9 +338,8 @@ TEST(strict_notnull_tests, TestStrictNotNull)
const strict_not_null<const int*> snn1{nn};
const strict_not_null<const int*> snn2{nn};
#ifdef CONFIRM_COMPILATION_ERRORS
strict_helper(nn);
#endif
static_assert(!StrictHelperCompilesFor<not_null<const int*>>,
"!StrictHelperCompilesFor<not_null<const int*>>");
strict_helper_const(nn);
EXPECT_TRUE(snn1 == nn);
@@ -206,12 +353,13 @@ TEST(strict_notnull_tests, TestStrictNotNull)
EXPECT_TRUE(hash_nn(snn1) == hash_nn(snn2));
EXPECT_TRUE(hash_snn(snn1) == hash_snn(nn));
}
}
#ifdef CONFIRM_COMPILATION_ERRORS
{
strict_not_null<int*> p{nullptr};
}
#endif
TEST(pointers_test, member_types)
{
// make sure `element_type` is inherited from `gsl::not_null`
static_assert(std::is_same<gsl::strict_not_null<int*>::element_type, int*>::value,
"check member type: element_type");
}
#if defined(__cplusplus) && (__cplusplus >= 201703L)
@@ -238,9 +386,8 @@ TEST(strict_notnull_tests, TestStrictNotNullConstructorTypeDeduction)
const int i = 42;
strict_not_null x{&i};
#ifdef CONFIRM_COMPILATION_ERRORS
helper(strict_not_null{&i});
#endif
static_assert(!HelperCompilesFor<strict_not_null<const int*>>,
"!HelperCompilesFor<strict_not_null<const int*>>");
helper_const(strict_not_null{&i});
EXPECT_TRUE(*x == 42);
@@ -262,9 +409,8 @@ TEST(strict_notnull_tests, TestStrictNotNullConstructorTypeDeduction)
const int* p = &i;
strict_not_null x{p};
#ifdef CONFIRM_COMPILATION_ERRORS
helper(strict_not_null{p});
#endif
static_assert(!HelperCompilesFor<strict_not_null<const int*>>,
"!HelperCompilesFor<strict_not_null<const int*>>");
helper_const(strict_not_null{p});
EXPECT_TRUE(*x == 42);
+3 -3
View File
@@ -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);