blob: c859b11675fb16ee43f0e3c5edd25912db87f213 [file]
/*
*
* Copyright (c) 2020 Project CHIP Authors
* Copyright (c) 2013-2017 Nest Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file defines and implements a number of miscellaneous
* templates for finding object minima and maxima and interface
* macros for assertion checking.
*
*/
#pragma once
#include <lib/core/CHIPConfig.h>
#include <lib/core/CHIPError.h>
#include <lib/core/ErrorStr.h>
#include <lib/support/Assertions.h>
#include <lib/support/ObjectDump.h>
#include <lib/support/logging/TextOnlyLogging.h>
#include <memory>
/**
* @def ReturnErrorOnFailure(expr, ...)
*
* @brief
* Returns the error code if the expression returns an error. For a CHIP_ERROR expression, this means any value other
* than CHIP_NO_ERROR. For an integer expression, this means non-zero.
*
* Example usage:
*
* @code
* ReturnErrorOnFailure(channel->SendMsg(msg), mState = Uninitialized);
* @endcode
*
* @param[in] expr An expression to be tested.
* @param[in] ... Statements to execute before returning. Optional.
*/
#define ReturnErrorOnFailure(expr, ...) \
do \
{ \
auto __err = (expr); \
if (!::chip::ChipError::IsSuccess(__err)) \
{ \
__VA_ARGS__; \
return __err; \
} \
} while (false)
/**
* @def ReturnErrorVariantOnFailure(expr)
*
* @brief
* This is for use when the calling function returns a Variant type. It returns a CHIP_ERROR variant with the corresponding error
* code if the expression returns an error.
*
* Example usage:
*
* @code
* ReturnErrorVariantOnFailure(NextStep, ParseSigma1(tlvReader, parsedSigma1));
* @endcode
*
* @param[in] variantType The Variant type that the calling function returns.
* @param[in] expr An expression to be tested.
* @param[in] ... Statements to execute before returning. Optional.
*/
#define ReturnErrorVariantOnFailure(variantType, expr, ...) \
do \
{ \
auto __err = (expr); \
if (!::chip::ChipError::IsSuccess(__err)) \
{ \
__VA_ARGS__; \
return variantType::Create<CHIP_ERROR>(__err); \
} \
} while (false)
/**
* @def ReturnLogErrorOnFailure(expr)
*
* @brief
* Returns the error code if the expression returns something different
* than CHIP_NO_ERROR.
*
* Example usage:
*
* @code
* ReturnLogErrorOnFailure(channel->SendMsg(msg));
* @endcode
*
* @param[in] expr A scalar expression to be evaluated against CHIP_NO_ERROR.
*/
#define ReturnLogErrorOnFailure(expr) \
do \
{ \
CHIP_ERROR __err = (expr); \
if (__err != CHIP_NO_ERROR) \
{ \
ChipLogError(NotSpecified, "%s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \
return __err; \
} \
} while (false)
/**
* @def SuccessOrLog(expr, MOD, MSG, ...)
*
* @brief
* If expr returns something other than CHIP_NO_ERROR, log a message for the specified module
* in the 'Error' category.
*
* Example usage:
*
* @code
* SuccessOrLog(channel->SendMsg(msg), Module, "Failure message: %s", param);
* @endcode
*
* @param[in] expr A scalar expression to be evaluated against CHIP_NO_ERROR.
* @param[in] MOD The log module to use.
* @param[in] MSG The log message format string.
* @param[in] ... Optional arguments for the log message.
*/
#define SuccessOrLog(expr, MOD, MSG, ...) \
do \
{ \
CHIP_ERROR __lerr = (expr); \
if (!::chip::ChipError::IsSuccess(__lerr)) \
{ \
ChipLogFailure(__lerr, MOD, MSG, ##__VA_ARGS__); \
} \
} while (false)
/**
* @def ReturnAndLogOnFailure(expr, MOD, MSG, ...)
*
* @brief
* If expr returns something than CHIP_NO_ERROR, log a chip message for the specified module
* in the 'Error' category and return.
*
* Example usage:
*
* @code
* ReturnAndLogOnFailure(channel->SendMsg(msg), Module, "Failure message: %s", param);
* @endcode
*
* @param[in] expr A scalar expression to be evaluated against CHIP_NO_ERROR.
* @param[in] MOD The log module to use.
* @param[in] MSG The log message format string.
* @param[in] ... Optional arguments for the log message.
*/
#define ReturnAndLogOnFailure(expr, MOD, MSG, ...) \
do \
{ \
CHIP_ERROR __err = (expr); \
if (!::chip::ChipError::IsSuccess(__err)) \
{ \
ChipLogFailure(__err, MOD, MSG, ##__VA_ARGS__); \
return; \
} \
} while (false)
/**
* @def ReturnErrorAndLogOnFailure(expr, MOD, MSG, ...)
*
* @brief
* If expr returns something than CHIP_NO_ERROR, log a chip message for the specified module
* in the 'Error' category and return the error.
*
* Example usage:
*
* @code
* ReturnErrorAndLogOnFailure(channel->SendMsg(msg), Module, "Failure message: %s", param);
* @endcode
*
* @param[in] expr A scalar expression to be evaluated against CHIP_NO_ERROR.
* @param[in] MOD The log module to use.
* @param[in] MSG The log message format string.
* @param[in] ... Optional arguments for the log message.
*/
#define ReturnErrorAndLogOnFailure(expr, MOD, MSG, ...) \
do \
{ \
CHIP_ERROR __err = (expr); \
if (!::chip::ChipError::IsSuccess(__err)) \
{ \
ChipLogFailure(__err, MOD, MSG, ##__VA_ARGS__); \
return __err; \
} \
} while (false)
/**
* @def ReturnValueAndLogOnFailure(expr, value, MOD, MSG, ...)
*
* @brief
* If expr returns something other than CHIP_NO_ERROR, log a message for the specified module
* in the 'Error' category and return the error.
*
* Example usage:
*
* @code
* ReturnValueAndLogOnFailure(channel->SendMsg(msg), false, Module, "Failure message: %s", param);
* @endcode
*
* @param[in] expr A scalar expression to be evaluated against CHIP_NO_ERROR.
* @param[in] value A value to return if @a expr is an error.
* @param[in] MOD The log module to use.
* @param[in] MSG The log message format string.
* @param[in] ... Optional arguments for the log message.
*/
#define ReturnValueAndLogOnFailure(expr, value, MOD, MSG, ...) \
do \
{ \
CHIP_ERROR __err = (expr); \
if (!::chip::ChipError::IsSuccess(__err)) \
{ \
ChipLogFailure(__err, MOD, MSG, ##__VA_ARGS__); \
return value; \
} \
} while (false)
/**
* @def ReturnOnFailure(expr, ...)
*
* @brief
* Returns if the expression returns an error. For a CHIP_ERROR expression, this means any value other
* than CHIP_NO_ERROR. For an integer expression, this means non-zero.
*
* Example usage:
*
* @code
* ReturnOnFailure(channel->SendMsg(msg), mState = Uninitialized);
* @endcode
*
* @param[in] expr An expression to be tested.
* @param[in] ... Statements to execute before returning. Optional.
*/
#define ReturnOnFailure(expr, ...) \
do \
{ \
auto __err = (expr); \
if (!::chip::ChipError::IsSuccess(__err)) \
{ \
__VA_ARGS__; \
return; \
} \
} while (false)
/**
* @def ReturnValueOnFailure(expr, value, ...)
*
* @brief
* Returns value if the expression returns an error. For a CHIP_ERROR expression, this means any value other
* than CHIP_NO_ERROR. For an integer expression, this means non-zero.
*
* Example usage:
*
* @code
* ReturnValueOnFailure(channel->SendMsg(msg), Status::Failure, mState = Uninitialized);
* @endcode
*
* @param[in] expr An expression to be tested.
* @param[in] value A value to return if @a expr is an error.
* @param[in] ... Statements to execute before returning. Optional.
*/
#define ReturnValueOnFailure(expr, value, ...) \
do \
{ \
auto __err = (expr); \
if (!::chip::ChipError::IsSuccess(__err)) \
{ \
__VA_ARGS__; \
return value; \
} \
} while (false)
/**
* @def VerifyOrReturnLogError(expr, code)
*
* @brief
* Returns and print a specified error code if expression evaluates to false
*
* Example usage:
*
* @code
* VerifyOrReturnLogError(param != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
* @endcode
*
* @param[in] expr A Boolean expression to be evaluated.
* @param[in] code A value to return if @a expr is false.
*/
#if CHIP_CONFIG_ERROR_SOURCE
#define VerifyOrReturnLogError(expr, code) \
do \
{ \
if (!(expr)) \
{ \
auto __code = (code); \
ChipLogError(NotSpecified, "%s at %s:%d", ErrorStr(__code), __FILE__, __LINE__); \
return __code; \
} \
} while (false)
#else // CHIP_CONFIG_ERROR_SOURCE
#define VerifyOrReturnLogError(expr, code) \
do \
{ \
if (!(expr)) \
{ \
auto __code = (code); \
ChipLogError(NotSpecified, "%s:%d false: %" CHIP_ERROR_FORMAT, #expr, __LINE__, __code.Format()); \
return __code; \
} \
} while (false)
#endif // CHIP_CONFIG_ERROR_SOURCE
/**
* @def SuccessOrShutdown(expr, ...)
*
* @brief
* This is expected to be called from within a class that implements a Shutdown method.
* It checks for the specified error, which is expected to commonly be successful (CHIP_NO_ERROR),
* on failure, it calls the statements to be executed before shutdown if provided,
* then calls the Shutdown method of the class and returns the error.
*
* @param[in] expr A ChipError object to be evaluated against success (CHIP_NO_ERROR).
* @param[in] ... Statements to execute before shutdown. Optional.
*
*/
#define SuccessOrShutdown(expr, ...) \
do \
{ \
auto __err = (expr); \
if (!::chip::ChipError::IsSuccess(__err)) \
{ \
__VA_ARGS__; \
this->Shutdown(); \
return __err; \
} \
} while (false)
/**
* @def SuccessOrExit(error)
*
* @brief
* This checks for the specified error, which is expected to
* commonly be successful (CHIP_NO_ERROR), and branches to
* the local label 'exit' if the status is unsuccessful.
*
* Example Usage:
*
* @code
* CHIP_ERROR TryHard()
* {
* CHIP_ERROR err;
*
* err = TrySomething();
* SuccessOrExit(err);
*
* err = TrySomethingElse();
* SuccessOrExit(err);
*
* exit:
* return err;
* }
* @endcode
*
* @param[in] error A ChipError object to be evaluated against success (CHIP_NO_ERROR).
*
*/
#define SuccessOrExit(error) VerifyOrExit(::chip::ChipError::IsSuccess((error)), {})
/**
* @def SuccessOrExitAction(error, anAction)
*
* @brief
* This checks for the specified error, which is expected to
* commonly be successful (CHIP_NO_ERROR), and both executes
* @a anAction and branches to the local label 'exit' if the
* status is unsuccessful.
*
* @param[in] error A ChipError object to be evaluated against success (CHIP_NO_ERROR).
*/
#define SuccessOrExitAction(error, action) VerifyOrExit(::chip::ChipError::IsSuccess((error)), action)
#ifndef chipDie
extern "C" void chipDie(void) __attribute((noreturn));
inline void chipDie(void)
{
ChipLogError(NotSpecified, "chipDie chipDie chipDie");
chipAbort();
}
#endif // chipDie
/**
* @def VerifyOrDie(aCondition)
*
* @brief
* This checks for the specified condition, which is expected to
* commonly be true and forces an immediate abort if the condition
* is false.
*
* Example Usage:
*
* @code
* void FreeBuffer(const uint8_t *buf)
* {
* VerifyOrDie(buf != NULL);
* free(buf);
* }
* @endcode
*
* @param[in] aCondition A Boolean expression to be evaluated.
*
* @sa #VerifyOrDieWithMsg
* @sa #chipDie
*
*/
#if CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE && CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE_NO_COND
#define VerifyOrDie(aCondition) \
VerifyOrDo(aCondition, ChipLogError(Support, "VerifyOrDie failure at %s:%d", __FILE__, __LINE__); chipAbort())
#elif CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
#define VerifyOrDie(aCondition) \
VerifyOrDo(aCondition, ChipLogError(Support, "VerifyOrDie failure at %s:%d: %s", __FILE__, __LINE__, #aCondition); chipAbort())
#else // CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
#define VerifyOrDie(aCondition) VerifyOrDieWithoutLogging(aCondition)
#endif // CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
/**
* @def SuccessOrDie(error)
*
* @brief
* This checks for the specified error, which is expected to
* commonly be successful (CHIP_NO_ERROR), forces an immediate abort if the status
* is unsuccessful.
*
*
* Example Usage:
*
* @code
* uint8_t* AllocateBuffer()
* {
* uint8_t* buffer;
* SuccessOrDie(ChipAllocateBuffer(buffer));
* return buffer;
* }
* @endcode
*
* @param[in] error A ChipError object to be evaluated against success (CHIP_NO_ERROR).
*
*/
#if CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE && CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE_NO_COND
#define SuccessOrDie(error) \
do \
{ \
auto __err = (error); \
VerifyOrDo(::chip::ChipError::IsSuccess(__err), \
ChipLogError(Support, "SuccessOrDie failure %s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \
chipAbort()); \
} while (false)
#elif CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
#define SuccessOrDie(error) \
do \
{ \
auto __err = (error); \
VerifyOrDo(::chip::ChipError::IsSuccess(__err), \
ChipLogError(Support, "SuccessOrDie failure %s at %s:%d: %s", ErrorStr(__err), __FILE__, __LINE__, #error); \
chipAbort()); \
} while (false)
#else // CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
#define SuccessOrDie(error) VerifyOrDieWithoutLogging(::chip::ChipError::IsSuccess((error)))
#endif // CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
/**
* @def VerifyOrDieWithObject(aCondition, aObject)
*
* Like VerifyOrDie(), but calls DumpObjectToLog()
* on the provided object on failure before aborting
* if CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE is enabled.
*/
#if CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
#define VerifyOrDieWithObject(aCondition, aObject) \
VerifyOrDo(aCondition, ::chip::DumpObjectToLog(aObject); \
ChipLogError(Support, "VerifyOrDie failure at %s:%d: %s", __FILE__, __LINE__, #aCondition); chipAbort())
#else // CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
#define VerifyOrDieWithObject(aCondition, aObject) VerifyOrDieWithoutLogging(aCondition)
#endif // CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
/**
* @def VerifyOrDieWithMsg(aCondition, aModule, aMessage, ...)
*
* @brief
* This checks for the specified condition, which is expected to
* commonly be true and both prints @a aMessage and forces an
* immediate abort if the condition is false.
*
* Example Usage:
*
* @code
* void FreeBuffer(const uint8_t *buf)
* {
* VerifyOrDieWithMsg(buf != NULL, MemoryManagement, "Invalid pointer passed to FreeBuffer");
* free(buf);
* }
* @endcode
*
* @param[in] aCondition A Boolean expression to be evaluated.
* @param[in] aModule A chip LogModule short-hand mnemonic identifing
* the logical section of code that is a
* source the logged message.
* @param[in] aMessage A pointer to a NULL-terminated C string with
* C Standard Library-style format specifiers
* containing the log message to be formatted
* and logged.
* @param[in] ... A variadic argument list whose elements should
* correspond to the format specifiers in @a
* aMessage.
*
* @sa #VerifyOrDie
* @sa #chipDie
*
*/
#define VerifyOrDieWithMsg(aCondition, aModule, aMessage, ...) \
VerifyOrDo(aCondition, ChipLogError(aModule, aMessage, ##__VA_ARGS__); chipAbort())
/**
* @def LogErrorOnFailure(expr)
*
* @brief
* Logs a message if the expression returns something different than CHIP_NO_ERROR.
*
* Example usage:
*
* @code
* ReturnLogErrorOnFailure(channel->SendMsg(msg));
* @endcode
*
* @param[in] expr A scalar expression to be evaluated against CHIP_NO_ERROR.
*/
#define LogErrorOnFailure(expr) \
do \
{ \
CHIP_ERROR __err = (expr); \
if (__err != CHIP_NO_ERROR) \
{ \
ChipLogError(NotSpecified, "%s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \
} \
} while (false)
#if (__cplusplus >= 201103L)
#ifndef __FINAL
#define __FINAL final
#endif
#ifndef __OVERRIDE
#define __OVERRIDE override
#endif
#ifndef __CONSTEXPR
#define __CONSTEXPR constexpr
#endif
#else
#ifndef __FINAL
#define __FINAL
#endif
#ifndef __OVERRIDE
#define __OVERRIDE
#endif
#ifndef __CONSTEXPR
#define __CONSTEXPR constexpr
#endif
#endif // (__cplusplus >= 201103L)
#if ((__cplusplus >= 201703L) || (defined(__GNUC__) && (__GNUC__ >= 7)) || (defined(__clang__)) && (__clang_major__ >= 4))
#define CHECK_RETURN_VALUE [[nodiscard]]
#elif defined(__GNUC__) && (__GNUC__ >= 4)
#define CHECK_RETURN_VALUE __attribute__((warn_unused_result))
#elif defined(_MSC_VER) && (_MSC_VER >= 1700)
#define CHECK_RETURN_VALUE _Check_return_
#else
#define CHECK_RETURN_VALUE
#endif
#if defined(__clang__)
#define FALLTHROUGH [[clang::fallthrough]]
#elif defined(__GNUC__)
#define FALLTHROUGH __attribute__((fallthrough))
#else
#define FALLTHROUGH (void) 0
#endif
/**
* @def MATTER_ARRAY_SIZE(aArray)
*
* @brief
* Returns the size of an array in number of elements.
*
* Example Usage:
*
* @code
* int numbers[10];
* SortNumbers(numbers, MATTER_ARRAY_SIZE(numbers));
* @endcode
*
* @return The size of an array in number of elements.
*
* @note Clever template-based solutions seem to fail when MATTER_ARRAY_SIZE is used
* with a variable-length array argument, so we just do the C-compatible
* thing in C++ as well.
*/
#ifndef MATTER_ARRAY_SIZE
#define MATTER_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#endif
/**
* @brief Ensures that if `str` is NULL, a non-null `default_str_value` is provided
*
* @param str - null-terminated string pointer or nullptr
* @param default_str_value - replacement value if `str` is nullptr
* @return `str` if not null, otherwise `default_str_value`
*/
inline const char * DefaultStringWhenNull(const char * str, const char * default_str_value)
{
return (str != nullptr) ? str : default_str_value;
}
/**
* @brief Ensure that a string for a %s specifier is shown as "(null)" if null
*
* @param str - null-terminated string pointer or nullptr
* @return `str` if not null, otherwise literal "(null)"
*/
inline const char * StringOrNullMarker(const char * str)
{
return DefaultStringWhenNull(str, "(null)");
}
namespace chip {
/**
* Utility for checking, at compile time if the array is constexpr, whether an
* array is sorted. Can be used for static_asserts.
*/
template <typename T>
constexpr bool ArrayIsSorted(const T * aArray, size_t aLength)
{
if (aLength == 0 || aLength == 1)
{
return true;
}
if (aArray[0] > aArray[1])
{
return false;
}
return ArrayIsSorted(aArray + 1, aLength - 1);
}
template <typename T, size_t N>
constexpr bool ArrayIsSorted(const T (&aArray)[N])
{
return ArrayIsSorted(aArray, N);
}
/**
* @def ScopeExit(fn)
*
* @brief
* RAII to automatically release resources on scope exit (instead of depending on goto exit)
* See https://en.cppreference.com/w/cpp/experimental/scope_exit.html
* Use with ReturnOnFailure, ReturnLogErrorOnFailure, ReturnAndLogOnFailure and other such methods
* to return an error code result from a method call without needing to store in a local var
*
* Example usage:
*
* @code
* Resource * resource = GetResource();
* auto resourceHolder = ScopeExit([&] { resource->Release() });
* // If the call below fails, logs, returns the error code, and calls resourceHolder
* ReturnAndLogOnFailure(ProcessAndSaveResource(resource), Module, "Failure message: %s", param);
* resourceHolder->release(); // Cancel clean-up at end of successful method
* @endcode
*/
template <class F>
__attribute__((always_inline)) inline auto ScopeExit(F && fn)
{
auto deleter = [f = std::forward<F>(fn)](void *) mutable { f(); };
return std::unique_ptr<void, decltype(deleter)>(reinterpret_cast<void *>(1), std::move(deleter));
}
} // namespace chip