diff options
author | Maxim Yurchuk <maxim-yurchuk@ydb.tech> | 2024-11-13 16:32:39 +0300 |
---|---|---|
committer | Maxim Yurchuk <maxim-yurchuk@ydb.tech> | 2024-11-13 16:32:39 +0300 |
commit | 23387aafadce23ea77beffb2981e8c0e2f2a7f0a (patch) | |
tree | 54b0f95fca1ad03d93f5a18abdfe98e8ab34b420 /library/cpp | |
parent | ee51155da394b56a8e3329d25a514422e5da7bc3 (diff) | |
parent | 4ab23311f7a6d45ac318179569df9ba46fb9ab68 (diff) | |
download | ydb-23387aafadce23ea77beffb2981e8c0e2f2a7f0a.tar.gz |
Merge branch 'rightlib' into mergelibs-yurchuk-manual
Diffstat (limited to 'library/cpp')
-rw-r--r-- | library/cpp/blockcodecs/core/codecs.h | 11 | ||||
-rw-r--r-- | library/cpp/containers/concurrent_hash/concurrent_hash.h | 21 | ||||
-rw-r--r-- | library/cpp/http/misc/httpcodes.h | 1 | ||||
-rw-r--r-- | library/cpp/svnversion/test/main.cpp | 1 | ||||
-rw-r--r-- | library/cpp/tld/tlds-alpha-by-domain.txt | 2 | ||||
-rw-r--r-- | library/cpp/yt/assert/assert.h | 14 | ||||
-rw-r--r-- | library/cpp/yt/error/convert_to_cpo.h | 43 | ||||
-rw-r--r-- | library/cpp/yt/error/error_attributes-inl.h | 31 | ||||
-rw-r--r-- | library/cpp/yt/error/error_attributes.h | 13 | ||||
-rw-r--r-- | library/cpp/yt/memory/ref-inl.h | 11 | ||||
-rw-r--r-- | library/cpp/yt/memory/ref.cpp | 19 | ||||
-rw-r--r-- | library/cpp/yt/memory/ref.h | 31 | ||||
-rw-r--r-- | library/cpp/yt/misc/tag_invoke.h | 95 | ||||
-rw-r--r-- | library/cpp/yt/misc/tag_invoke_cpo.h | 25 | ||||
-rw-r--r-- | library/cpp/yt/misc/unittests/tag_invoke_cpo_ut.cpp | 107 | ||||
-rw-r--r-- | library/cpp/yt/misc/unittests/tag_invoke_impl_ut.cpp | 72 | ||||
-rw-r--r-- | library/cpp/yt/misc/unittests/ya.make | 2 |
17 files changed, 474 insertions, 25 deletions
diff --git a/library/cpp/blockcodecs/core/codecs.h b/library/cpp/blockcodecs/core/codecs.h index 6512abddefc..61efc04744a 100644 --- a/library/cpp/blockcodecs/core/codecs.h +++ b/library/cpp/blockcodecs/core/codecs.h @@ -26,13 +26,14 @@ namespace NBlockCodecs { { } - template <> - inline TData(const TString& t) - : TStringBuf((const char*)t.data(), t.size()) - { - } }; + template <> + inline TData::TData(const TString& t) + : TStringBuf((const char*)t.data(), t.size()) + { + } + struct TCodecError: public yexception { }; diff --git a/library/cpp/containers/concurrent_hash/concurrent_hash.h b/library/cpp/containers/concurrent_hash/concurrent_hash.h index 88da30fd78d..74573ecc095 100644 --- a/library/cpp/containers/concurrent_hash/concurrent_hash.h +++ b/library/cpp/containers/concurrent_hash/concurrent_hash.h @@ -12,11 +12,12 @@ namespace NPrivate { }; } -template <typename K, typename V, size_t BucketCount = 64, typename L = TAdaptiveLock> +template <typename K, typename V, size_t BucketCount = 64, typename L = TAdaptiveLock, class TLockOps = TCommonLockOps<L>> class TConcurrentHashMap { public: using TActualMap = THashMap<K, V>; using TLock = L; + using TBucketGuard = TGuard<TLock, TLockOps>; struct TBucket { friend class TConcurrentHashMap; @@ -88,13 +89,13 @@ public: void Insert(const K& key, const V& value) { TBucket& bucket = GetBucketForKey(key); - TGuard<TLock> guard(bucket.Mutex); + TBucketGuard guard(bucket.Mutex); bucket.Map[key] = value; } void InsertUnique(const K& key, const V& value) { TBucket& bucket = GetBucketForKey(key); - TGuard<TLock> guard(bucket.Mutex); + TBucketGuard guard(bucket.Mutex); if (!bucket.Map.insert(std::make_pair(key, value)).second) { Y_ABORT("non-unique key"); } @@ -102,14 +103,14 @@ public: V& InsertIfAbsent(const K& key, const V& value) { TBucket& bucket = GetBucketForKey(key); - TGuard<TLock> guard(bucket.Mutex); + TBucketGuard guard(bucket.Mutex); return bucket.Map.insert(std::make_pair(key, value)).first->second; } template <typename TKey, typename... Args> V& EmplaceIfAbsent(TKey&& key, Args&&... args) { TBucket& bucket = GetBucketForKey(key); - TGuard<TLock> guard(bucket.Mutex); + TBucketGuard guard(bucket.Mutex); if (V* value = bucket.TryGetUnsafe(key)) { return *value; } @@ -123,7 +124,7 @@ public: template <typename Callable> V& InsertIfAbsentWithInit(const K& key, Callable initFunc) { TBucket& bucket = GetBucketForKey(key); - TGuard<TLock> guard(bucket.Mutex); + TBucketGuard guard(bucket.Mutex); if (V* value = bucket.TryGetUnsafe(key)) { return *value; } @@ -133,13 +134,13 @@ public: V Get(const K& key) const { const TBucket& bucket = GetBucketForKey(key); - TGuard<TLock> guard(bucket.Mutex); + TBucketGuard guard(bucket.Mutex); return bucket.GetUnsafe(key); } bool Get(const K& key, V& result) const { const TBucket& bucket = GetBucketForKey(key); - TGuard<TLock> guard(bucket.Mutex); + TBucketGuard guard(bucket.Mutex); if (const V* value = bucket.TryGetUnsafe(key)) { result = *value; return true; @@ -149,13 +150,13 @@ public: V Remove(const K& key) { TBucket& bucket = GetBucketForKey(key); - TGuard<TLock> guard(bucket.Mutex); + TBucketGuard guard(bucket.Mutex); return bucket.RemoveUnsafe(key); } bool Has(const K& key) const { const TBucket& bucket = GetBucketForKey(key); - TGuard<TLock> guard(bucket.Mutex); + TBucketGuard guard(bucket.Mutex); return bucket.HasUnsafe(key); } }; diff --git a/library/cpp/http/misc/httpcodes.h b/library/cpp/http/misc/httpcodes.h index 4565b1c581b..b9407ec8eec 100644 --- a/library/cpp/http/misc/httpcodes.h +++ b/library/cpp/http/misc/httpcodes.h @@ -57,6 +57,7 @@ enum HttpCodes { HTTP_PRECONDITION_REQUIRED = 428, HTTP_TOO_MANY_REQUESTS = 429, HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + HTTP_REQUESTED_HOST_UNAVAILABLE = 434, HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451, HTTP_INTERNAL_SERVER_ERROR = 500, diff --git a/library/cpp/svnversion/test/main.cpp b/library/cpp/svnversion/test/main.cpp index 830686d5b3d..632c8d3915b 100644 --- a/library/cpp/svnversion/test/main.cpp +++ b/library/cpp/svnversion/test/main.cpp @@ -8,6 +8,7 @@ int main() { Cout << "GetProgramSvnVersion(): " << GetProgramSvnVersion() << Endl; Cout << "GetCustomVersion(): " << GetCustomVersion() << Endl; + Cout << "GetReleaseVersion(): " << GetReleaseVersion() << Endl; Cout << "PrintProgramSvnVersion(): " << Endl; PrintProgramSvnVersion(); Cout << "GetArcadiaSourcePath(): " << GetArcadiaSourcePath() << Endl; Cout << "GetArcadiaSourceUrl(): " << GetArcadiaSourceUrl() << Endl; diff --git a/library/cpp/tld/tlds-alpha-by-domain.txt b/library/cpp/tld/tlds-alpha-by-domain.txt index 2a7c080416b..3b4fd6d6821 100644 --- a/library/cpp/tld/tlds-alpha-by-domain.txt +++ b/library/cpp/tld/tlds-alpha-by-domain.txt @@ -1,4 +1,4 @@ -# Version 2024110700, Last Updated Thu Nov 7 07:07:01 2024 UTC +# Version 2024111000, Last Updated Sun Nov 10 07:07:01 2024 UTC AAA AARP ABB diff --git a/library/cpp/yt/assert/assert.h b/library/cpp/yt/assert/assert.h index 7a9e761a3a9..f2131d5bbcd 100644 --- a/library/cpp/yt/assert/assert.h +++ b/library/cpp/yt/assert/assert.h @@ -55,6 +55,20 @@ void AssertTrapImpl( } \ } while (false) +//! Behaves as |YT_ASSERT| in debug mode and as |Y_ASSUME| in release. +#ifdef NDEBUG + #define YT_ASSUME(expr) Y_ASSUME(expr) +#else + #define YT_ASSUME(expr) YT_ASSERT(expr) +#endif + +//! Behaves as |YT_ASSERT(false)| in debug mode and as |Y_UNREACHABLE| in release. +#ifdef NDEBUG + #define YT_UNREACHABLE() Y_UNREACHABLE() +#else + #define YT_UNREACHABLE() YT_ASSERT(false) +#endif + //! Fatal error code marker. Abnormally terminates the current process. #ifdef YT_COMPILING_UDF #define YT_ABORT() __YT_BUILTIN_ABORT() diff --git a/library/cpp/yt/error/convert_to_cpo.h b/library/cpp/yt/error/convert_to_cpo.h new file mode 100644 index 00000000000..55f608c3b60 --- /dev/null +++ b/library/cpp/yt/error/convert_to_cpo.h @@ -0,0 +1,43 @@ +#pragma once + +#include <library/cpp/yt/misc/tag_invoke_cpo.h> + +#include <util/generic/strbuf.h> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +// NB(arkady-e1ppa): This file intentionally is unaware of possible "yson-implementations" +// e.g. INode and TYsonString(Buf). This is done for two reasons: +// 1) We can't have dep on INodePtr here anyway. +// 2) We would like to eventually remove dep on yson of this module. + +//////////////////////////////////////////////////////////////////////////////// + +// NB(arkady-e1ppa): We intentionally generate a separate namespace +// where ConvertToImpl functions would reside +// without polluting general-use namespaces. +namespace NConvertToImpl { + +template <class T> +struct TFn : public NYT::TTagInvokeCpoBase<TFn<T>> +{ }; + +} // NConvertToImpl + +//////////////////////////////////////////////////////////////////////////////// + +template <class T> +inline constexpr NConvertToImpl::TFn<T> ConvertTo = {}; + +//////////////////////////////////////////////////////////////////////////////// + +template <class TTo, class TFrom> +concept CConvertToWorks = requires (const TFrom& from) { + { NYT::ConvertTo<TTo>(from) } -> std::same_as<TTo>; +}; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yt/error/error_attributes-inl.h b/library/cpp/yt/error/error_attributes-inl.h index a05e62d0e91..8ffbf3e0a39 100644 --- a/library/cpp/yt/error/error_attributes-inl.h +++ b/library/cpp/yt/error/error_attributes-inl.h @@ -9,6 +9,34 @@ namespace NYT { //////////////////////////////////////////////////////////////////////////////// template <class T> + requires CConvertToWorks<T, TErrorAttributes::TValue> +T TErrorAttributes::Get(TStringBuf key) const +{ + auto yson = GetYson(key); + try { + return NYT::ConvertTo<T>(yson); + } catch (const std::exception& ex) { + ThrowCannotParseAttributeException(key, ex); + } +} + +template <class T> + requires CConvertToWorks<T, TErrorAttributes::TValue> +typename TOptionalTraits<T>::TOptional TErrorAttributes::Find(TStringBuf key) const +{ + auto yson = FindYson(key); + if (!yson) { + return typename TOptionalTraits<T>::TOptional(); + } + try { + return NYT::ConvertTo<T>(yson); + } catch (const std::exception& ex) { + ThrowCannotParseAttributeException(key, ex); + } +} + +template <class T> + requires CConvertToWorks<T, TErrorAttributes::TValue> T TErrorAttributes::GetAndRemove(const TString& key) { auto result = Get<T>(key); @@ -17,12 +45,14 @@ T TErrorAttributes::GetAndRemove(const TString& key) } template <class T> + requires CConvertToWorks<T, TErrorAttributes::TValue> T TErrorAttributes::Get(TStringBuf key, const T& defaultValue) const { return Find<T>(key).value_or(defaultValue); } template <class T> + requires CConvertToWorks<T, TErrorAttributes::TValue> T TErrorAttributes::GetAndRemove(const TString& key, const T& defaultValue) { auto result = Find<T>(key); @@ -35,6 +65,7 @@ T TErrorAttributes::GetAndRemove(const TString& key, const T& defaultValue) } template <class T> + requires CConvertToWorks<T, TErrorAttributes::TValue> typename TOptionalTraits<T>::TOptional TErrorAttributes::FindAndRemove(const TString& key) { auto result = Find<T>(key); diff --git a/library/cpp/yt/error/error_attributes.h b/library/cpp/yt/error/error_attributes.h index 88771f83601..7c2f4a54078 100644 --- a/library/cpp/yt/error/error_attributes.h +++ b/library/cpp/yt/error/error_attributes.h @@ -1,5 +1,6 @@ #pragma once +#include "convert_to_cpo.h" #include "mergeable_dictionary.h" #include <library/cpp/yt/misc/optional.h> @@ -13,6 +14,9 @@ namespace NYT { // Eventually it would be a simple hash map. // NB(arkady-e1ppa): For now most methods are defined in yt/yt/core/misc/stripped_error.cpp // eventually they will be moved here. +// NB(arkady-e1ppa): For now we use TYsonString as value. +// TODO(arkady-e1ppa): Try switching to TString/std::string eventually. +// representing text-encoded yson string eventually (maybe). class TErrorAttributes { public: @@ -54,28 +58,34 @@ public: //! Finds the attribute and deserializes its value. //! Throws if no such value is found. template <class T> + requires CConvertToWorks<T, TValue> T Get(TStringBuf key) const; //! Same as #Get but removes the value. template <class T> + requires CConvertToWorks<T, TValue> T GetAndRemove(const TString& key); //! Finds the attribute and deserializes its value. //! Uses default value if no such attribute is found. template <class T> + requires CConvertToWorks<T, TValue> T Get(TStringBuf key, const T& defaultValue) const; //! Same as #Get but removes the value if it exists. template <class T> + requires CConvertToWorks<T, TValue> T GetAndRemove(const TString& key, const T& defaultValue); //! Finds the attribute and deserializes its value. //! Returns null if no such attribute is found. template <class T> + requires CConvertToWorks<T, TValue> typename TOptionalTraits<T>::TOptional Find(TStringBuf key) const; //! Same as #Find but removes the value if it exists. template <class T> + requires CConvertToWorks<T, TValue> typename TOptionalTraits<T>::TOptional FindAndRemove(const TString& key); template <CMergeableDictionary TDictionary> @@ -92,6 +102,9 @@ private: TErrorAttributes(TErrorAttributes&& other) = default; TErrorAttributes& operator= (TErrorAttributes&& other) = default; + + // defined in yt/yt/core/misc/stripped_error.cpp right now. + [[noreturn]] static void ThrowCannotParseAttributeException(TStringBuf key, const std::exception& ex); }; bool operator == (const TErrorAttributes& lhs, const TErrorAttributes& rhs); diff --git a/library/cpp/yt/memory/ref-inl.h b/library/cpp/yt/memory/ref-inl.h index 7fcd91908cf..a2074f97311 100644 --- a/library/cpp/yt/memory/ref-inl.h +++ b/library/cpp/yt/memory/ref-inl.h @@ -142,6 +142,17 @@ Y_FORCE_INLINE TSharedRef TSharedRef::FromString(TString str) return FromString<TDefaultSharedBlobTag>(std::move(str)); } +template <class TTag> +Y_FORCE_INLINE TSharedRef TSharedRef::FromString(std::string str) +{ + return FromString(std::move(str), GetRefCountedTypeCookie<TTag>()); +} + +Y_FORCE_INLINE TSharedRef TSharedRef::FromString(std::string str) +{ + return FromString<TDefaultSharedBlobTag>(std::move(str)); +} + Y_FORCE_INLINE TStringBuf TSharedRef::ToStringBuf() const { return TStringBuf(Begin(), Size()); diff --git a/library/cpp/yt/memory/ref.cpp b/library/cpp/yt/memory/ref.cpp index 4d02ef6875b..de04a83870e 100644 --- a/library/cpp/yt/memory/ref.cpp +++ b/library/cpp/yt/memory/ref.cpp @@ -46,6 +46,7 @@ private: //////////////////////////////////////////////////////////////////////////////// +template <class TString> class TStringHolder : public TSharedRangeHolder { @@ -232,11 +233,27 @@ TMutableRef TMutableRef::FromBlob(TBlob& blob) TSharedRef TSharedRef::FromString(TString str, TRefCountedTypeCookie tagCookie) { - auto holder = New<TStringHolder>(std::move(str), tagCookie); + return FromStringImpl(std::move(str), tagCookie); +} + +TSharedRef TSharedRef::FromString(std::string str, TRefCountedTypeCookie tagCookie) +{ + return FromStringImpl(std::move(str), tagCookie); +} + +template <class TString> +TSharedRef TSharedRef::FromStringImpl(TString str, TRefCountedTypeCookie tagCookie) +{ + auto holder = New<TStringHolder<TString>>(std::move(str), tagCookie); auto ref = TRef::FromString(holder->String()); return TSharedRef(ref, std::move(holder)); } +TSharedRef TSharedRef::FromString(const char* str) +{ + return FromString(std::string(str)); +} + TSharedRef TSharedRef::FromBlob(TBlob&& blob) { auto ref = TRef::FromBlob(blob); diff --git a/library/cpp/yt/memory/ref.h b/library/cpp/yt/memory/ref.h index 78ea46675fc..fa40cd9afbd 100644 --- a/library/cpp/yt/memory/ref.h +++ b/library/cpp/yt/memory/ref.h @@ -134,22 +134,34 @@ public: operator TRef() const; - //! Creates a TSharedRef from a string. - //! Since strings are ref-counted, no data is copied. + //! Creates a TSharedRef from TString. + //! Since strings are ref-counted, no data is being copied. //! The memory is marked with a given tag. template <class TTag> static TSharedRef FromString(TString str); - //! Creates a TSharedRef from a string. - //! Since strings are ref-counted, no data is copied. - //! The memory is marked with TDefaultSharedBlobTag. + //! Same as above but the memory is marked with TDefaultSharedBlobTag. static TSharedRef FromString(TString str); - //! Creates a TSharedRef reference from a string. - //! Since strings are ref-counted, no data is copied. - //! The memory is marked with a given tag. + //! Same as above but the memory tag is specified in #tagCookie. static TSharedRef FromString(TString str, TRefCountedTypeCookie tagCookie); + //! Creates a TSharedRef from std::string. + //! No data is being copied in #FromString itself but since #str is passed by value + //! a copy may occur at caller's side. + //! The memory is marked with a given tag. + template <class TTag> + static TSharedRef FromString(std::string str); + + //! Same as above but the memory is marked with TDefaultSharedBlobTag. + static TSharedRef FromString(std::string str); + + //! Same as above but the memory tag is specified in #tagCookie. + static TSharedRef FromString(std::string str, TRefCountedTypeCookie tagCookie); + + //! Creates a TSharedRef from a zero-terminated C string. + static TSharedRef FromString(const char* str); + //! Creates a TSharedRef for a given blob taking ownership of its content. static TSharedRef FromBlob(TBlob&& blob); @@ -176,6 +188,9 @@ public: private: friend class TSharedRefArrayImpl; + + template <class TString> + static TSharedRef FromStringImpl(TString str, TRefCountedTypeCookie tagCookie); }; //////////////////////////////////////////////////////////////////////////////// diff --git a/library/cpp/yt/misc/tag_invoke.h b/library/cpp/yt/misc/tag_invoke.h new file mode 100644 index 00000000000..94cec9b295d --- /dev/null +++ b/library/cpp/yt/misc/tag_invoke.h @@ -0,0 +1,95 @@ +#pragma once + +#include "concepts.h" + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +namespace NTagInvokeDetail { + +// This name shadows possible overloads of TagInvoke from parent namespaces +// which could be found by normal unqualified name lookup. +void TagInvoke() = delete; + +//////////////////////////////////////////////////////////////////////////////// + +// NB(arkady-e1ppa): We do not use trailing return type there in order to +// allow incomplete tag types to be safely used here. +struct TFn +{ + template <class TTag, class... TArgs> + requires requires (TTag&& tag, TArgs&&... args) { + // "Adl finds TagInvoke overload". + TagInvoke(std::forward<TTag>(tag), std::forward<TArgs>(args)...); + } + constexpr decltype(auto) operator()(TTag&& tag, TArgs&&... args) const + noexcept(noexcept(TagInvoke(std::forward<TTag>(tag), std::forward<TArgs>(args)...))) + { + return TagInvoke(std::forward<TTag>(tag), std::forward<TArgs>(args)...); + } +}; + +} // namespace NTagInvokeDetail + +//////////////////////////////////////////////////////////////////////////////// + +// Inline namespace is required so that there is no conflict with +// customizations of TagInvoke defined as friend function definition +// inside a class from namespace NYT. +inline namespace NTagInvokeCPO { + +inline constexpr NTagInvokeDetail::TFn TagInvoke = {}; + +} // inline namespace NTagInvokeCPO + +//////////////////////////////////////////////////////////////////////////////// + +// Some helpful concepts and aliases. +template <class TTag, class... TArgs> +concept CTagInvocable = requires (TTag&& tag, TArgs&&... args) { + NYT::TagInvoke(std::forward<TTag>(tag), std::forward<TArgs>(args)...); +}; + +//////////////////////////////////////////////////////////////////////////////// + +template <class TTag, class... TArgs> +concept CNothrowTagInvocable = + CTagInvocable<TTag, TArgs...> && + requires (TTag&& tag, TArgs&&... args) { + { NYT::TagInvoke(std::forward<TTag>(tag), std::forward<TArgs>(args)...) } noexcept; + }; + +//////////////////////////////////////////////////////////////////////////////// + +template <class TTag, class... TArgs> +using TTagInvokeResult = std::invoke_result_t<decltype(NYT::TagInvoke), TTag, TArgs...>; + +//////////////////////////////////////////////////////////////////////////////// + +template <auto V> +using TTagInvokeTag = std::decay_t<decltype(V)>; + +//////////////////////////////////////////////////////////////////////////////// + +namespace NDetail { + +template <class TTag, class TSignature> +struct TTagInvokeTraitsHelper; + +template <class TTag, class TReturn, bool NoExcept, class... TArgs> +struct TTagInvokeTraitsHelper<TTag, TReturn(TArgs...) noexcept(NoExcept)> +{ + static constexpr bool IsInvocable = NYT::CInvocable<decltype(NYT::TagInvoke), TReturn(TTag, TArgs...) noexcept(NoExcept)>; +}; + +} // namespace NDetail + +//////////////////////////////////////////////////////////////////////////////// + +template <class TTag, class TSignature> +concept CTagInvocableS = NDetail::TTagInvokeTraitsHelper<TTag, TSignature>::IsInvocable; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yt/misc/tag_invoke_cpo.h b/library/cpp/yt/misc/tag_invoke_cpo.h new file mode 100644 index 00000000000..9526df8adc0 --- /dev/null +++ b/library/cpp/yt/misc/tag_invoke_cpo.h @@ -0,0 +1,25 @@ +#pragma once + +#include "tag_invoke.h" + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +// CRTP-base which defines a baseline CPO behavior e.g. +// "If there is TagInvoke overload with the correct tag, use it". +template <class TThis> +struct TTagInvokeCpoBase +{ + template <class... TArgs> + requires CTagInvocable<const TThis&, TArgs...> + constexpr decltype(auto) operator()(TArgs&&... args) const + noexcept(CNothrowTagInvocable<const TThis&, TArgs...>) + { + return NYT::TagInvoke(static_cast<const TThis&>(*this), std::forward<TArgs>(args)...); + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yt/misc/unittests/tag_invoke_cpo_ut.cpp b/library/cpp/yt/misc/unittests/tag_invoke_cpo_ut.cpp new file mode 100644 index 00000000000..d7ae073c89f --- /dev/null +++ b/library/cpp/yt/misc/unittests/tag_invoke_cpo_ut.cpp @@ -0,0 +1,107 @@ +#include <library/cpp/testing/gtest/gtest.h> + +#include <library/cpp/yt/misc/tag_invoke.h> +#include <library/cpp/yt/misc/tag_invoke_cpo.h> + +namespace NYT { +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +inline constexpr struct TFooFn +{ + + // Customizable overload. + template <class... TArgs> + requires CTagInvocable<TFooFn, TArgs...> + constexpr decltype(auto) operator() (TArgs&&... args) const + noexcept(noexcept(NYT::TagInvoke(*this, std::forward<TArgs>(args)...))) + { + return NYT::TagInvoke(*this, std::forward<TArgs>(args)...); + } + + // Default overload. + template <class... TArgs> + requires (!CTagInvocable<TFooFn, TArgs...>) + constexpr decltype(auto) operator() (TArgs&&...) const + noexcept + { + return 42; + } +} Foo = {}; + +//////////////////////////////////////////////////////////////////////////////// + +TEST(TTagInvokeUsageTests, DefaultOverload) +{ + EXPECT_EQ(Foo(42), 42); + + struct TTTT + { }; + + EXPECT_EQ(Foo(TTTT{}), 42); +} + +//////////////////////////////////////////////////////////////////////////////// + +template <bool NoExcept> +struct TCustomFoo +{ + int Val; + + friend int TagInvoke(TTagInvokeTag<Foo>, TCustomFoo f) noexcept(NoExcept) + { + return f.Val + 11; + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +TEST(TTagInvokeUsageTests, CustomOverload) +{ + static_assert(CTagInvocable<TTagInvokeTag<Foo>, TCustomFoo<true>>); + static_assert(CTagInvocable<TTagInvokeTag<Foo>, TCustomFoo<false>>); + static_assert(CNothrowTagInvocable<TTagInvokeTag<Foo>, TCustomFoo<true>>); + static_assert(!CNothrowTagInvocable<TTagInvokeTag<Foo>, TCustomFoo<false>>); + + EXPECT_EQ(Foo(TCustomFoo<true>{.Val = 42}), 53); + EXPECT_EQ(Foo(TCustomFoo<false>{.Val = 42}), 53); +} + +//////////////////////////////////////////////////////////////////////////////// + +inline constexpr struct TBarFn + : public TTagInvokeCpoBase<TBarFn> +{ } Bar = {}; + +template <class T> +concept CBarable = requires (T&& t) { + Bar(t); +}; + +//////////////////////////////////////////////////////////////////////////////// + +struct THasCustom +{ + friend int TagInvoke(TTagInvokeTag<Bar>, THasCustom) + { + return 11; + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +TEST(TTagInvokeCpoTests, JustWorks) +{ + struct TNoCustom + { }; + static_assert(!CBarable<TNoCustom>); + + static_assert(CBarable<THasCustom>); + EXPECT_EQ(Bar(THasCustom{}), 11); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace +} // namespace NYT diff --git a/library/cpp/yt/misc/unittests/tag_invoke_impl_ut.cpp b/library/cpp/yt/misc/unittests/tag_invoke_impl_ut.cpp new file mode 100644 index 00000000000..e3ac0b58f25 --- /dev/null +++ b/library/cpp/yt/misc/unittests/tag_invoke_impl_ut.cpp @@ -0,0 +1,72 @@ +//////////////////////////////////////////////////////////////////////////////// +// NB(arkady-e1ppa): This decl/def order is intentional to simulate some tricky +// patterns of inclusion order. + +namespace NYT { +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +// This overload should be visible by direct call +// from namespaces enclosing ns NYT +// but invisible to any template code +// therefore it must not affect CTagInvocable concept. +template <class T, class U> +int TagInvoke(const T&, const U&) +{ + return 42; +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// + +// NB(arkady-e1ppa): Weird name to avoid ODR violation while keeping the +// struct inside of ns NYT (and not some anonymous ones). +struct TUniquelyTaggedForTagInvokeImplUt +{ + friend int TagInvoke(TUniquelyTaggedForTagInvokeImplUt, int v) + { + return v + 2; + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT + +//////////////////////////////////////////////////////////////////////////////// + +// Actual includes start. +#include <library/cpp/testing/gtest/gtest.h> + +#include <library/cpp/yt/misc/tag_invoke.h> + +//////////////////////////////////////////////////////////////////////////////// + +namespace NYT { +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +TEST(TTagInvokeCompileTests, UnqualidTemplate) +{ + static_assert(!CTagInvocable<int, int>); + // Unqualified finds overload defined above + // and never the CPO since constraints fail. + EXPECT_EQ(TagInvoke(42, 42), 42); +} + +TEST(TTagInvokeCompileTests, HiddenFriend) +{ + static_assert(CTagInvocable<TUniquelyTaggedForTagInvokeImplUt, int>); + EXPECT_EQ(TagInvoke(TUniquelyTaggedForTagInvokeImplUt{}, 42), 44); + EXPECT_EQ(NYT::TagInvoke(TUniquelyTaggedForTagInvokeImplUt{}, 42), 44); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace +} // namespace NYT diff --git a/library/cpp/yt/misc/unittests/ya.make b/library/cpp/yt/misc/unittests/ya.make index 63df71fb832..a414c5c8e53 100644 --- a/library/cpp/yt/misc/unittests/ya.make +++ b/library/cpp/yt/misc/unittests/ya.make @@ -11,6 +11,8 @@ SRCS( non_null_ptr_ut.cpp preprocessor_ut.cpp strong_typedef_ut.cpp + tag_invoke_cpo_ut.cpp + tag_invoke_impl_ut.cpp ) PEERDIR( |