Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:57:43

0001 /***********************************************************************************\
0002 * (c) Copyright 1998-2022 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 __cplusplus >= 201703L && !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, typename = std::enable_if_t<is_StatusCode_enum<T>::value>>
0109   StatusCode( T sc ) noexcept : StatusCode{ static_cast<StatusCode::code_t>( sc ), is_StatusCode_enum<T>::instance } {}
0110 
0111   /// Constructor from code_t and category (explicit conversion only)
0112   explicit StatusCode( code_t code, const StatusCode::Category& cat ) noexcept : m_cat( &cat ), m_code( code ) {}
0113 
0114   /// Constructor from code_t in the default category (explicit conversion only)
0115   explicit StatusCode( code_t code ) noexcept : StatusCode( code, default_category() ) {}
0116 
0117   /// Copy constructor
0118   StatusCode( const StatusCode& rhs ) noexcept = default;
0119 
0120   /// Move constructor
0121   StatusCode( StatusCode&& rhs ) noexcept = default;
0122 
0123   /// Destructor
0124   ~StatusCode() = default;
0125 
0126   StatusCode& operator=( const StatusCode& rhs ) noexcept = default;
0127 
0128   bool isSuccess() const;
0129   bool isFailure() const { return !isSuccess(); }
0130   bool isRecoverable() const;
0131 
0132   /// Shorthand for isSuccess()
0133   explicit operator bool() const { return isSuccess(); }
0134 
0135   /// Retrieve value
0136   code_t getCode() const { return m_code; }
0137 
0138   /// Allow discarding a StatusCode without warning
0139   const StatusCode& ignore() const { return *this; }
0140   StatusCode&       ignore() { return *this; }
0141 
0142   /// Chain code blocks making the execution conditional a success result.
0143   ///
0144   /// The chained execution stops on the first non-success StatusCode in the chain and
0145   /// returns it, or continues until the end and returns the last produced StatusCode.
0146   ///
0147   /// For example:
0148   /// \code
0149   /// StatusCode myFunction() {
0150   ///   return subFunction()
0151   ///       .andThen([]() {
0152   ///         do_something();
0153   ///       })
0154   ///       .andThen(anotherFunction)
0155   ///       .andThen([]() {
0156   ///         if (is_special_case())
0157   ///           return do_something_else();
0158   ///         return StatusCode::SUCCESS;
0159   ///       });
0160   /// }
0161   /// \endcode
0162   template <typename F, typename... ARGS>
0163   StatusCode andThen( F&& f, ARGS&&... args ) const {
0164     if ( isFailure() ) return *this;
0165     return i_invoke( std::forward<F>( f ), std::forward<ARGS>( args )... );
0166   }
0167 
0168   /// Chain code blocks making the execution conditional a failure result.
0169   ///
0170   /// Inverse of StatusCode::andThen(), the passed function gets invoked only if
0171   /// if the StatusCode is a failure, in which case it either pass it on or overrides it,
0172   /// If the StatusCode is a success, it is passed on.
0173   ///
0174   /// For example:
0175   /// \code
0176   /// StatusCode myFunction() {
0177   ///   return subFunction()
0178   ///       .andThen([]() {
0179   ///         do_something();
0180   ///       })
0181   ///       .orElse(reportProblem);
0182   /// }
0183   /// \endcode
0184   template <typename F, typename... ARGS>
0185   StatusCode orElse( F&& f, ARGS&&... args ) const {
0186     if ( isSuccess() ) return *this;
0187     return i_invoke( std::forward<F>( f ), std::forward<ARGS>( args )... );
0188   }
0189 
0190   /// Throw a GaudiException in case of failures.
0191   ///
0192   /// It can be chained with StatusCode::then, behaving as a pass-through for a success StatusCode,
0193   /// while for non-success a GaudiException is thrown, propagating the failure into the exception
0194   /// (using the StatusCode field of the exception).
0195   ///
0196   /// For example:
0197   /// \code
0198   /// void myFunction() {
0199   ///   doSomething()
0200   ///       .andThen( doSomethingElse )
0201   ///       .orThrow( "some error", "myFunction" )
0202   ///       .andThen( moreActions )
0203   ///       .orThrow( "too bad, we were nearly there", "myFunction" );
0204   /// }
0205   /// \endcode
0206   const StatusCode& orThrow( std::string_view message, std::string_view tag ) const {
0207     if ( isFailure() ) i_doThrow( message, tag );
0208     return *this;
0209   }
0210 
0211   /// Throw a GaudiException in case of failures.
0212   ///
0213   /// See above, but in this case the message is not specified explicitly,
0214   /// but taken from `message()`
0215   const StatusCode& orThrow( std::string_view tag = "" ) const {
0216     if ( isFailure() ) i_doThrow( message(), tag ); // make sure `message()` is only called on error path
0217     return *this;
0218   }
0219 
0220   /// Retrieve category
0221   const StatusCode::Category& getCategory() const { return *m_cat; }
0222 
0223   /// Description (or name) of StatusCode value
0224   std::string message() const { return getCategory().message( m_code ); }
0225 
0226   friend std::ostream& operator<<( std::ostream& s, const StatusCode& sc ) {
0227     s << sc.message();
0228     return s;
0229   }
0230 
0231   /// Check if StatusCode value and category are the same
0232   /// \note For code values `0(FAILURE)` and `1(SUCCESS)` the category is ignored
0233   /// \note e.g. `sc==StatusCode::SUCCESS` is equivalent to `sc.isSuccess()` for all categories
0234   friend bool operator==( const StatusCode& lhs, const StatusCode& rhs );
0235   friend bool operator!=( const StatusCode& lhs, const StatusCode& rhs ) { return !( lhs == rhs ); }
0236 
0237   /// Comparison (values are grouped by category first)
0238   friend bool operator<( const StatusCode& lhs, const StatusCode& rhs ) {
0239     return ( lhs.m_cat < rhs.m_cat || ( lhs.m_cat == rhs.m_cat && lhs.m_code < rhs.m_code ) );
0240   }
0241 
0242   /// Ternary logic operator with RECOVERABLE being the "third" state
0243   StatusCode& operator&=( const StatusCode& rhs );
0244   StatusCode& operator|=( const StatusCode& rhs ); ///< @copydoc operator&=
0245   //
0246   /// Ternary AND operator
0247   friend StatusCode operator&( StatusCode lhs, const StatusCode& rhs ) { return lhs &= rhs; }
0248 
0249   /// Ternary OR operator
0250   friend StatusCode operator|( StatusCode lhs, const StatusCode& rhs ) { return lhs |= rhs; }
0251 
0252   /// Boolean AND assignment operator
0253   friend bool& operator&=( bool& lhs, const StatusCode& sc ) { return lhs &= sc.isSuccess(); }
0254 
0255   /// Boolean OR assignment operator
0256   friend bool& operator|=( bool& lhs, const StatusCode& sc ) { return lhs |= sc.isSuccess(); }
0257 
0258 private:
0259   const Category* m_cat{ &default_category() };                        ///< The status code category
0260   code_t          m_code{ static_cast<code_t>( ErrorCode::SUCCESS ) }; ///< The status code value
0261 
0262   ErrorCode default_value() const; ///< Project onto the default StatusCode values
0263 
0264   /// Helper function to avoid circular dependency between GaudiException.h and StatusCode.h
0265   [[noreturn]] void i_doThrow( std::string_view message, std::string_view tag ) const;
0266 
0267   /// Helper to invoke a callable and return the resulting StatusCode or this, if the callable returns void.
0268   template <typename F, typename... ARGS, typename = std::enable_if_t<std::is_invocable_v<F, ARGS...>>>
0269   StatusCode i_invoke( F&& f, ARGS&&... args ) const {
0270     if constexpr ( std::is_invocable_r_v<StatusCode, F, ARGS...> ) {
0271       return std::invoke( std::forward<F>( f ), std::forward<ARGS>( args )... );
0272     } else {
0273       // static_assert( std::is_same_v<void,std::invoke_result_t<F,ARGS...>>); // how paranoid should this be?
0274       std::invoke( std::forward<F>( f ), std::forward<ARGS>( args )... );
0275       return *this;
0276     }
0277   }
0278 };
0279 
0280 /*
0281  * Macros to declare/implement StatusCode enums/categories
0282  */
0283 
0284 /// Declare an enum to be used as StatusCode value
0285 /// @param ENUM enum class
0286 #define STATUSCODE_ENUM_DECL( ENUM )                                                                                   \
0287   template <>                                                                                                          \
0288   struct is_StatusCode_enum<ENUM> : std::true_type {                                                                   \
0289     static const StatusCode::Category& instance;                                                                       \
0290   };
0291 
0292 /// Assign a category to the StatusCode enum declared with STATUSCODE_ENUM_DECL( ENUM )
0293 /// @param ENUM     enum class
0294 /// @param CATEGORY (optional) category, otherwise use default StatusCode category
0295 #define STATUSCODE_ENUM_IMPL( ... ) BOOST_PP_OVERLOAD( STATUSCODE_ENUM_IMPL_, __VA_ARGS__ )( __VA_ARGS__ )
0296 
0297 #define STATUSCODE_ENUM_IMPL_1( ENUM )                                                                                 \
0298   const StatusCode::Category& is_StatusCode_enum<ENUM>::instance = StatusCode::default_category();
0299 
0300 #define STATUSCODE_ENUM_IMPL_2( ENUM, CATEGORY )                                                                       \
0301   const StatusCode::Category& is_StatusCode_enum<ENUM>::instance = CATEGORY{};
0302 
0303 // Declare the default StatusCode enum
0304 STATUSCODE_ENUM_DECL( StatusCode::ErrorCode )
0305 
0306 /*
0307  * Inline methods
0308  */
0309 
0310 inline const StatusCode::Category& StatusCode::default_category() noexcept {
0311   return is_StatusCode_enum<StatusCode::ErrorCode>::instance;
0312 }
0313 
0314 inline bool StatusCode::isSuccess() const {
0315   return ( m_code == static_cast<code_t>( ErrorCode::SUCCESS ) || m_cat->isSuccess( m_code ) );
0316 }
0317 
0318 inline bool StatusCode::isRecoverable() const { return m_cat->isRecoverable( m_code ); }
0319 
0320 inline StatusCode::ErrorCode StatusCode::default_value() const {
0321   auto r = isSuccess() ? ErrorCode::SUCCESS : ( isRecoverable() ? ErrorCode::RECOVERABLE : ErrorCode::FAILURE );
0322   return r;
0323 }
0324 
0325 inline bool operator==( const StatusCode& lhs, const StatusCode& rhs ) {
0326   return ( lhs.m_code == rhs.m_code ) &&
0327          ( lhs.m_code == static_cast<StatusCode::code_t>( StatusCode::ErrorCode::SUCCESS ) ||
0328            lhs.m_code == static_cast<StatusCode::code_t>( StatusCode::ErrorCode::FAILURE ) ||
0329            ( lhs.m_cat == rhs.m_cat ) );
0330 }
0331 
0332 inline StatusCode& StatusCode::operator&=( const StatusCode& rhs ) {
0333   // Ternary AND lookup matrix
0334   static constexpr StatusCode::code_t AND[3][3] = { { 0, 0, 0 }, { 0, 1, 2 }, { 0, 2, 2 } };
0335 
0336   StatusCode::code_t l = static_cast<StatusCode::code_t>( default_value() );
0337   StatusCode::code_t r = static_cast<StatusCode::code_t>( rhs.default_value() );
0338   m_code               = AND[l][r];
0339   return *this;
0340 }
0341 
0342 inline StatusCode& StatusCode::operator|=( const StatusCode& rhs ) {
0343   // Ternary OR lookup matrix
0344   static constexpr StatusCode::code_t OR[3][3] = { { 0, 1, 2 }, { 1, 1, 1 }, { 2, 1, 2 } };
0345 
0346   StatusCode::code_t l = static_cast<StatusCode::code_t>( default_value() );
0347   StatusCode::code_t r = static_cast<StatusCode::code_t>( rhs.default_value() );
0348   m_code               = OR[l][r];
0349   return *this;
0350 }
0351 
0352 #endif // GAUDIKERNEL_STATUSCODE_H