Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-18 09:13:48

0001 /***********************************************************************************\
0002 * (c) Copyright 1998-2025 CERN for the benefit of the LHCb and ATLAS collaborations *
0003 *                                                                                   *
0004 * This software is distributed under the terms of the Apache version 2 licence,     *
0005 * copied verbatim in the file "LICENSE".                                            *
0006 *                                                                                   *
0007 * In applying this licence, CERN does not waive the privileges and immunities       *
0008 * granted to it by virtue of its status as an Intergovernmental Organization        *
0009 * or submit itself to any jurisdiction.                                             *
0010 \***********************************************************************************/
0011 #ifndef GAUDIKERNEL_STATUSCODE_H
0012 #define GAUDIKERNEL_STATUSCODE_H
0013 
0014 #include <boost/preprocessor/facilities/overload.hpp>
0015 #include <functional>
0016 #include <ostream>
0017 #include <type_traits>
0018 #include <utility>
0019 
0020 #include <GaudiKernel/Kernel.h>
0021 
0022 template <typename T>
0023 struct is_StatusCode_enum : std::false_type {};
0024 
0025 /**
0026  * @class StatusCode
0027  *
0028  * This class is used for returning status codes from appropriate routines.
0029  * A StatusCode is comprised of an (integer) value and a category (similar to std::error_code).
0030  * By default StatusCodes are created within a default StatusCode::Category, which
0031  * defines the isSuccess(), isFailure() and isRecoverable() behaviour depending on
0032  * the value of the StatusCode. This behaviour can be modified by defining a custom
0033  * StatusCode::Category and overriding the respective methods.
0034  *
0035  * To define a new StatusCode::Category, do the following:
0036  * - Define an enum class with the values of the StatusCode
0037  * - Register the enum with `STATUSCODE_ENUM_DECL( MyEnum )`
0038  * - Derive your category from StatusCode::Category and implement/override the relevant methods
0039  * - Register the new category with `STATUSCODE_ENUM_IMPL( MyEnum, MyCategory )` in a .cpp file
0040  *
0041  * \par Combining StatusCodes
0042  *  - The bitwise logic operators (&, |, &=, |=) can be used to combine StatusCodes and follows
0043  *    three-valued (ternary) logic with RECOVERABLE being the third state. No short-circuiting is applied:
0044  *    \code
0045  *       StatusCode sc = sc1 & sc2;
0046  *       sc |= sc3;
0047  *    \endcode
0048  *  - The boolean logic operators (&&, ||) perform regular two-valued logic only considering
0049  *    success/failure. The usual short-circuiting applies:
0050  *    \code
0051  *       bool b = sc1 && sc2;
0052  *    \endcode
0053  *
0054  * \note
0055  * The StatusCode values 0 and 1 are considered FAILURE/SUCCESS for all categories, i.e.
0056  * - operator==() and operator!=() ignore the category for these two values
0057  * - isSuccess() and isFailure() cannot be overridden for these two values
0058  *
0059  * \remark See https://akrzemi1.wordpress.com/2017/07/12/your-own-error-code for details on the underlying design
0060  */
0061 class
0062 #if !defined( __CLING__ )
0063     [[nodiscard]]
0064 #endif
0065     StatusCode final {
0066 public:
0067   typedef unsigned long code_t; ///< type of StatusCode value
0068 
0069   enum class ErrorCode : code_t { FAILURE = 0, SUCCESS = 1, RECOVERABLE = 2 };
0070 
0071   /**
0072    * @class StatusCode::Category
0073    *
0074    * The category assigned to a StatusCode. Derive from this class to implement your own category.
0075    * The mapping of StatusCode values to success and recoverable conditions can be defined by
0076    * overriding the appropriate methods.
0077    */
0078   struct Category {
0079     constexpr Category() noexcept = default;
0080     virtual ~Category() {}
0081 
0082     /// Name of the category
0083     virtual const char* name() const = 0;
0084 
0085     /// Description for code within this category
0086     virtual std::string message( code_t code ) const { return "UNKNOWN(" + std::to_string( code ) + ")"; }
0087 
0088     /// Is code considered success ?
0089     /// \note isFailure() cannot be overridden as it is defined as `!`isSuccess()
0090     virtual bool isSuccess( code_t code ) const { return code == static_cast<code_t>( ErrorCode::SUCCESS ); }
0091 
0092     /// Is code considered recoverable ?
0093     virtual bool isRecoverable( code_t code ) const { return code == static_cast<code_t>( ErrorCode::RECOVERABLE ); }
0094   };
0095 
0096   /// Default Gaudi StatusCode category
0097   static const Category& default_category() noexcept;
0098 
0099   // Provide shorthands for default code values
0100   constexpr const static auto SUCCESS     = ErrorCode::SUCCESS;
0101   constexpr const static auto FAILURE     = ErrorCode::FAILURE;
0102   constexpr const static auto RECOVERABLE = ErrorCode::RECOVERABLE;
0103 
0104   /// Default constructor
0105   StatusCode() = default;
0106 
0107   /// Constructor from enum type (allowing implicit conversion)
0108   template <typename T>
0109     requires( is_StatusCode_enum<T>::value )
0110   StatusCode( T sc ) noexcept : StatusCode{ static_cast<StatusCode::code_t>( sc ), is_StatusCode_enum<T>::instance } {}
0111 
0112   /// Constructor from code_t and category (explicit conversion only)
0113   explicit StatusCode( code_t code, const StatusCode::Category& cat ) noexcept : m_cat( &cat ), m_code( code ) {}
0114 
0115   /// Constructor from code_t in the default category (explicit conversion only)
0116   explicit StatusCode( code_t code ) noexcept : StatusCode( code, default_category() ) {}
0117 
0118   /// Copy constructor
0119   StatusCode( const StatusCode& rhs ) noexcept = default;
0120 
0121   /// Move constructor
0122   StatusCode( StatusCode&& rhs ) noexcept = default;
0123 
0124   /// Destructor
0125   ~StatusCode() = default;
0126 
0127   StatusCode& operator=( const StatusCode& rhs ) noexcept = default;
0128 
0129   bool isSuccess() const;
0130   bool isFailure() const { return !isSuccess(); }
0131   bool isRecoverable() const;
0132 
0133   /// Shorthand for isSuccess()
0134   explicit operator bool() const { return isSuccess(); }
0135 
0136   /// Retrieve value
0137   code_t getCode() const { return m_code; }
0138 
0139   /// Allow discarding a StatusCode without warning
0140   const StatusCode& ignore() const { return *this; }
0141   StatusCode&       ignore() { return *this; }
0142 
0143   /// Chain code blocks making the execution conditional a success result.
0144   ///
0145   /// The chained execution stops on the first non-success StatusCode in the chain and
0146   /// returns it, or continues until the end and returns the last produced StatusCode.
0147   ///
0148   /// For example:
0149   /// \code
0150   /// StatusCode myFunction() {
0151   ///   return subFunction()
0152   ///       .andThen([]() {
0153   ///         do_something();
0154   ///       })
0155   ///       .andThen(anotherFunction)
0156   ///       .andThen([]() {
0157   ///         if (is_special_case())
0158   ///           return do_something_else();
0159   ///         return StatusCode::SUCCESS;
0160   ///       });
0161   /// }
0162   /// \endcode
0163   template <typename F, typename... ARGS>
0164   StatusCode andThen( F&& f, ARGS&&... args ) const {
0165     if ( isFailure() ) return *this;
0166     return i_invoke( std::forward<F>( f ), std::forward<ARGS>( args )... );
0167   }
0168 
0169   /// Chain code blocks making the execution conditional a failure result.
0170   ///
0171   /// Inverse of StatusCode::andThen(), the passed function gets invoked only if
0172   /// if the StatusCode is a failure, in which case it either pass it on or overrides it,
0173   /// If the StatusCode is a success, it is passed on.
0174   ///
0175   /// For example:
0176   /// \code
0177   /// StatusCode myFunction() {
0178   ///   return subFunction()
0179   ///       .andThen([]() {
0180   ///         do_something();
0181   ///       })
0182   ///       .orElse(reportProblem);
0183   /// }
0184   /// \endcode
0185   template <typename F, typename... ARGS>
0186   StatusCode orElse( F&& f, ARGS&&... args ) const {
0187     if ( isSuccess() ) return *this;
0188     return i_invoke( std::forward<F>( f ), std::forward<ARGS>( args )... );
0189   }
0190 
0191   /// Throw a GaudiException in case of failures.
0192   ///
0193   /// It can be chained with StatusCode::then, behaving as a pass-through for a success StatusCode,
0194   /// while for non-success a GaudiException is thrown, propagating the failure into the exception
0195   /// (using the StatusCode field of the exception).
0196   ///
0197   /// For example:
0198   /// \code
0199   /// void myFunction() {
0200   ///   doSomething()
0201   ///       .andThen( doSomethingElse )
0202   ///       .orThrow( "some error", "myFunction" )
0203   ///       .andThen( moreActions )
0204   ///       .orThrow( "too bad, we were nearly there", "myFunction" );
0205   /// }
0206   /// \endcode
0207   const StatusCode& orThrow( std::string_view message, std::string_view tag ) const {
0208     if ( isFailure() ) i_doThrow( message, tag );
0209     return *this;
0210   }
0211 
0212   /// Throw a GaudiException in case of failures.
0213   ///
0214   /// See above, but in this case the message is not specified explicitly,
0215   /// but taken from `message()`
0216   const StatusCode& orThrow( std::string_view tag = "" ) const {
0217     if ( isFailure() ) i_doThrow( message(), tag ); // make sure `message()` is only called on error path
0218     return *this;
0219   }
0220 
0221   /// Retrieve category
0222   const StatusCode::Category& getCategory() const { return *m_cat; }
0223 
0224   /// Description (or name) of StatusCode value
0225   std::string message() const { return getCategory().message( m_code ); }
0226 
0227   friend std::ostream& operator<<( std::ostream& s, const StatusCode& sc ) {
0228     s << sc.message();
0229     return s;
0230   }
0231 
0232   /// Check if StatusCode value and category are the same
0233   /// \note For code values `0(FAILURE)` and `1(SUCCESS)` the category is ignored
0234   /// \note e.g. `sc==StatusCode::SUCCESS` is equivalent to `sc.isSuccess()` for all categories
0235   friend bool operator==( const StatusCode& lhs, const StatusCode& rhs );
0236   friend bool operator!=( const StatusCode& lhs, const StatusCode& rhs ) { return !( lhs == rhs ); }
0237 
0238   /// Comparison (values are grouped by category first)
0239   friend bool operator<( const StatusCode& lhs, const StatusCode& rhs ) {
0240     return ( lhs.m_cat < rhs.m_cat || ( lhs.m_cat == rhs.m_cat && lhs.m_code < rhs.m_code ) );
0241   }
0242 
0243   /// Ternary logic operator with RECOVERABLE being the "third" state
0244   StatusCode& operator&=( const StatusCode& rhs );
0245   StatusCode& operator|=( const StatusCode& rhs ); ///< @copydoc operator&=
0246   //
0247   /// Ternary AND operator
0248   friend StatusCode operator&( StatusCode lhs, const StatusCode& rhs ) { return lhs &= rhs; }
0249 
0250   /// Ternary OR operator
0251   friend StatusCode operator|( StatusCode lhs, const StatusCode& rhs ) { return lhs |= rhs; }
0252 
0253   /// Boolean AND assignment operator
0254   friend bool& operator&=( bool& lhs, const StatusCode& sc ) { return lhs &= sc.isSuccess(); }
0255 
0256   /// Boolean OR assignment operator
0257   friend bool& operator|=( bool& lhs, const StatusCode& sc ) { return lhs |= sc.isSuccess(); }
0258 
0259 private:
0260   const Category* m_cat{ &default_category() };                        ///< The status code category
0261   code_t          m_code{ static_cast<code_t>( ErrorCode::SUCCESS ) }; ///< The status code value
0262 
0263   ErrorCode default_value() const; ///< Project onto the default StatusCode values
0264 
0265   /// Helper function to avoid circular dependency between GaudiException.h and StatusCode.h
0266   [[noreturn]] void i_doThrow( std::string_view message, std::string_view tag ) const;
0267 
0268   /// Helper to invoke a callable and return the resulting StatusCode or this, if the callable returns void.
0269   template <typename... ARGS, std::invocable<ARGS...> F> // requires ( std::is_invocable_v<F, ARGS...> )
0270   StatusCode i_invoke( F&& f, ARGS&&... args ) const {
0271     if constexpr ( std::is_invocable_r_v<StatusCode, F, ARGS...> ) {
0272       return std::invoke( std::forward<F>( f ), std::forward<ARGS>( args )... );
0273     } else {
0274       // static_assert( std::is_same_v<void,std::invoke_result_t<F,ARGS...>>); // how paranoid should this be?
0275       std::invoke( std::forward<F>( f ), std::forward<ARGS>( args )... );
0276       return *this;
0277     }
0278   }
0279 };
0280 
0281 /*
0282  * Macros to declare/implement StatusCode enums/categories
0283  */
0284 
0285 /// Declare an enum to be used as StatusCode value
0286 /// @param ENUM enum class
0287 #define STATUSCODE_ENUM_DECL( ENUM )                                                                                   \
0288   template <>                                                                                                          \
0289   struct is_StatusCode_enum<ENUM> : std::true_type {                                                                   \
0290     static const StatusCode::Category& instance;                                                                       \
0291   };
0292 
0293 /// Assign a category to the StatusCode enum declared with STATUSCODE_ENUM_DECL( ENUM )
0294 /// @param ENUM     enum class
0295 /// @param CATEGORY (optional) category, otherwise use default StatusCode category
0296 #define STATUSCODE_ENUM_IMPL( ... ) BOOST_PP_OVERLOAD( STATUSCODE_ENUM_IMPL_, __VA_ARGS__ )( __VA_ARGS__ )
0297 
0298 #define STATUSCODE_ENUM_IMPL_1( ENUM )                                                                                 \
0299   const StatusCode::Category& is_StatusCode_enum<ENUM>::instance = StatusCode::default_category();
0300 
0301 #define STATUSCODE_ENUM_IMPL_2( ENUM, CATEGORY )                                                                       \
0302   const StatusCode::Category& is_StatusCode_enum<ENUM>::instance = CATEGORY{};
0303 
0304 // Declare the default StatusCode enum
0305 STATUSCODE_ENUM_DECL( StatusCode::ErrorCode )
0306 
0307 /*
0308  * Inline methods
0309  */
0310 
0311 inline const StatusCode::Category& StatusCode::default_category() noexcept {
0312   return is_StatusCode_enum<StatusCode::ErrorCode>::instance;
0313 }
0314 
0315 inline bool StatusCode::isSuccess() const {
0316   return ( m_code == static_cast<code_t>( ErrorCode::SUCCESS ) || m_cat->isSuccess( m_code ) );
0317 }
0318 
0319 inline bool StatusCode::isRecoverable() const { return m_cat->isRecoverable( m_code ); }
0320 
0321 inline StatusCode::ErrorCode StatusCode::default_value() const {
0322   auto r = isSuccess() ? ErrorCode::SUCCESS : ( isRecoverable() ? ErrorCode::RECOVERABLE : ErrorCode::FAILURE );
0323   return r;
0324 }
0325 
0326 inline bool operator==( const StatusCode& lhs, const StatusCode& rhs ) {
0327   return ( lhs.m_code == rhs.m_code ) &&
0328          ( lhs.m_code == static_cast<StatusCode::code_t>( StatusCode::ErrorCode::SUCCESS ) ||
0329            lhs.m_code == static_cast<StatusCode::code_t>( StatusCode::ErrorCode::FAILURE ) ||
0330            ( lhs.m_cat == rhs.m_cat ) );
0331 }
0332 
0333 inline StatusCode& StatusCode::operator&=( const StatusCode& rhs ) {
0334   // Ternary AND lookup matrix
0335   static constexpr StatusCode::code_t AND[3][3] = { { 0, 0, 0 }, { 0, 1, 2 }, { 0, 2, 2 } };
0336 
0337   StatusCode::code_t l = static_cast<StatusCode::code_t>( default_value() );
0338   StatusCode::code_t r = static_cast<StatusCode::code_t>( rhs.default_value() );
0339   m_code               = AND[l][r];
0340   return *this;
0341 }
0342 
0343 inline StatusCode& StatusCode::operator|=( const StatusCode& rhs ) {
0344   // Ternary OR lookup matrix
0345   static constexpr StatusCode::code_t OR[3][3] = { { 0, 1, 2 }, { 1, 1, 1 }, { 2, 1, 2 } };
0346 
0347   StatusCode::code_t l = static_cast<StatusCode::code_t>( default_value() );
0348   StatusCode::code_t r = static_cast<StatusCode::code_t>( rhs.default_value() );
0349   m_code               = OR[l][r];
0350   return *this;
0351 }
0352 
0353 #endif // GAUDIKERNEL_STATUSCODE_H