Compare commits

..

1 Commits

Author SHA1 Message Date
Carson Radtke 249146590e create 4.2.1 release 2025-12-08 11:17:57 -07:00
10 changed files with 22 additions and 217 deletions
-84
View File
@@ -1,84 +0,0 @@
# 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.
-22
View File
@@ -1,22 +0,0 @@
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"
+2 -2
View File
@@ -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=3.1.0 \
-DMACOSX_BUNDLE_SHORT_VERSION_STRING=3.1.0 \
-DMACOSX_BUNDLE_BUNDLE_VERSION=4.2.1 \
-DMACOSX_BUNDLE_SHORT_VERSION_STRING=4.2.1 \
..
- name: Build
+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.2.0 LANGUAGES CXX)
project(GSL VERSION 4.2.1 LANGUAGES CXX)
add_library(GSL INTERFACE)
add_library(Microsoft.GSL::GSL ALIAS GSL)
+1 -1
View File
@@ -202,7 +202,7 @@ include(FetchContent)
FetchContent_Declare(GSL
GIT_REPOSITORY "https://github.com/microsoft/GSL"
GIT_TAG "v4.2.0"
GIT_TAG "v4.2.1"
GIT_SHALLOW ON
)
+10 -24
View File
@@ -412,36 +412,22 @@ 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 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
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.
#### Which version of span should I use?
The following table compares the different span implementations to help you choose which one is best for your project:
##### Use `gsl::span` if
| 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 |
- 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.
**Recommendations:**
##### Use `std::span` if
- **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)
- your project is C++20 and you need the performance offered by `std::span`.
#### Types
+7 -7
View File
@@ -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>
constexpr auto operator==(const not_null<T>& lhs,
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 @@ constexpr auto operator==(const not_null<T>& lhs,
}
template <class T, class U>
constexpr auto operator!=(const not_null<T>& lhs,
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 @@ constexpr auto operator!=(const not_null<T>& lhs,
}
template <class T, class U>
constexpr auto operator<(const not_null<T>& lhs,
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 @@ constexpr auto operator<(const not_null<T>& lhs,
}
template <class T, class U>
constexpr auto operator<=(const not_null<T>& lhs,
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 @@ constexpr auto operator<=(const not_null<T>& lhs,
}
template <class T, class U>
constexpr auto operator>(const not_null<T>& lhs,
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 @@ constexpr auto operator>(const not_null<T>& lhs,
}
template <class T, class U>
constexpr auto operator>=(const not_null<T>& lhs,
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()))
{
-1
View File
@@ -204,7 +204,6 @@ 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
-74
View File
@@ -1,74 +0,0 @@
///////////////////////////////////////////////////////////////////////////////
//
// 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));
}