Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,3 @@ AlignConsecutiveAssignments: false
AlignTrailingComments: true

SpaceAfterCStyleCast: true
CommentPragmas: '^ NO-FORMAT:'
39 changes: 39 additions & 0 deletions .github/workflows/clang-format.yml
Original file line number Diff line number Diff line change
@@ -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 }}
Comment on lines +24 to +27
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather use the pre-installed version, but I'm not going to block the PR on it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pre-installed version needs the WhitespaceSensitiveMacros change, clang-format-20 does not.

Regarding the burden on the authors: I think it might be a good idea to provide an apply-clang-format.bat and an apply-clang-format.sh. Of course the bat file would only work in the developer command prompt and could only use the clang-format version shipped with Visual Studio. What do you think about it? Shall I add those files to make formatting easier?


# 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
4 changes: 1 addition & 3 deletions include/gsl/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand Down
4 changes: 1 addition & 3 deletions include/gsl/assert
Original file line number Diff line number Diff line change
Expand Up @@ -93,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);
Expand Down
34 changes: 18 additions & 16 deletions include/gsl/byte
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,13 @@ namespace gsl
{
#if GSL_USE_STD_BYTE

namespace impl {
// impl::byte is used by gsl::as_bytes so our own code does not trigger a deprecation warning as would be the case when we used gsl::byte.
// Users of GSL should only use gsl::byte, not gsl::impl::byte.
using byte = std::byte;
}
namespace impl
{
// impl::byte is used by gsl::as_bytes so our own code does not trigger a deprecation warning as
// would be the case when we used gsl::byte. Users of GSL should only use gsl::byte, not
// gsl::impl::byte.
using byte = std::byte;
} // namespace impl

using byte GSL_DEPRECATED("Use std::byte instead.") = std::byte;

Expand All @@ -100,11 +102,13 @@ enum class byte_may_alias byte : unsigned char
{
};

namespace impl {
// impl::byte is used by gsl::as_bytes so our own code does not trigger a deprecation warning as would be the case when we used gsl::byte.
// Users of GSL should only use gsl::byte, not gsl::impl::byte.
using byte = gsl::byte;
}
namespace impl
{
// impl::byte is used by gsl::as_bytes so our own code does not trigger a deprecation warning as
// would be the case when we used gsl::byte. Users of GSL should only use gsl::byte, not
// gsl::impl::byte.
using byte = gsl::byte;
} // namespace impl

template <class IntegerType, std::enable_if_t<std::is_integral<IntegerType>::value, bool> = true>
constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept
Expand Down Expand Up @@ -170,15 +174,13 @@ constexpr IntegerType to_integer(byte b) noexcept

#endif // GSL_USE_STD_BYTE


template <typename T>
// NOTE: need suppression since c++14 does not allow "return {t}"
// GSL_SUPPRESS(type.4) // NO-FORMAT: attribute // TODO: suppression does not work
constexpr gsl::impl::byte to_byte(T t) noexcept
{
static_assert(std::is_same<T, unsigned char>::value,
"gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. "
"If you are calling to_byte with an integer constant use: gsl::to_byte<t>() version.");
static_assert(
std::is_same<T, unsigned char>::value,
"gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. "
"If you are calling to_byte with an integer constant use: gsl::to_byte<t>() version.");
return gsl::impl::byte(t);
}

Expand Down
14 changes: 7 additions & 7 deletions include/gsl/gsl
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
#define GSL_GSL_H

// IWYU pragma: begin_exports
#include "./algorithm" // copy
#include "./assert" // Ensures/Expects
#include "./byte" // byte
#include "./pointers" // owner, not_null
#include "./span" // span
#include "./zstring" // zstring
#include "./util" // finally()/narrow_cast()...
#include "./algorithm" // copy
#include "./assert" // Ensures/Expects
#include "./byte" // byte
#include "./pointers" // owner, not_null
#include "./span" // span
#include "./util" // finally()/narrow_cast()...
#include "./zstring" // zstring

#ifdef __cpp_exceptions
#include "./narrow" // narrow()
Expand Down
47 changes: 21 additions & 26 deletions include/gsl/narrow
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -28,53 +28,48 @@ 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{}))))
{
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;
}
Expand Down
Loading
Loading