mirror of
https://github.com/microsoft/GSL.git
synced 2026-08-06 05:34:26 +02:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e0943d20d | |||
| 2d343b0440 | |||
| c21970972b | |||
| 466e4ebaa5 |
@@ -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.
|
||||
@@ -0,0 +1,22 @@
|
||||
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@v4
|
||||
|
||||
- name: Install Build Dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y clang cmake make
|
||||
|
||||
- name: Configure CMake (C++14)
|
||||
run: cmake -B build-cxx14 . -DGSL_CXX_STANDARD=14 -DGSL_TEST=ON -G "Unix Makefiles"
|
||||
|
||||
- name: Configure CMake (C++20)
|
||||
run: cmake -B build-cxx20 . -DGSL_CXX_STANDARD=20 -DGSL_TEST=ON -G "Unix Makefiles"
|
||||
|
||||
@@ -28,8 +28,8 @@ jobs:
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=9 \
|
||||
-DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \
|
||||
"-DMACOSX_BUNDLE_GUI_IDENTIFIER=GSL.\$(EXECUTABLE_NAME)" \
|
||||
-DMACOSX_BUNDLE_BUNDLE_VERSION=4.2.2 \
|
||||
-DMACOSX_BUNDLE_SHORT_VERSION_STRING=4.2.2 \
|
||||
-DMACOSX_BUNDLE_BUNDLE_VERSION=3.1.0 \
|
||||
-DMACOSX_BUNDLE_SHORT_VERSION_STRING=3.1.0 \
|
||||
..
|
||||
|
||||
- name: Build
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
CMakeFiles
|
||||
build
|
||||
build*/
|
||||
tests/CMakeFiles
|
||||
tests/Debug
|
||||
*.opensdf
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.14...3.16)
|
||||
|
||||
project(GSL VERSION 4.2.2 LANGUAGES CXX)
|
||||
project(GSL VERSION 4.2.0 LANGUAGES CXX)
|
||||
|
||||
add_library(GSL INTERFACE)
|
||||
add_library(Microsoft.GSL::GSL ALIAS GSL)
|
||||
|
||||
@@ -49,7 +49,7 @@ dyn_array | &
|
||||
move_owner | ☐ | A helper function that moves one `owner` to the other
|
||||
[final_action](docs/headers.md#user-content-H-util-final_action) | ☑ | A RAII style class that invokes a functor on its destruction
|
||||
[finally](docs/headers.md#user-content-H-util-finally) | ☑ | A helper function instantiating [final_action](docs/headers.md#user-content-H-util-final_action)
|
||||
[GSL_SUPPRESS](docs/headers.md#user-content-H-assert-gsl_suppress) | ☑ | A macro that takes an argument and turns it into `[[gsl::suppress(x)]]` or `[[gsl::suppress("x")]]` depending on the compiler.
|
||||
[GSL_SUPPRESS](docs/headers.md#user-content-H-assert-gsl_suppress) | ☑ | A macro that takes an argument and turns it into `[[gsl::suppress(x)]]` or `[[gsl::suppress("x")]]`
|
||||
[[implicit]] | ☐ | A "marker" to put on single-argument constructors to explicitly make them non-explicit
|
||||
[index](docs/headers.md#user-content-H-util-index) | ☑ | A type to use for all container and array indexing (currently an alias for `std::ptrdiff_t`)
|
||||
[narrow](docs/headers.md#user-content-H-narrow-narrow) | ☑ | A checked version of `narrow_cast`; it can throw [narrowing_error](docs/headers.md#user-content-H-narrow-narrowing_error)
|
||||
@@ -202,7 +202,7 @@ include(FetchContent)
|
||||
|
||||
FetchContent_Declare(GSL
|
||||
GIT_REPOSITORY "https://github.com/microsoft/GSL"
|
||||
GIT_TAG "v4.2.2"
|
||||
GIT_TAG "v4.2.0"
|
||||
GIT_SHALLOW ON
|
||||
)
|
||||
|
||||
|
||||
+26
-12
@@ -49,9 +49,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")]]`.
|
||||
|
||||
Older versions of MSVC (VS 2022 and earlier) only understand `[[gsl::suppress(tag)]]` without the double quotes around `tag`.
|
||||
Clang does not use exactly that syntax, but requires `tag` to be put in double quotes `[[gsl::suppress("tag")]]`.
|
||||
|
||||
For portable code you can use `GSL_SUPPRESS(tag)`.
|
||||
|
||||
@@ -412,22 +412,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
|
||||
|
||||
|
||||
+4
-5
@@ -47,14 +47,13 @@
|
||||
//
|
||||
#if defined(__clang__)
|
||||
#define GSL_SUPPRESS(x) [[gsl::suppress(#x)]]
|
||||
#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__)
|
||||
#else
|
||||
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__NVCC__)
|
||||
#define GSL_SUPPRESS(x) [[gsl::suppress(x)]]
|
||||
#else
|
||||
#define GSL_SUPPRESS(x)
|
||||
#endif // defined(__clang__)
|
||||
#endif // _MSC_VER
|
||||
#endif // __clang__
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define GSL_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace details
|
||||
// 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,
|
||||
sizeof(T) <= 2*sizeof(void*) && std::is_trivially_copy_constructible<T>::value,
|
||||
const T,
|
||||
const T&>;
|
||||
|
||||
@@ -173,7 +173,7 @@ 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,
|
||||
constexpr auto operator==(const not_null<T>& lhs,
|
||||
const not_null<U>& rhs) noexcept(noexcept(lhs.get() == rhs.get()))
|
||||
-> decltype(lhs.get() == rhs.get())
|
||||
{
|
||||
@@ -181,7 +181,7 @@ auto operator==(const not_null<T>& lhs,
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto operator!=(const not_null<T>& lhs,
|
||||
constexpr auto operator!=(const not_null<T>& lhs,
|
||||
const not_null<U>& rhs) noexcept(noexcept(lhs.get() != rhs.get()))
|
||||
-> decltype(lhs.get() != rhs.get())
|
||||
{
|
||||
@@ -189,7 +189,7 @@ auto operator!=(const not_null<T>& lhs,
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto operator<(const not_null<T>& lhs,
|
||||
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()))
|
||||
{
|
||||
@@ -197,7 +197,7 @@ auto operator<(const not_null<T>& lhs,
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto operator<=(const not_null<T>& lhs,
|
||||
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()))
|
||||
{
|
||||
@@ -205,7 +205,7 @@ auto operator<=(const not_null<T>& lhs,
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto operator>(const not_null<T>& lhs,
|
||||
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()))
|
||||
{
|
||||
@@ -213,7 +213,7 @@ auto operator>(const not_null<T>& lhs,
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto operator>=(const not_null<T>& lhs,
|
||||
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()))
|
||||
{
|
||||
|
||||
@@ -204,6 +204,7 @@ add_executable(gsl_tests
|
||||
assertion_tests.cpp
|
||||
at_tests.cpp
|
||||
byte_tests.cpp
|
||||
constexpr_notnull_tests.cpp
|
||||
notnull_tests.cpp
|
||||
owner_tests.cpp
|
||||
pointers_tests.cpp
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user