From dde7b1dff186e115343597270c744f3791c988f9 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sat, 8 Aug 2026 14:58:55 -0700 Subject: [PATCH 1/3] src: use concepts where appropriate Some general modernizations of templates Signed-off-by: James M Snell --- node.gyp | 1 + src/node_concepts.h | 24 ++++++++++++++++++++++++ src/util-inl.h | 33 +++++++++++++++------------------ src/util.h | 20 +++++++++++--------- 4 files changed, 51 insertions(+), 27 deletions(-) create mode 100644 src/node_concepts.h diff --git a/node.gyp b/node.gyp index 7bff8e8a4e7f..63ce0e956244 100644 --- a/node.gyp +++ b/node.gyp @@ -255,6 +255,7 @@ 'src/node_blob.h', 'src/node_buffer.h', 'src/node_builtins.h', + 'src/node_concepts.h', 'src/node_config_file.h', 'src/node_constants.h', 'src/node_context_data.h', diff --git a/src/node_concepts.h b/src/node_concepts.h new file mode 100644 index 000000000000..02c2f15299bc --- /dev/null +++ b/src/node_concepts.h @@ -0,0 +1,24 @@ +#ifndef SRC_NODE_CONCEPTS_H_ +#define SRC_NODE_CONCEPTS_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include +#include +#include + +namespace node { + +// A numeric type recognized by std::numeric_limits. +template +concept NumericValue = std::numeric_limits::is_specialized; + +// A numeric type or an enum (which has an underlying numeric type). +template +concept NumericOrEnum = NumericValue || std::is_enum_v; + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_CONCEPTS_H_ diff --git a/src/util-inl.h b/src/util-inl.h index e357d15a1449..e36133646b8f 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -195,7 +195,7 @@ char ToLower(char c) { return std::tolower(c, std::locale::classic()); } -template +template std::string ToLower(const T& in) { auto it = std::cbegin(in); auto end = std::cend(in); @@ -210,7 +210,7 @@ char ToUpper(char c) { return std::toupper(c, std::locale::classic()); } -template +template std::string ToUpper(const T& in) { auto it = std::cbegin(in); auto end = std::cend(in); @@ -239,7 +239,7 @@ bool StringEqualNoCaseN(const char* a, const char* b, size_t length) { return true; } -template +template inline T MultiplyWithOverflowCheck(T a, T b) { auto ret = a * b; if (a != 0) @@ -485,22 +485,19 @@ v8::Local ConvertNumberToV8Value(v8::Isolate* isolate, return v8::Number::New(isolate, static_cast(number)); } -template +template v8::MaybeLocal ToV8Value(v8::Local context, - const T& number, - v8::Isolate* isolate) { + const T& number, + v8::Isolate* isolate) { if (isolate == nullptr) isolate = v8::Isolate::GetCurrent(); return ConvertNumberToV8Value(isolate, number); } template + requires std::is_arithmetic_v v8::Local ToV8ValuePrimitiveArray(v8::Local context, - const std::vector& vec, - v8::Isolate* isolate) { - static_assert( - std::is_same_v || std::is_integral_v || - std::is_floating_point_v, - "Only primitive types (bool, integral, floating-point) are supported."); + const std::vector& vec, + v8::Isolate* isolate) { if (isolate == nullptr) isolate = v8::Isolate::GetCurrent(); v8::EscapableHandleScope handle_scope(isolate); @@ -550,6 +547,7 @@ void MaybeStackBuffer::AllocateSufficientStorage( } template + requires (sizeof(T) == 1) ArrayBufferViewContents::ArrayBufferViewContents( v8::Local value) { DCHECK(value->IsArrayBufferView() || value->IsSharedArrayBuffer() || @@ -558,6 +556,7 @@ ArrayBufferViewContents::ArrayBufferViewContents( } template + requires (sizeof(T) == 1) ArrayBufferViewContents::ArrayBufferViewContents( v8::Local value) { CHECK(value->IsArrayBufferView()); @@ -565,14 +564,15 @@ ArrayBufferViewContents::ArrayBufferViewContents( } template + requires (sizeof(T) == 1) ArrayBufferViewContents::ArrayBufferViewContents( v8::Local abv) { Read(abv); } template + requires (sizeof(T) == 1) void ArrayBufferViewContents::Read(v8::Local abv) { - static_assert(sizeof(T) == 1, "Only supports one-byte data at the moment"); length_ = abv->ByteLength(); if (length_ > sizeof(stack_storage_) || abv->HasBuffer()) { auto buf_data = abv->Buffer()->Data(); @@ -585,8 +585,8 @@ void ArrayBufferViewContents::Read(v8::Local abv) { } template + requires (sizeof(T) == 1) void ArrayBufferViewContents::ReadValue(v8::Local buf) { - static_assert(sizeof(T) == 1, "Only supports one-byte data at the moment"); DCHECK(buf->IsArrayBufferView() || buf->IsSharedArrayBuffer() || buf->IsArrayBuffer()); @@ -649,10 +649,7 @@ constexpr std::string_view FastStringKey::as_string_view() const { } // Converts a V8 numeric value to a corresponding C++ primitive or enum type. -template ::is_specialized || - std::is_enum_v>> +template T FromV8Value(v8::Local value) { if constexpr (std::is_enum_v) { using Underlying = std::underlying_type_t; diff --git a/src/util.h b/src/util.h index 48305bfdc131..bebf0d7ab0b4 100644 --- a/src/util.h +++ b/src/util.h @@ -30,6 +30,7 @@ #include "v8.h" #include "node.h" +#include "node_concepts.h" #include "node_exit_code.h" #include @@ -40,6 +41,7 @@ #include #include +#include #include #include #include @@ -99,7 +101,7 @@ inline char* Calloc(size_t n); inline char* UncheckedMalloc(size_t n); inline char* UncheckedCalloc(size_t n); -template +template inline T MultiplyWithOverflowCheck(T a, T b); namespace per_process { @@ -366,12 +368,12 @@ inline v8::Local FIXED_ONE_BYTE_STRING(v8::Isolate* isolate, // tolower() is locale-sensitive. Use ToLower() instead. inline char ToLower(char c); -template +template inline std::string ToLower(const T& in); // toupper() is locale-sensitive. Use ToUpper() instead. inline char ToUpper(char c); -template +template inline std::string ToUpper(const T& in); // strcasecmp() is locale-sensitive. Use StringEqualNoCase() instead. @@ -534,6 +536,7 @@ class MaybeStackBuffer { // or for small data, a copy of it. This object's lifetime is bound to the // original ArrayBufferView's lifetime. template + requires (sizeof(T) == 1) class ArrayBufferViewContents { public: ArrayBufferViewContents() = default; @@ -610,7 +613,7 @@ class BufferValue : public MaybeStackBuffer { // silence a compiler warning about that. template inline void USE(T&&) {} -template +template struct OnScopeLeaveImpl { Fn fn_; bool active_; @@ -630,7 +633,7 @@ struct OnScopeLeaveImpl { // auto on_scope_leave = OnScopeLeave([&] { // // ... run some code ... // }); -template +template inline MUST_USE_RESULT OnScopeLeaveImpl OnScopeLeave(Fn&& fn) { return OnScopeLeaveImpl{std::move(fn)}; } @@ -710,11 +713,10 @@ inline v8::MaybeLocal ToV8Value(v8::Local context, inline v8::MaybeLocal ToV8Value(v8::Local context, v8_inspector::StringView str, v8::Isolate* isolate); -template ::is_specialized, bool>::type> +template inline v8::MaybeLocal ToV8Value(v8::Local context, - const T& number, - v8::Isolate* isolate = nullptr); + const T& number, + v8::Isolate* isolate = nullptr); template inline v8::MaybeLocal ToV8Value(v8::Local context, const std::vector& vec, From 679745a2dc678d3464e23738e341cdda64a2c200 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sat, 8 Aug 2026 15:22:32 -0700 Subject: [PATCH 2/3] src: apply more template/concept modernizations Signed-off-by: James M Snell --- src/aliased_buffer-inl.h | 20 ++++++++++++++++++++ src/aliased_buffer.h | 3 +-- src/blob_serializer_deserializer-inl.h | 20 ++++++++++---------- src/blob_serializer_deserializer.h | 10 ++++++++++ src/debug_utils-inl.h | 14 ++++---------- src/json_utils.h | 6 +++--- src/memory_tracker-inl.h | 2 +- src/memory_tracker.h | 6 ++---- src/node_concepts.h | 17 ++++++++++++++++- src/node_platform.h | 9 +++------ src/node_realm-inl.h | 4 +--- src/node_realm.h | 2 +- src/node_snapshotable.h | 13 +++---------- src/req_wrap-inl.h | 3 +-- src/tracing/trace_event_legacy_inl.h | 7 ++++--- src/util-inl.h | 19 +++++++++---------- src/util.h | 23 +++++------------------ 17 files changed, 94 insertions(+), 84 deletions(-) diff --git a/src/aliased_buffer-inl.h b/src/aliased_buffer-inl.h index da2c45321f6d..5cb36e707ce2 100644 --- a/src/aliased_buffer-inl.h +++ b/src/aliased_buffer-inl.h @@ -12,6 +12,7 @@ namespace node { typedef size_t AliasedBufferIndex; template + requires std::is_scalar_v AliasedBufferBase::AliasedBufferBase( v8::Isolate* isolate, const size_t count, const AliasedBufferIndex* index) : isolate_(isolate), count_(count), byte_offset_(0), index_(index) { @@ -34,6 +35,7 @@ AliasedBufferBase::AliasedBufferBase( } template + requires std::is_scalar_v AliasedBufferBase::AliasedBufferBase( v8::Isolate* isolate, const size_t byte_offset, @@ -65,6 +67,7 @@ AliasedBufferBase::AliasedBufferBase( } template + requires std::is_scalar_v AliasedBufferBase::AliasedBufferBase( const AliasedBufferBase& that) : isolate_(that.isolate_), @@ -76,6 +79,7 @@ AliasedBufferBase::AliasedBufferBase( } template + requires std::is_scalar_v AliasedBufferIndex AliasedBufferBase::Serialize( v8::Local context, v8::SnapshotCreator* creator) { DCHECK(is_valid()); @@ -83,6 +87,7 @@ AliasedBufferIndex AliasedBufferBase::Serialize( } template + requires std::is_scalar_v inline void AliasedBufferBase::Deserialize( v8::Local context) { DCHECK_NOT_NULL(index_); @@ -99,6 +104,7 @@ inline void AliasedBufferBase::Deserialize( } template + requires std::is_scalar_v AliasedBufferBase& AliasedBufferBase::operator=( AliasedBufferBase&& that) noexcept { DCHECK(is_valid()); @@ -116,41 +122,48 @@ AliasedBufferBase& AliasedBufferBase::operator=( } template + requires std::is_scalar_v v8::Local AliasedBufferBase::GetJSArray() const { DCHECK(is_valid()); return js_array_.Get(isolate_); } template + requires std::is_scalar_v void AliasedBufferBase::Release() { DCHECK_NULL(index_); js_array_.Reset(); } template + requires std::is_scalar_v inline void AliasedBufferBase::MakeWeak() { DCHECK(is_valid()); js_array_.SetWeak(); } template + requires std::is_scalar_v v8::Local AliasedBufferBase::GetArrayBuffer() const { return GetJSArray()->Buffer(); } template + requires std::is_scalar_v inline const NativeT* AliasedBufferBase::GetNativeBuffer() const { DCHECK(is_valid()); return buffer_; } template + requires std::is_scalar_v inline const NativeT* AliasedBufferBase::operator*() const { return GetNativeBuffer(); } template + requires std::is_scalar_v inline void AliasedBufferBase::SetValue(const size_t index, NativeT value) { DCHECK_LT(index, count_); @@ -159,6 +172,7 @@ inline void AliasedBufferBase::SetValue(const size_t index, } template + requires std::is_scalar_v inline const NativeT AliasedBufferBase::GetValue( const size_t index) const { DCHECK(is_valid()); @@ -167,6 +181,7 @@ inline const NativeT AliasedBufferBase::GetValue( } template + requires std::is_scalar_v typename AliasedBufferBase::Reference AliasedBufferBase::operator[](size_t index) { DCHECK(is_valid()); @@ -174,16 +189,19 @@ AliasedBufferBase::operator[](size_t index) { } template + requires std::is_scalar_v NativeT AliasedBufferBase::operator[](size_t index) const { return GetValue(index); } template + requires std::is_scalar_v size_t AliasedBufferBase::Length() const { return count_; } template + requires std::is_scalar_v void AliasedBufferBase::reserve(size_t new_capacity) { DCHECK(is_valid()); DCHECK_GE(new_capacity, count_); @@ -214,11 +232,13 @@ void AliasedBufferBase::reserve(size_t new_capacity) { } template + requires std::is_scalar_v inline bool AliasedBufferBase::is_valid() const { return index_ == nullptr && !js_array_.IsEmpty(); } template + requires std::is_scalar_v inline size_t AliasedBufferBase::SelfSize() const { return sizeof(*this); } diff --git a/src/aliased_buffer.h b/src/aliased_buffer.h index 8e988bcb010d..ff2b961724d6 100644 --- a/src/aliased_buffer.h +++ b/src/aliased_buffer.h @@ -29,10 +29,9 @@ typedef size_t AliasedBufferIndex; * observed. Any notification APIs will be left as a future exercise. */ template + requires std::is_scalar_v class AliasedBufferBase final : public MemoryRetainer { public: - static_assert(std::is_scalar_v); - AliasedBufferBase(v8::Isolate* isolate, size_t count, const AliasedBufferIndex* index = nullptr); diff --git a/src/blob_serializer_deserializer-inl.h b/src/blob_serializer_deserializer-inl.h index fa258dbe8144..2a2aed1171f7 100644 --- a/src/blob_serializer_deserializer-inl.h +++ b/src/blob_serializer_deserializer-inl.h @@ -90,8 +90,8 @@ std::string BlobSerializerDeserializer::GetName() const { // Helper for reading numeric types. template template + requires std::is_arithmetic_v T BlobDeserializer::ReadArithmetic() { - static_assert(std::is_arithmetic_v, "Not an arithmetic type"); T result; ReadArithmetic(&result, 1); return result; @@ -158,8 +158,8 @@ std::string_view BlobDeserializer::ReadStringView(StringLogMode mode) { // Helper for reading an array of numeric types. template template + requires std::is_arithmetic_v void BlobDeserializer::ReadArithmetic(T* out, size_t count) { - static_assert(std::is_arithmetic_v, "Not an arithmetic type"); DCHECK_GT(count, 0); // Should not read contents for vectors of size 0. if (is_debug) { std::string name = GetName(); @@ -180,8 +180,8 @@ void BlobDeserializer::ReadArithmetic(T* out, size_t count) { // Helper for reading numeric vectors. template template + requires std::is_arithmetic_v std::vector BlobDeserializer::ReadArithmeticVector(size_t count) { - static_assert(std::is_arithmetic_v, "Not an arithmetic type"); DCHECK_GT(count, 0); // Should not read contents for vectors of size 0. std::vector result(count); ReadArithmetic(result.data(), count); @@ -191,8 +191,8 @@ std::vector BlobDeserializer::ReadArithmeticVector(size_t count) { // Helper for reading non-numeric vectors. template template + requires(!std::is_arithmetic_v) std::vector BlobDeserializer::ReadNonArithmeticVector(size_t count) { - static_assert(!std::is_arithmetic_v, "Arithmetic type"); DCHECK_GT(count, 0); // Should not read contents for vectors of size 0. std::vector result; result.reserve(count); @@ -224,8 +224,8 @@ T BlobDeserializer::ReadElement() { // Helper for writing numeric types. template template + requires std::is_arithmetic_v size_t BlobSerializer::WriteArithmetic(const T& data) { - static_assert(std::is_arithmetic_v, "Not an arithmetic type"); return WriteArithmetic(&data, 1); } @@ -303,8 +303,8 @@ static size_t kPreviewCount = 16; // Helper for writing an array of numeric types. template template + requires std::is_arithmetic_v size_t BlobSerializer::WriteArithmetic(const T* data, size_t count) { - static_assert(std::is_arithmetic_v, "Arithmetic type"); DCHECK_GT(count, 0); // Should not write contents for vectors of size 0. if (is_debug) { size_t preview_count = count < kPreviewCount ? count : kPreviewCount; @@ -338,18 +338,18 @@ size_t BlobSerializer::WriteArithmetic(const T* data, size_t count) { // Helper for writing numeric vectors. template template + requires std::is_arithmetic_v size_t BlobSerializer::WriteArithmeticVector( const std::vector& data) { - static_assert(std::is_arithmetic_v, "Arithmetic type"); return WriteArithmetic(data.data(), data.size()); } // Helper for writing non-numeric vectors. template template -size_t BlobSerializer::WriteNonArithmeticVector( - const std::vector& data) { - static_assert(!std::is_arithmetic_v, "Arithmetic type"); + requires(!std::is_arithmetic_v) +size_t + BlobSerializer::WriteNonArithmeticVector(const std::vector& data) { DCHECK_GT(data.size(), 0); // Should not write contents for vectors of size 0. size_t written_total = 0; diff --git a/src/blob_serializer_deserializer.h b/src/blob_serializer_deserializer.h index fe7989e22a35..cd63d0477675 100644 --- a/src/blob_serializer_deserializer.h +++ b/src/blob_serializer_deserializer.h @@ -1,7 +1,9 @@ #ifndef SRC_BLOB_SERIALIZER_DESERIALIZER_H_ #define SRC_BLOB_SERIALIZER_DESERIALIZER_H_ +#include #include +#include #include #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS @@ -49,6 +51,7 @@ class BlobDeserializer : public BlobSerializerDeserializer { // Helper for reading numeric types. template + requires std::is_arithmetic_v T ReadArithmetic(); // Layout of vectors: @@ -63,15 +66,18 @@ class BlobDeserializer : public BlobSerializerDeserializer { // Helper for reading an array of numeric types. template + requires std::is_arithmetic_v void ReadArithmetic(T* out, size_t count); // Helper for reading numeric vectors. template + requires std::is_arithmetic_v std::vector ReadArithmeticVector(size_t count); private: // Helper for reading non-numeric vectors. template + requires(!std::is_arithmetic_v) std::vector ReadNonArithmeticVector(size_t count); template @@ -94,6 +100,7 @@ class BlobSerializer : public BlobSerializerDeserializer { // Helper for writing numeric types. template + requires std::is_arithmetic_v size_t WriteArithmetic(const T& data); // Layout of vectors: @@ -110,15 +117,18 @@ class BlobSerializer : public BlobSerializerDeserializer { // Helper for writing an array of numeric types. template + requires std::is_arithmetic_v size_t WriteArithmetic(const T* data, size_t count); // Helper for writing numeric vectors. template + requires std::is_arithmetic_v size_t WriteArithmeticVector(const std::vector& data); private: // Helper for writing non-numeric vectors. template + requires(!std::is_arithmetic_v) size_t WriteNonArithmeticVector(const std::vector& data); template diff --git a/src/debug_utils-inl.h b/src/debug_utils-inl.h index 3afab8629968..5f739246825a 100644 --- a/src/debug_utils-inl.h +++ b/src/debug_utils-inl.h @@ -49,10 +49,7 @@ struct ToStringHelper { return value.ToStringView(); } - template || std::is_enum_v, bool>, - typename dummy = bool> + template static std::string Convert(const T& value) { return std::to_string(value); } @@ -81,9 +78,7 @@ struct ToStringHelper { return utf8_value.ToString(); } - template >> + template static std::string BaseConvert(const T& value) { auto v = static_cast(value); char ret[3 * sizeof(T)]; @@ -96,9 +91,8 @@ struct ToStringHelper { } while ((v >>= BASE_BITS) != 0); return ptr; } - template >> + template + requires(!std::integral) static auto BaseConvert(T&& value) { return Convert(std::forward(value)); } diff --git a/src/json_utils.h b/src/json_utils.h index 06d4a7ac0905..e75556578522 100644 --- a/src/json_utils.h +++ b/src/json_utils.h @@ -3,6 +3,8 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include "node_concepts.h" + #include #include #include @@ -132,9 +134,7 @@ class JSONWriter { }; private: - template ::is_specialized, bool>::type> + template inline void write_value(T number) { if constexpr (std::is_same::value) out_ << (number ? "true" : "false"); diff --git a/src/memory_tracker-inl.h b/src/memory_tracker-inl.h index a59452fd994c..3c82983ce01e 100644 --- a/src/memory_tracker-inl.h +++ b/src/memory_tracker-inl.h @@ -203,7 +203,7 @@ void MemoryTracker::TrackField(const char* edge_name, TrackField(edge_name, container, node_name, element_name); } -template +template void MemoryTracker::TrackField(const char* edge_name, const T& value, const char* node_name) { diff --git a/src/memory_tracker.h b/src/memory_tracker.h index 88987c954f4d..d7893f10b3af 100644 --- a/src/memory_tracker.h +++ b/src/memory_tracker.h @@ -2,6 +2,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include "node_concepts.h" #include "v8-profiler.h" #include @@ -229,10 +230,7 @@ class MemoryTracker { inline void TrackField(const char* edge_name, const std::basic_string& value, const char* node_name = nullptr); - template ::is_specialized, bool>::type, - typename dummy = bool> + template inline void TrackField(const char* edge_name, const T& value, const char* node_name = nullptr); diff --git a/src/node_concepts.h b/src/node_concepts.h index 02c2f15299bc..5dbce5c6f88a 100644 --- a/src/node_concepts.h +++ b/src/node_concepts.h @@ -10,13 +10,28 @@ namespace node { // A numeric type recognized by std::numeric_limits. +// The is_array guard prevents a hard error from instantiating +// numeric_limits, whose member functions would return array types. template -concept NumericValue = std::numeric_limits::is_specialized; +concept NumericValue = ! +std::is_array_v&& std::numeric_limits::is_specialized; // A numeric type or an enum (which has an underlying numeric type). template concept NumericOrEnum = NumericValue || std::is_enum_v; +// A type that has a valid std::char_traits specialization, as required by +// std::basic_string and std::basic_string_view. +template +concept StandardCharType = + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v; + +// Test whether some value can be called with (). +template +concept IsCallable = std::is_function::value || requires { &T::operator(); }; + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/node_platform.h b/src/node_platform.h index f47e2a46b66b..e98ecf322802 100644 --- a/src/node_platform.h +++ b/src/node_platform.h @@ -3,6 +3,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include #include #include #include @@ -20,12 +21,8 @@ class NodePlatform; class IsolateData; class PerIsolatePlatformData; -template -struct has_priority : std::false_type {}; - template -struct has_priority().priority)>> - : std::true_type {}; +concept has_priority = requires(T t) { t.priority; }; template class TaskQueue { @@ -35,7 +32,7 @@ class TaskQueue { struct EntryCompare { bool operator()(const std::unique_ptr& a, const std::unique_ptr& b) const { - if constexpr (has_priority::value) { + if constexpr (has_priority) { return a->priority < b->priority; } else { return false; diff --git a/src/node_realm-inl.h b/src/node_realm-inl.h index b004bd1e1500..394ece5a8ace 100644 --- a/src/node_realm-inl.h +++ b/src/node_realm-inl.h @@ -92,10 +92,8 @@ inline T* Realm::GetBindingData() { return result; } -template +template T, typename... Args> inline T* Realm::AddBindingData(v8::Local target, Args&&... args) { - // This won't compile if T is not a BaseObject subclass. - static_assert(std::is_base_of_v); // The binding data must be weak so that it won't keep the realm reachable // from strong GC roots indefinitely. The wrapper object of binding data // should be referenced from JavaScript, thus the binding data should be diff --git a/src/node_realm.h b/src/node_realm.h index 2ed04aa271b4..690beaf1a1aa 100644 --- a/src/node_realm.h +++ b/src/node_realm.h @@ -127,7 +127,7 @@ class Realm : public MemoryRetainer { // Methods created using SetMethod(), SetPrototypeMethod(), etc. inside // this scope can access the created T* object using // GetBindingData(args) later. - template + template T, typename... Args> T* AddBindingData(v8::Local target, Args&&... args); template static inline T* GetBindingData(const v8::PropertyCallbackInfo& info); diff --git a/src/node_snapshotable.h b/src/node_snapshotable.h index 31be74bcfd56..18ef5fbd43fd 100644 --- a/src/node_snapshotable.h +++ b/src/node_snapshotable.h @@ -41,11 +41,8 @@ struct InternalFieldInfoBase { EmbedderObjectType type; size_t length; - template + template T> static T* New(EmbedderObjectType type) { - static_assert(std::is_base_of_v || - std::is_same_v, - "Can only accept InternalFieldInfoBase subclasses"); void* buf = ::operator new[](sizeof(T)); memset(buf, 0, sizeof(T)); // Make the padding reproducible. T* result = new (buf) T; @@ -54,13 +51,9 @@ struct InternalFieldInfoBase { return result; } - template + template T> + requires std::is_trivially_copyable_v T* Copy() const { - static_assert(std::is_base_of_v || - std::is_same_v, - "Can only accept InternalFieldInfoBase subclasses"); - static_assert(std::is_trivially_copyable_v, - "Can only memcpy trivially copyable class"); void* buf = ::operator new[](sizeof(T)); T* result = new (buf) T; memcpy(result, this, sizeof(T)); diff --git a/src/req_wrap-inl.h b/src/req_wrap-inl.h index ac0ca8921a1e..61d6d79116ad 100644 --- a/src/req_wrap-inl.h +++ b/src/req_wrap-inl.h @@ -111,8 +111,7 @@ struct CallLibuvFunction { template struct MakeLibuvRequestCallback { static T For(ReqWrap* req_wrap, T v) { - static_assert(!is_callable, - "MakeLibuvRequestCallback missed a callback"); + static_assert(!IsCallable, "MakeLibuvRequestCallback missed a callback"); return v; } }; diff --git a/src/tracing/trace_event_legacy_inl.h b/src/tracing/trace_event_legacy_inl.h index 4fadb6d8e38f..9030473afe96 100644 --- a/src/tracing/trace_event_legacy_inl.h +++ b/src/tracing/trace_event_legacy_inl.h @@ -9,6 +9,8 @@ #error Perfetto is enabled. #endif +#include + #include "v8-platform.h" #include "tracing/agent_legacy.h" #include "tracing/trace_event_helper.h" @@ -558,9 +560,8 @@ static inline void SetTraceValue(v8::ConvertableToTraceFormat* convertable_value *value = static_cast(reinterpret_cast(convertable_value)); } -template -static inline typename std::enable_if< - std::is_convertible::value>::type +template T> +static inline void SetTraceValue(std::unique_ptr ptr, unsigned char* type, uint64_t* value) { SetTraceValue(ptr.release(), type, value); } diff --git a/src/util-inl.h b/src/util-inl.h index e36133646b8f..4ac8726aa43f 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -487,8 +487,8 @@ v8::Local ConvertNumberToV8Value(v8::Isolate* isolate, template v8::MaybeLocal ToV8Value(v8::Local context, - const T& number, - v8::Isolate* isolate) { + const T& number, + v8::Isolate* isolate) { if (isolate == nullptr) isolate = v8::Isolate::GetCurrent(); return ConvertNumberToV8Value(isolate, number); } @@ -496,9 +496,8 @@ v8::MaybeLocal ToV8Value(v8::Local context, template requires std::is_arithmetic_v v8::Local ToV8ValuePrimitiveArray(v8::Local context, - const std::vector& vec, - v8::Isolate* isolate) { - + const std::vector& vec, + v8::Isolate* isolate) { if (isolate == nullptr) isolate = v8::Isolate::GetCurrent(); v8::EscapableHandleScope handle_scope(isolate); @@ -547,7 +546,7 @@ void MaybeStackBuffer::AllocateSufficientStorage( } template - requires (sizeof(T) == 1) + requires(sizeof(T) == 1) ArrayBufferViewContents::ArrayBufferViewContents( v8::Local value) { DCHECK(value->IsArrayBufferView() || value->IsSharedArrayBuffer() || @@ -556,7 +555,7 @@ ArrayBufferViewContents::ArrayBufferViewContents( } template - requires (sizeof(T) == 1) + requires(sizeof(T) == 1) ArrayBufferViewContents::ArrayBufferViewContents( v8::Local value) { CHECK(value->IsArrayBufferView()); @@ -564,14 +563,14 @@ ArrayBufferViewContents::ArrayBufferViewContents( } template - requires (sizeof(T) == 1) + requires(sizeof(T) == 1) ArrayBufferViewContents::ArrayBufferViewContents( v8::Local abv) { Read(abv); } template - requires (sizeof(T) == 1) + requires(sizeof(T) == 1) void ArrayBufferViewContents::Read(v8::Local abv) { length_ = abv->ByteLength(); if (length_ > sizeof(stack_storage_) || abv->HasBuffer()) { @@ -585,7 +584,7 @@ void ArrayBufferViewContents::Read(v8::Local abv) { } template - requires (sizeof(T) == 1) + requires(sizeof(T) == 1) void ArrayBufferViewContents::ReadValue(v8::Local buf) { DCHECK(buf->IsArrayBufferView() || buf->IsSharedArrayBuffer() || buf->IsArrayBuffer()); diff --git a/src/util.h b/src/util.h index bebf0d7ab0b4..a979f626bdb4 100644 --- a/src/util.h +++ b/src/util.h @@ -392,14 +392,6 @@ constexpr size_t strsize(const T (&)[N]) { return N - 1; } -// A type that has a valid std::char_traits specialization, as required by -// std::basic_string and std::basic_string_view. -template -concept standard_char_type = - std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v || - std::is_same_v; - // Allocates an array of member type T. For up to kStackStorageSize items, // the stack is used, otherwise malloc(). template @@ -513,11 +505,11 @@ class MaybeStackBuffer { free(buf_); } - template + template inline std::basic_string ToString() const { return {out(), length()}; } - template + template inline std::basic_string_view ToStringView() const { return {out(), length()}; } @@ -536,7 +528,7 @@ class MaybeStackBuffer { // or for small data, a copy of it. This object's lifetime is bound to the // original ArrayBufferView's lifetime. template - requires (sizeof(T) == 1) + requires(sizeof(T) == 1) class ArrayBufferViewContents { public: ArrayBufferViewContents() = default; @@ -679,11 +671,6 @@ struct MallocedBuffer { MallocedBuffer& operator=(const MallocedBuffer&) = delete; }; -// Test whether some value can be called with (). -template -concept is_callable = - std::is_function::value || requires { &T::operator(); }; - template struct FunctionDeleter { void operator()(T* pointer) const { function(pointer); } @@ -715,8 +702,8 @@ inline v8::MaybeLocal ToV8Value(v8::Local context, v8::Isolate* isolate); template inline v8::MaybeLocal ToV8Value(v8::Local context, - const T& number, - v8::Isolate* isolate = nullptr); + const T& number, + v8::Isolate* isolate = nullptr); template inline v8::MaybeLocal ToV8Value(v8::Local context, const std::vector& vec, From 5613c5c13df1bbea92976ffac91a7cf146eadc03 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sat, 8 Aug 2026 15:37:55 -0700 Subject: [PATCH 3/3] src: apply more concept modernizations Signed-off-by: James M Snell --- src/crypto/crypto_aes.cc | 2 +- src/crypto/crypto_util.h | 2 +- src/node_i18n.cc | 4 +--- src/node_report.h | 2 +- src/node_sea.cc | 10 ++++------ src/node_snapshotable.cc | 12 +++++------- src/node_wasi.cc | 8 ++++---- src/stream_base-inl.h | 31 +++++++++++++------------------ src/stream_base.h | 7 +++---- src/stream_wrap.cc | 6 ++---- 10 files changed, 35 insertions(+), 49 deletions(-) diff --git a/src/crypto/crypto_aes.cc b/src/crypto/crypto_aes.cc index 171688b92926..bea8f5b24be5 100644 --- a/src/crypto/crypto_aes.cc +++ b/src/crypto/crypto_aes.cc @@ -259,7 +259,7 @@ WebCryptoCipherStatus AES_KW_Cipher(Environment* env, // implementation here: // https://github.com/chromium/chromium/blob/7af6cfd/components/webcrypto/algorithms/aes_ctr.cc -template +template T CeilDiv(T a, T b) { return a == 0 ? 0 : 1 + (a - 1) / b; } diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h index 5c9dad13196b..dd7e0842a29b 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -732,8 +732,8 @@ class ArrayBufferOrViewContents final { } template + requires(sizeof(M) == 1) void CopyTo(M* dest, size_t len) const { - static_assert(sizeof(M) == 1, "sizeof(M) must equal 1"); len = std::min(len, size()); if (len > 0 && data() != nullptr) { memcpy(dest, data(), len); diff --git a/src/node_i18n.cc b/src/node_i18n.cc index 3c4f419aa294..96ec2f6ee5ab 100644 --- a/src/node_i18n.cc +++ b/src/node_i18n.cc @@ -103,14 +103,12 @@ namespace i18n { namespace { template + requires(sizeof(T) == 1 || sizeof(T) == 2) MaybeLocal ToBufferEndian(Environment* env, MaybeStackBuffer* buf) { Local ret; if (!Buffer::New(env, buf).ToLocal(&ret)) { return {}; } - - static_assert(sizeof(T) == 1 || sizeof(T) == 2, - "Currently only one- or two-byte buffers are supported"); if constexpr (sizeof(T) > 1 && IsBigEndian()) { SPREAD_BUFFER_ARG(ret, retbuf); CHECK(nbytes::SwapBytes16(retbuf_data, retbuf_length)); diff --git a/src/node_report.h b/src/node_report.h index 98be339ae90d..ae1e03df625b 100644 --- a/src/node_report.h +++ b/src/node_report.h @@ -22,7 +22,7 @@ namespace report { void WalkHandleNetwork(uv_handle_t* h, void* arg); void WalkHandleNoNetwork(uv_handle_t* h, void* arg); -template +template std::string ValueToHexString(T value) { std::stringstream hex; diff --git a/src/node_sea.cc b/src/node_sea.cc index 1be41e6f1414..aee057193642 100644 --- a/src/node_sea.cc +++ b/src/node_sea.cc @@ -66,9 +66,8 @@ class SeaSerializer : public BlobSerializer { : BlobSerializer( per_process::enabled_debug_list.enabled(DebugCategory::SEA)) {} - template ::value>* = nullptr, - std::enable_if_t::value>* = nullptr> + template + requires(!std::is_arithmetic_v && !std::same_as) size_t Write(const T& data); }; @@ -150,9 +149,8 @@ class SeaDeserializer : public BlobDeserializer { : BlobDeserializer( per_process::enabled_debug_list.enabled(DebugCategory::SEA), v) {} - template ::value>* = nullptr, - std::enable_if_t::value>* = nullptr> + template + requires(!std::is_arithmetic_v && !std::same_as) T Read(); }; diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc index f90e5f28ac28..7f5d9b9e1821 100644 --- a/src/node_snapshotable.cc +++ b/src/node_snapshotable.cc @@ -155,9 +155,8 @@ class SnapshotDeserializer : public BlobDeserializer { DebugCategory::SNAPSHOT_SERDES), v) {} - template ::value>* = nullptr, - std::enable_if_t::value>* = nullptr> + template + requires(!std::is_arithmetic_v && !std::same_as) T Read(); }; @@ -172,9 +171,8 @@ class SnapshotSerializer : public BlobSerializer { sink.reserve(4 * 1024 * 1024); } - template ::value>* = nullptr, - std::enable_if_t::value>* = nullptr> + template + requires(!std::is_arithmetic_v && !std::same_as) size_t Write(const T& data); }; @@ -733,13 +731,13 @@ static std::string FormatSize(size_t size) { } template + requires(std::same_as || std::same_as) void WriteByteVectorLiteral(std::ostream* ss, const T* vec, size_t size, const char* var_name, bool use_array_literals) { constexpr bool is_uint8_t = std::is_same_v; - static_assert(is_uint8_t || std::is_same_v); constexpr const char* type_name = is_uint8_t ? "uint8_t" : "char"; if (!use_array_literals) { const uint8_t* data = reinterpret_cast(vec); diff --git a/src/node_wasi.cc b/src/node_wasi.cc index 5cd9115611f1..6c2adc826571 100644 --- a/src/node_wasi.cc +++ b/src/node_wasi.cc @@ -358,8 +358,8 @@ template ::value, bool> = true> + std::size_t... Indices> + requires(!std::is_void_v) inline void CallAndSetReturn(std::index_sequence, const FunctionCallbackInfo& args, WASI* wasi, @@ -372,8 +372,8 @@ template ::value, bool> = true> + std::size_t... Indices> + requires std::is_void_v inline void CallAndSetReturn(std::index_sequence, const FunctionCallbackInfo& args, WASI* wasi, diff --git a/src/stream_base-inl.h b/src/stream_base-inl.h index 4418cdad504f..cc468ee566a7 100644 --- a/src/stream_base-inl.h +++ b/src/stream_base-inl.h @@ -98,25 +98,20 @@ StreamBase::StreamBase(Environment* env) : env_(env) { PushStreamListener(&default_listener_); } -template +template OtherBase> SimpleShutdownWrap::SimpleShutdownWrap( - StreamBase* stream, - v8::Local req_wrap_obj) - : ShutdownWrap(stream, req_wrap_obj), - OtherBase(stream->stream_env(), - req_wrap_obj, - AsyncWrap::PROVIDER_SHUTDOWNWRAP) { -} - -template -SimpleWriteWrap::SimpleWriteWrap( - StreamBase* stream, - v8::Local req_wrap_obj) - : WriteWrap(stream, req_wrap_obj), - OtherBase(stream->stream_env(), - req_wrap_obj, - AsyncWrap::PROVIDER_WRITEWRAP) { -} + StreamBase* stream, v8::Local req_wrap_obj) + : ShutdownWrap(stream, req_wrap_obj), + OtherBase(stream->stream_env(), + req_wrap_obj, + AsyncWrap::PROVIDER_SHUTDOWNWRAP) {} + +template OtherBase> +SimpleWriteWrap::SimpleWriteWrap(StreamBase* stream, + v8::Local req_wrap_obj) + : WriteWrap(stream, req_wrap_obj), + OtherBase( + stream->stream_env(), req_wrap_obj, AsyncWrap::PROVIDER_WRITEWRAP) {} void StreamBase::AttachToObject(v8::Local obj) { obj->SetAlignedPointerInInternalField( diff --git a/src/stream_base.h b/src/stream_base.h index be00134eb1fc..cb795a541297 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -432,12 +432,11 @@ class StreamBase : public StreamResource { friend class Environment; // For kNumStreamBaseStateFields. }; - // These are helpers for creating `ShutdownWrap`/`WriteWrap` instances. // `OtherBase` must have a constructor that matches the `AsyncWrap` -// constructors’s (Environment*, Local, AsyncWrap::Provider) signature +// constructors's (Environment*, Local, AsyncWrap::Provider) signature // and be a subclass of `AsyncWrap`. -template +template OtherBase> class SimpleShutdownWrap : public ShutdownWrap, public OtherBase { public: enum InternalFields { @@ -459,7 +458,7 @@ class SimpleShutdownWrap : public ShutdownWrap, public OtherBase { } }; -template +template OtherBase> class SimpleWriteWrap : public WriteWrap, public OtherBase { public: enum InternalFields { diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index b41f6ac74947..6b85d6533879 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -227,12 +227,10 @@ void LibuvStreamWrap::OnUvAlloc(size_t suggested_size, uv_buf_t* buf) { } template + requires(std::derived_from || + std::derived_from) static MaybeLocal AcceptHandle(Environment* env, LibuvStreamWrap* parent) { - static_assert(std::is_base_of::value || - std::is_base_of::value, - "Can only accept stream handles"); - EscapableHandleScope scope(env->isolate()); Local wrap_obj;