#pragma once #include #include #include #include #include #include #include #include #include #include namespace NYT { //////////////////////////////////////////////////////////////////////////////// //! An opaque wrapper around |int| value capable of conversions from |int|s and //! arbitrary enum types. class TErrorCode { public: using TUnderlying = int; constexpr TErrorCode(); explicit constexpr TErrorCode(int value); template requires std::is_enum_v constexpr TErrorCode(E value); constexpr operator int() const; template requires std::is_enum_v constexpr operator E() const; template requires std::is_enum_v constexpr bool operator == (E rhs) const; constexpr bool operator == (TErrorCode rhs) const; private: int Value_; }; //////////////////////////////////////////////////////////////////////////////// void FormatValue(TStringBuilderBase* builder, TErrorCode code, TStringBuf spec); //////////////////////////////////////////////////////////////////////////////// template concept CErrorNestable = requires (TError& error, TValue&& operand) { { error <<= std::forward(operand) } -> std::same_as; }; template <> class [[nodiscard]] TErrorOr { public: TErrorOr(); ~TErrorOr(); TErrorOr(const TError& other); TErrorOr(TError&& other) noexcept; TErrorOr(const std::exception& ex); struct TDisableFormat { }; static constexpr TDisableFormat DisableFormat = {}; TErrorOr(TString message, TDisableFormat); TErrorOr(TErrorCode code, TString message, TDisableFormat); template explicit TErrorOr( TFormatString format, TArgs&&... arg); template TErrorOr( TErrorCode code, TFormatString format, TArgs&&... args); TError& operator = (const TError& other); TError& operator = (TError&& other) noexcept; static TError FromSystem(); static TError FromSystem(int error); static TError FromSystem(const TSystemError& error); TErrorCode GetCode() const; TError& SetCode(TErrorCode code); TErrorCode GetNonTrivialCode() const; THashSet GetDistinctNonTrivialErrorCodes() const; const TString& GetMessage() const; TError& SetMessage(TString message); bool HasOriginAttributes() const; TProcessId GetPid() const; TStringBuf GetThreadName() const; NThreading::TThreadId GetTid() const; bool HasDatetime() const; TInstant GetDatetime() const; bool HasAttributes() const noexcept; const TErrorAttributes& Attributes() const; TErrorAttributes* MutableAttributes(); const std::vector& InnerErrors() const; std::vector* MutableInnerErrors(); // Used for deserialization only. TOriginAttributes* MutableOriginAttributes() const noexcept; void UpdateOriginAttributes(); TError Truncate( int maxInnerErrorCount = 2, i64 stringLimit = 16_KB, const THashSet& attributeWhitelist = {}) const &; TError Truncate( int maxInnerErrorCount = 2, i64 stringLimit = 16_KB, const THashSet& attributeWhitelist = {}) &&; bool IsOK() const; template requires (!CStringLiteral>) void ThrowOnError(U&& u) const &; template void ThrowOnError(TFormatString format, TArgs&&... args) const &; template void ThrowOnError(TErrorCode code, TFormatString format, TArgs&&... args) const &; inline void ThrowOnError() const &; template requires (!CStringLiteral>) void ThrowOnError(U&& u) &&; template void ThrowOnError(TFormatString format, TArgs&&... args) &&; template void ThrowOnError(TErrorCode code, TFormatString format, TArgs&&... args) &&; inline void ThrowOnError() &&; template TFilter> std::optional FindMatching(const TFilter& filter) const; template TFilter> std::optional FindMatching(const TFilter& filter) const; std::optional FindMatching(TErrorCode code) const; std::optional FindMatching(const THashSet& codes) const; template requires (!CStringLiteral>) TError Wrap(U&& u) const &; template TError Wrap(TFormatString format, TArgs&&... args) const &; template TError Wrap(TErrorCode code, TFormatString format, TArgs&&... args) const &; TError Wrap() const &; template requires (!CStringLiteral>) TError Wrap(U&& u) &&; template TError Wrap(TFormatString format, TArgs&&... args) &&; template TError Wrap(TErrorCode code, TFormatString format, TArgs&&... args) &&; TError Wrap() &&; //! Perform recursive aggregation of error codes and messages over the error tree. //! Result of this aggregation is suitable for error clustering in groups of //! "similar" errors. Refer to yt/yt/library/error_skeleton/skeleton_ut.cpp for examples. //! //! This method builds skeleton from scratch by doing complete error tree traversal, //! so calling it in computationally hot code is discouraged. //! //! In order to prevent core -> re2 dependency, implementation belongs to a separate library //! yt/yt/library/error_skeleton. Calling this method without PEERDIR'ing implementation //! results in an exception. TString GetSkeleton() const; TError& operator <<= (const TErrorAttribute& attribute) &; TError& operator <<= (const std::vector& attributes) &; TError& operator <<= (const TError& innerError) &; TError& operator <<= (TError&& innerError) &; TError& operator <<= (const std::vector& innerErrors) &; TError& operator <<= (std::vector&& innerErrors) &; TError& operator <<= (TAnyMergeableDictionaryRef attributes) &; template TError&& operator << (TValue&& operand) &&; template TError operator << (TValue&& operand) const &; template TError&& operator << (const std::optional& rhs) &&; template TError operator << (const std::optional& rhs) const &; private: class TImpl; std::unique_ptr Impl_; explicit TErrorOr(std::unique_ptr impl); void MakeMutable(); friend class TErrorAttributes; }; //////////////////////////////////////////////////////////////////////////////// bool operator == (const TError& lhs, const TError& rhs); //////////////////////////////////////////////////////////////////////////////// void FormatValue(TStringBuilderBase* builder, const TError& error, TStringBuf spec); //////////////////////////////////////////////////////////////////////////////// using TErrorVisitor = std::function; //! Traverses the error tree in DFS order. void TraverseError( const TError& error, const TErrorVisitor& visitor, int depth = 0); //////////////////////////////////////////////////////////////////////////////// template struct TErrorTraits { using TWrapped = TErrorOr; using TUnwrapped = T; }; template struct TErrorTraits> { using TUnderlying = T; using TWrapped = TErrorOr; using TUnwrapped = T; }; //////////////////////////////////////////////////////////////////////////////// class TErrorException : public std::exception { public: DEFINE_BYREF_RW_PROPERTY(TError, Error); public: TErrorException() = default; TErrorException(const TErrorException& other) = default; TErrorException(TErrorException&& other) = default; const char* what() const noexcept override; private: mutable TString CachedWhat_; }; // Make these templates to avoid type erasure during throw. template requires std::derived_from, TErrorException> TException&& operator <<= (TException&& ex, const TError& error); template requires std::derived_from, TErrorException> TException&& operator <<= (TException&& ex, TError&& error); //////////////////////////////////////////////////////////////////////////////// namespace NDetail { struct TErrorAdaptor { template requires std::constructible_from TError operator << (TArg&& rhs) const; template requires std::constructible_from && std::derived_from, TError> TArg&& operator << (TArg&& rhs) const; }; // Make these to correctly forward TError to Wrap call. template requires std::derived_from, TError> && (!CStringLiteral>) void ThrowErrorExceptionIfFailed(TErrorLike&& error, U&& u); template requires std::derived_from, TError> void ThrowErrorExceptionIfFailed(TErrorLike&& error, TFormatString format, TArgs&&... args); template requires std::derived_from, TError> void ThrowErrorExceptionIfFailed(TErrorLike&& error, TErrorCode code, TFormatString format, TArgs&&... args); template requires std::derived_from, TError> void ThrowErrorExceptionIfFailed(TErrorLike&& error); } // namespace NDetail //////////////////////////////////////////////////////////////////////////////// #define THROW_ERROR \ throw ::NYT::TErrorException() <<= ::NYT::NDetail::TErrorAdaptor() << #define THROW_ERROR_EXCEPTION(head, ...) \ THROW_ERROR ::NYT::TError(head __VA_OPT__(,) __VA_ARGS__) // NB: When given an error and a string as arguments, this macro automatically wraps // new error around the initial one. #define THROW_ERROR_EXCEPTION_IF_FAILED(error, ...) \ ::NYT::NDetail::ThrowErrorExceptionIfFailed((error) __VA_OPT__(,) __VA_ARGS__); \ #define THROW_ERROR_EXCEPTION_UNLESS(condition, head, ...) \ if ((condition)) {\ } else { \ THROW_ERROR ::NYT::TError(head __VA_OPT__(,) __VA_ARGS__); \ } #define THROW_ERROR_EXCEPTION_IF(condition, head, ...) \ THROW_ERROR_EXCEPTION_UNLESS(!(condition), head, __VA_ARGS__) //////////////////////////////////////////////////////////////////////////////// template class [[nodiscard]] TErrorOr : public TError { public: TErrorOr(); TErrorOr(const T& value); TErrorOr(T&& value) noexcept; TErrorOr(const TErrorOr& other); TErrorOr(TErrorOr&& other) noexcept; TErrorOr(const TError& other); TErrorOr(TError&& other) noexcept; TErrorOr(const std::exception& ex); template TErrorOr(const TErrorOr& other); template TErrorOr(TErrorOr&& other) noexcept; TErrorOr& operator = (const TErrorOr& other) requires std::is_copy_assignable_v; TErrorOr& operator = (TErrorOr&& other) noexcept requires std::is_nothrow_move_assignable_v; const T& Value() const & Y_LIFETIME_BOUND; T& Value() & Y_LIFETIME_BOUND; T&& Value() && Y_LIFETIME_BOUND; template requires (!CStringLiteral>) const T& ValueOrThrow(U&& u) const & Y_LIFETIME_BOUND; template const T& ValueOrThrow(TFormatString format, TArgs&&... args) const & Y_LIFETIME_BOUND; template const T& ValueOrThrow(TErrorCode code, TFormatString format, TArgs&&... args) const & Y_LIFETIME_BOUND; const T& ValueOrThrow() const & Y_LIFETIME_BOUND; template requires (!CStringLiteral>) T& ValueOrThrow(U&& u) & Y_LIFETIME_BOUND; template T& ValueOrThrow(TFormatString format, TArgs&&... args) & Y_LIFETIME_BOUND; template T& ValueOrThrow(TErrorCode code, TFormatString format, TArgs&&... args) & Y_LIFETIME_BOUND; T& ValueOrThrow() & Y_LIFETIME_BOUND; template requires (!CStringLiteral>) T&& ValueOrThrow(U&& u) && Y_LIFETIME_BOUND; template T&& ValueOrThrow(TFormatString format, TArgs&&... args) && Y_LIFETIME_BOUND; template T&& ValueOrThrow(TErrorCode code, TFormatString format, TArgs&&... args) && Y_LIFETIME_BOUND; T&& ValueOrThrow() && Y_LIFETIME_BOUND; const T& ValueOrDefault(const T& defaultValue Y_LIFETIME_BOUND) const & Y_LIFETIME_BOUND; T& ValueOrDefault(T& defaultValue Y_LIFETIME_BOUND) & Y_LIFETIME_BOUND; constexpr T ValueOrDefault(T&& defaultValue) const &; constexpr T ValueOrDefault(T&& defaultValue) &&; private: std::optional Value_; }; //////////////////////////////////////////////////////////////////////////////// template void FormatValue(TStringBuilderBase* builder, const TErrorOr& error, TStringBuf spec); //////////////////////////////////////////////////////////////////////////////// template auto RunNoExcept(F&& functor, As&&... args) noexcept -> decltype(functor(std::forward(args)...)) { return functor(std::forward(args)...); } //////////////////////////////////////////////////////////////////////////////// } // namespace NYT #define STRIPPED_ERROR_INL_H_ #include "error-inl.h" #undef STRIPPED_ERROR_INL_H_