/*---------------------------------------------------------------------------* Copyright (C) 2012 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. *---------------------------------------------------------------------------*/ /*! @file @brief These are Result class declarations. :include nn/Result.h */ #ifndef NN_RESULT_H__ #define NN_RESULT_H__ #include //------------------------------------------------------------------- // For C / C++ /* @addtogroup nn_root @{ */ /* @defgroup nn_Result_c Result (C) @brief C interface module for nn::Result, which indicates the result of an operation. @{ */ #ifdef __cplusplus extern "C" { #endif // ifdef __cplusplus /* @brief Structure that indicates the result of an operation. Used by C member functions. */ typedef struct nnResult { bit32 value; } nnResult; #ifdef __cplusplus } #endif // ifdef __cplusplus /* } */ /* } */ //------------------------------------------------------------------- // For C++ #ifdef __cplusplus namespace nn { /*! @brief Class that indicates the result of an operation. Functions in the @ref nn namespace return instances of the @ref nn::Result class. For information about the handling of the Result codes returned by the different libraries, see the library and API reference manuals. This section gives a general description of handling Result codes. @section Description of Values Result contains a 32-bit value that includes information about the error level, the module where the error occurred, and the error type. This information is only used to determine the cause of problems during debugging, and must not be used in any error handling code in the application. A future SDK update will provide details on how to get this information. @section Hierarchy The result values defined by each library are organized in a hierarchy, with each Result object potentially having parents and children. In error handling, a higher-level Result code is regarded as including the lower-level Result codes. This hierarchical organization makes it possible to identify the causes of errors by coding error handling using the less numerous high-level Result codes, and then referring to the more numerous low-level Result codes during debugging. This resembles the exception inheritance mechanics used in C++ and other languages. Use the <= and >= operators or the Includes member function to determine inclusion. @section Example of Error Handling The following sample code shows typical error handling using Result codes. \code nn::Result result = nn::lib::SomeApi(); if( result.IsFailure() ) { if( result <= nn::lib::ResultNotFound() ) { // Code would branch from here for all errors that occur when something is not found. // Using file access as an example, this could apply to ResultDirectoryNotFound or ResultFileNotFound. } else if( result <= nn::lib::ResultConnectionError() ) { // Code would branch from here for all connection errors. // Using server access as an example, this could apply to errors caused when a server cannot be found or when there is no response from the server. } else { // Code would branch from here for all other errors. // Code would presumably branch from here when individual error handling is not required, or when an unexpected error is returned. // Handling differs by library, but there are two basic approaches. // - Inform the user of failure, and return to the previous sequence. // - A fatal system problem might have occurred. Display an error and halt the application. } } \endcode Note the following. @subsection Identifying Execution Failures System updates might define additional result values that were not defined when an application was developed. In such cases, you can call the nn::Result::IsSuccess and nn::Result::IsFailure functions to determine whether a process succeeded or failed. We recommend not just using matching result values without also checking for success, as this entails the risk of falsely determining success in cases of unexpected errors. @subsection Explicitly Specifying Namespaces Some result codes are defined with the same names but different values in different libraries. Always specify the namespace explicitly so that your application does not mishandle unexpected errors. @subsection Determining Inclusion Use the following method to determine the definition of each result and what it includes. @code if(result <= nn::fs::ResultNotFound()) { // This can also catch nn::fs::ResultMediaNotFound. } @endcode @code if(nn::fs::ResultNotFound::Includes(result)) { ... } @endcode @code if(nn::fs::ResultNotFound() >= result) { ... } @endcode @attention Do not directly handle any Result codes not mentioned in the API Reference. @section Errors Not Allowed in Retail Products The following Result codes indicate errors caused by application bugs or other problems. You must fix your program so that none of these errors occur in retail products. Result codes other than these may be caused by user operations or may be impossible to entirely eliminate during development. You must handle any errors that can be caused by the user. For more information, see the API Reference. @section Handling Unexpected Errors System updates might define additional result values that were not defined when an application was developed. You almost always need to implement code to handle unexpected errors. For more information about handling, see the reference materials for the relevant libraries. We recommend contacting support if no handling method is described in the documentation. @section Unrecoverable Fatal Errors A fatal error is an error caused by a hardware bug or another error that should not arise in the retail product. The system cannot recover from such an error and there is no means of handling the error. As a general rule, when a fatal error occurs, the error is not passed to the application but instead the system halts and displays FATAL ERROR on the screen. When this error occurs, check the manual according to the message that displays. If the problem cannot be resolved, contact Nintendo Support as a last resort. The system just halts if it cannot even display the error. */ class Result { private: static const bit32 MASK_FAIL_BIT = 0x80000000u; // Most Significant Bit static const s32 SIZE_DESCRIPTION = 20; static const s32 SIZE_MODULE = 9; static const s32 SIZE_LEVEL = 3; static const s32 SHIFTS_DESCRIPTION = 0; static const s32 SHIFTS_MODULE = SHIFTS_DESCRIPTION + SIZE_DESCRIPTION; static const s32 SHIFTS_LEVEL = SHIFTS_MODULE + SIZE_MODULE; //NN_STATIC_ASSERT( SHIFTS_LEVEL + SIZE_LEVEL == 32 ); static const u32 MASK_NEGATIVE_LEVEL = (~0u) << SIZE_LEVEL; #define NN_RESULT_H_MAKE_MASK(size, shift) (((~0u) >> (32 - (size))) << (shift)) #define NN_RESULT_H_MAKE_MASK_HELPER(c) \ static const u32 MASK_ ## c = NN_RESULT_H_MAKE_MASK(SIZE_ ## c, SHIFTS_ ## c) NN_RESULT_H_MAKE_MASK_HELPER(DESCRIPTION); NN_RESULT_H_MAKE_MASK_HELPER(MODULE); NN_RESULT_H_MAKE_MASK_HELPER(LEVEL); #undef NN_RESULT_H_MAKE_MASK_HELPER #undef NN_RESULT_H_MAKE_MASK #define NN_RESULT_H_MAKE_MAX(size) ((~0u) >> (32 - (size))) #define NN_RESULT_H_MAKE_MAX_HELPER(c) \ static const s32 MAX_ ## c = NN_RESULT_H_MAKE_MAX(SIZE_ ## c) NN_RESULT_H_MAKE_MAX_HELPER(DESCRIPTION); NN_RESULT_H_MAKE_MAX_HELPER(MODULE); NN_RESULT_H_MAKE_MAX_HELPER(LEVEL); #undef NN_RESULT_H_MAKE_MAX_HELPER #undef NN_RESULT_H_MAKE_MAX public: /* * @brief Enumeration type indicating the severity of an error. (Do not use this type for error handling.) */ enum LevelImpl { LEVEL_SUCCESS = 0, LEVEL_FATAL = -1, LEVEL_USAGE = -2, LEVEL_STATUS = -3, LEVEL_END = -7 }; typedef int Level; enum { MODULE_APPLICATION = MAX_MODULE - 1, // These values are not used. MODULE_INVALID_RESULT_VALUE = MAX_MODULE // These values are not used. }; /* * @brief Enumeration type that indicates the details of errors. (Do not use this type for error handling.) */ enum { DESCRIPTION_SUCCESS = 0, // Succeeded. DESCRIPTION_INVALID_RESULT_VALUE = MAX_DESCRIPTION - 0 // These values are not used. }; private: u32 m_Code; friend struct ResultSuccess; template friend struct ConstRangeBase; explicit Result(u32 code) : m_Code(code) {} public: Result() : m_Code( static_cast( ((static_cast(MAX_LEVEL) << SHIFTS_LEVEL) & MASK_LEVEL) | ((static_cast(MAX_MODULE) << SHIFTS_MODULE) & MASK_MODULE) | ((static_cast(MAX_DESCRIPTION) << SHIFTS_DESCRIPTION) & MASK_DESCRIPTION) )) { } Result(Level level, int module, int description) : m_Code( static_cast( ((static_cast(level) << SHIFTS_LEVEL) & MASK_LEVEL) | ((static_cast(module) << SHIFTS_MODULE) & MASK_MODULE) | ((static_cast(description) << SHIFTS_DESCRIPTION) & MASK_DESCRIPTION) )) { /* NN_ASSERT(-3 <= level && level <= 0); NN_ASSERT(0 <= module && module < MAX_MODULE); NN_ASSERT(0 <= description && description < MAX_DESCRIPTION); */ } Result(nnResult result) : m_Code(result.value) {} /*! @name Determinations @{ */ /*! * @brief Returns true if the operation failed, or false if it succeeded. */ bool IsFailure() const { return (m_Code & MASK_FAIL_BIT) != 0; } /*! * @brief Returns true if the operation succeeded, or false if it failed. */ bool IsSuccess() const { return !IsFailure(); } /*! @} */ /* * @brief Returns a Level enumerated type indicating the severity of the error. * * Enumeration type definitions will change in the future. Do not use them for any purpose other than checking the content of errors during development. */ Level GetLevel() const { // TODO: Create a platform-dependent arithmetic shift inline function and call it. u32 level; if ( IsLegacy() ) { level = GetLegacyLevel(); } else { level = GetCodeBits(MASK_LEVEL, SHIFTS_LEVEL); } if(m_Code & MASK_FAIL_BIT) { return static_cast( level | MASK_NEGATIVE_LEVEL); } else { return static_cast( level ); } } /* * @brief Returns a Module enumerated type indicating the module in which the error occurred. * * Enumeration type definitions will change in the future. Do not use them for any purpose other than checking the content of errors during development. */ int GetModule() const { if ( IsLegacy() ) { return GetLegacyModule(); } else { return GetCodeBits(MASK_MODULE, SHIFTS_MODULE); } } /* * @brief Returns a Description enumeration type indicating the details of the error. * * Enumeration type definitions will change in the future. Do not use them for any purpose other than checking the content of errors during development. */ int GetDescription() const { if(IsLegacy()) { return GetLegacyDescription(); } else { return static_cast( GetCodeBits(MASK_DESCRIPTION, SHIFTS_DESCRIPTION) ); } } u32 GetValue() const { return m_Code; } operator nnResult() const { nnResult r = {m_Code}; return r; } /*! @name Development Support @{ */ /*! * @brief Converts a Result code into a 32-bit array. */ u32 GetPrintableBits() const { return m_Code; } /*! @} */ /* @name Determinations @{ */ /* * @brief Checks for equality. * * Use to handle some errors for libraries other than @ref nn::fs. For information about error handling in @ref nn::fs, see the @ref nn::fs library reference. * * * @param[in] rhs Specifies the right-hand operand. * @return Returns true if the operands are equal, and false otherwise. */ bool operator ==(const Result& rhs) const { return this->m_Code == rhs.m_Code; } /* * @brief Checks for inequality. * * Use to handle some errors for libraries other than @ref nn::fs. For information about error handling in @ref nn::fs, see the @ref nn::fs library reference. * * * @param[in] rhs Specifies the right-hand operand. * @return Returns true if the operands are not equal, and false otherwise. */ bool operator !=(const Result& rhs) const { return this->m_Code != rhs.m_Code; } /*! @} */ template struct ConstRangeBase; template struct ConstRange; template struct ConstRangePlus; #include "./Result.legacy.inclass.h" private: u32 GetCodeBits(u32 mask, s32 shift) const { return ((m_Code & mask) >> shift); } }; struct ResultSuccess { operator Result() const { return Result(0); } operator nnResult() const { return Result(0); } }; template struct Result::ConstRangeBase { static const Result::Level Level = TLevel; static const int Module = TModule; static const int Description = TDescription; static const u32 Value = static_cast( ((static_cast(TLevel) << SHIFTS_LEVEL) & MASK_LEVEL) | ((TModule << SHIFTS_MODULE) & MASK_MODULE) | ((TDescription << SHIFTS_DESCRIPTION) & MASK_DESCRIPTION) ); operator Result() const { return Result(Value); } operator nnResult() const { return Result(Value); } }; template struct Result::ConstRangeBase { static const Result::Level Level = TLevel; static const int Module = TModule; }; template struct Result::ConstRange : public Result::ConstRangeBase { static const int DescriptionStart = TDescriptionStart; static const int DescriptionEnd = TDescriptionEnd; static bool Includes(Result result) { return result.GetModule() == TModule && (TDescriptionStart <= result.GetDescription() && result.GetDescription() < TDescriptionEnd); } friend bool operator<=(Result lhs, const ConstRange&) { return ConstRange::Includes(lhs); } friend bool operator>=(const ConstRange&, Result rhs) { return ConstRange::Includes(rhs); } template friend Result::ConstRangePlus< Result::ConstRange, Result::ConstRange > operator+(const Result::ConstRange&, const Result::ConstRange&) { return Result::ConstRangePlus< Result::ConstRange, Result::ConstRange >(); } }; template struct Result::ConstRangePlus { static bool Includes(Result result) { return Result1::Includes(result) || Result2::Includes(result); } friend bool operator<=(Result lhs, const ConstRangePlus&) { return ConstRangePlus::Includes(lhs); } friend bool operator>=(const ConstRangePlus&, Result rhs) { return ConstRangePlus::Includes(rhs); } template friend Result::ConstRangePlus > operator+(const Result::ConstRangePlus&, const Result::ConstRangePlus&) { return ConstRangePlus, ConstRangePlus >(); } template friend Result::ConstRangePlus > operator+(const Result::ConstRangePlus&, const ConstRange&) { return ConstRangePlus >(); } template friend Result::ConstRangePlus > operator+(const Result::ConstRange&, const Result::ConstRangePlus&) { return ConstRangePlus >(); } }; #include "./Result.legacy.h" } #define NN_DEFINE_RESULT_CONST_RANGE(name, level, module, description, descriptionMin, descriptionMax) \ typedef ::nn::Result::ConstRange<(level), (module), (description), (descriptionMin), (descriptionMax)> name #define NN_RESULT_THROW_IF_FAILED(result) \ do \ { \ ::nn::Result _nn_result_throw_if_failed_tmp_ = (result); \ if (_nn_result_throw_if_failed_tmp_.IsFailure()) \ { \ return _nn_result_throw_if_failed_tmp_; \ } \ } while(0) #define NN_RESULT_RETURN(value) \ do \ { \ *pOut = (value); \ return ::nn::ResultSuccess(); \ } while (0) #define NN_RESULT_SUCCESS \ do \ { \ return ::nn::ResultSuccess(); \ } while (0) #define NN_RESULT_THROW(result) return (result) #define NN_RESULT_THROW_UNLESS(cond, result) \ if (cond) {} else NN_RESULT_THROW(result) #define NN_RESULT_TRY(result) { ::nn::Result _nn_result_try_tmp_ = (result); if (0) {} # define NN_RESULT_CATCH(type) else if (_nn_result_try_tmp_ <= type()) # define NN_RESULT_CATCH_ALL else if (_nn_result_try_tmp_.IsFailure()) # define NN_RESULT_RETHROW NN_RESULT_THROW(_nn_result_try_tmp_) #define NN_RESULT_END_TRY NN_RESULT_CATCH_ALL NN_RESULT_RETHROW; } #endif // __cplusplus #endif /* NN_RESULT_H_ */