blob: 174b37fc33e6f25187068600d48e27dd13e34884 [file] [log] [blame]
/*
* Copyright 2010-2016 Nest Labs Inc. All Rights Reserved.
*
* 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.
*
* Description:
* This file defines macros internal to the implementation of the
* Nest Labs assertion and exception checking facility.
*
*/
#ifndef NLASSERT_INTERNAL_H
#define NLASSERT_INTERNAL_H
#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
# define __nlLIKELY(condition) __builtin_expect(condition, 1)
# define __nlUNLIKELY(condition) __builtin_expect(condition, 0)
#else
# define __nlLIKELY(condition) (condition)
# define __nlUNLIKELY(condition) (condition)
#endif /* defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) */
// Notes on the unusual design of __nlSHOULD_ASSERT and __nl_ASSERT_UNUSED:
//
//
// For non-production builds (aka "development" or "debug" builds)
// ===============================================================
//
// The nlASSERT(condition) macro evaluates the "condition" expression and, if
// it is false, calls NL_ASSERT_ABORT() to abort execution (after optionally
// performing some logging and debugging operations). Partially expanded, for
// clarity, it looks like this:
//
// if (__nlSHOULD_ASSERT(condition))
// {
// do
// {
// /* optional logging, backtrace, and/or trap handling */
// } while (0);
//
// {
// NL_ASSERT_ABORT();
// }
// }
// else do { /* nothing */ } while (0)
//
// NOTE: The "if (foo) / else do { } while (0)" construct is just a more
// robust version of the standard "do / while (0)" macro wrapper.
// It is explained below, in the comments accompanying __nlCHECK.
//
// The __nlSHOULD_ASSERT(condition) macro must evaluate to true if "condition"
// is false; conceptually, its definition is "!(condition)". But it is not
// actually defined that way, for this reason:
//
// It is not uncommon for an equality test like "if (x == y)" to be accidentally
// written as an assignment: "if (x = y)". GCC and similar compilers can detect
// this and emit a warning, but the warning is suppressed if the assignment is
// surrounded by an extra pair of parentheses to indicate that it is
// intentional: "if ((x = y))". So if __nlSHOULD_ASSERT(condition) were defined
// as "!(condition)", the parentheses around "condition" -- required by the "!"
// operator -- would be seen by the compiler as an indication that the
// assignment was intentional, so no warning would be emitted if, for example,
// "nlASSERT(x == y)" were mistakenly written as "nlASSERT(x = y)".
//
// Therefore, __nlSHOULD_ASSERT(condition) is defined so that there will be no
// extra parentheses around "condition" when the nlASSERT(condition) macro is
// expanded. With this definition, nlASSERT(condition) expands to:
//
// if (condition)
// {
// /* do nothing */
// }
// else if (1)
// {
// do
// {
// /* optional logging, backtrace, and/or trap handling */
// } while (0);
//
// {
// NL_ASSERT_ABORT();
// }
// }
// else do { /* nothing */ } while (0)
//
// GCC's branch-prediction hinting mechanism ("__builtin_expect(condition,1)")
// would also suppress the "unintended assignment" warning, so is not used in
// the macro definition. But the macro compiles to exactly the same assembly
// code as it would if the hint were included, so omitting the hint incurs no
// speed or memory cost. This is true for both ARM and x86 ISAs (tested under
// GCC 4.x.x, with optimization for speed enabled via compiler flag -O3).
//
//
// For production builds (aka "release" builds)
// ============================================
//
// The nlASSERT(condition) macro is disabled by defining it as
// __nlASSERT_UNUSED(condition).
//
// The __nlASSERT_UNUSED(condition) macro must not perform any logging or
// debugging operations, and it must not abort program execution even when
// "condition" is false. It cannot simply be defined as "(void)0", however,
// because it must allow side effects in "condition", if any, to occur exactly
// as they would in the non-production version of the macro. And it can't be
// defined as (void)(condition) because it is desirable for an unintended
// assignment in "condition" to be caught by the compiler, just as it would be
// in a non-production build.
//
// Therefore, __nl_ASSERT_UNUSED(condition) is defined so that "condition" is
// treated as a truth value, to ensure that unintended assignment will be
// caught, and so that "condition" is evaluated at runtime if and only if it
// would also be evaluated by the non-production version of nlASSERT(condition),
// to ensure that side effects will occur identically.
#define __nlSHOULD_ASSERT(condition) condition) { /* do nothing */ } else if (1
#define __nlASSERT_UNUSED(condition) do { if (condition) { /* do nothing */ } } while (0)
/** @cond */
#define __nlSTATIC_ASSERT_CONCAT(aPrefix, aSuffix) aPrefix ## aSuffix
#define _nlSTATIC_ASSERT_CONCAT(aPrefix, aSuffix) __nlSTATIC_ASSERT_CONCAT(aPrefix, aSuffix)
/** @endcond */
/**
* @def _nlSTATIC_ASSERT(aCondition)
*
* @brief
* This checks, at compile-time, for the specified condition, which
* is expected to commonly be true and terminates compilation if the
* condition is false. It is legal, in both C and C++, anywhere that
* a declaration would be.
*
* @note This is a package-internal interface. If C++11/C11 or greater is
* available, this falls back to using C++11/C11 intrinsic facilities:
* static_assert or _Static_assert.
*
* @param[in] aCondition A Boolean expression to be evaluated.
* @param[in] aMessage A pointer to a NULL-terminated C string
* containing a caller-specified message
* further describing the assertion
* failure. Note, this message is not
* actually emitted in any meaningful way for
* non-C11 or -C++11 code. It serves to
* simply comment or annotate the assertion
* and to provide interface parallelism with
* the run-time assertion interfaces.
*
*/
#if defined(__cplusplus) && (__cplusplus >= 201103L)
# define _nlSTATIC_ASSERT(aCondition, aMessage) static_assert(aCondition, aMessage)
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
# define _nlSTATIC_ASSERT(aCondition, aMessage) _Static_assert(aCondition, aMessage)
#else
# ifdef __COUNTER__
# define _nlSTATIC_ASSERT(aCondition, aMessage) typedef char _nlSTATIC_ASSERT_CONCAT(STATIC_ASSERT_t_, __COUNTER__)[(aCondition) ? 1 : -1] __attribute__ ((unused))
# else
# define _nlSTATIC_ASSERT(aCondition, aMessage) typedef char _nlSTATIC_ASSERT_CONCAT(STATIC_ASSERT_t_, __LINE__)[(aCondition) ? 1 : -1] __attribute__ ((unused))
# endif
#endif /* defined(__cplusplus) && (__cplusplus >= 201103L) */
// __nlSTATIC_ASSERT_UNUSED(aCondition)
//
// Can be used everywhere that _nlSTATIC_ASSERT can, and behaves exactly the
// same way except that it never asserts.
#if defined(__cplusplus) && (__cplusplus >= 201103L)
# define __nlSTATIC_ASSERT_UNUSED(aCondition, aMessage) static_assert((aCondition) || 1, aMessage)
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
# define __nlSTATIC_ASSERT_UNUSED(aCondition, aMessage) _Static_assert((aCondition) || 1, aMessage)
#else
# ifdef __COUNTER__
# define __nlSTATIC_ASSERT_UNUSED(aCondition, aMessage) typedef char _nlSTATIC_ASSERT_CONCAT(STATIC_ASSERT_t_, __COUNTER__)[((aCondition) || 1) ? 1 : -1] __attribute__ ((unused))
# else
# define __nlSTATIC_ASSERT_UNUSED(aCondition, aMessage) typedef char _nlSTATIC_ASSERT_CONCAT(STATIC_ASSERT_t_, __LINE__)[((aCondition) || 1) ? 1 : -1] __attribute__ ((unused))
# endif
#endif /* defined(__cplusplus) && (__cplusplus >= 201103L) */
#define __NL_ASSERT_MAYBE_RUN_TRIGGERS(flags, prefix, name, condition, label, file, line, message) \
do \
{ \
if ((flags) & NL_ASSERT_FLAG_LOG) { \
NL_ASSERT_LOG(prefix, name, condition, label, file, line, message); \
} \
if ((flags) & NL_ASSERT_FLAG_BACKTRACE) { \
NL_ASSERT_BACKTRACE(); \
} \
if ((flags) & NL_ASSERT_FLAG_TRAP) { \
NL_ASSERT_TRAP(); \
} \
} while (0)
#define __NL_ASSERT_MAYBE_RUN_PRE_ACTION_TRIGGERS(flags, prefix, name, condition, label, file, line, message) \
do \
{ \
if ((flags) & NL_ASSERT_FLAG_LOG) { \
NL_ASSERT_LOG(prefix, name, condition, label, file, line, message); \
} \
if ((flags) & NL_ASSERT_FLAG_BACKTRACE) { \
NL_ASSERT_BACKTRACE(); \
} \
} while (0)
#define __NL_ASSERT_MAYBE_RUN_POST_ACTION_TRIGGERS(flags, prefix, name, condition, label, file, line, message) \
do \
{ \
if ((flags) & NL_ASSERT_FLAG_TRAP) { \
NL_ASSERT_TRAP(); \
} \
} while (0)
// __nlEXPECT
#define __nlEXPECT(flags, condition, label) \
do \
{ \
if (__nlSHOULD_ASSERT(condition)) \
{ \
__NL_ASSERT_MAYBE_RUN_TRIGGERS((flags), \
NL_ASSERT_PREFIX_STRING, \
NL_ASSERT_COMPONENT_STRING, \
#condition, \
#label, \
NL_ASSERT_FILE, \
__LINE__, \
0); \
goto label; \
} \
} while (0)
#define __nlEXPECT_PRINT(flags, condition, label, message) \
do \
{ \
if (__nlSHOULD_ASSERT(condition)) \
{ \
__NL_ASSERT_MAYBE_RUN_TRIGGERS((flags), \
NL_ASSERT_PREFIX_STRING, \
NL_ASSERT_COMPONENT_STRING, \
#condition, \
#label, \
NL_ASSERT_FILE, \
__LINE__, \
message); \
goto label; \
} \
} while (0)
#define __nlEXPECT_ACTION(flags, condition, label, action) \
do \
{ \
if (__nlSHOULD_ASSERT(condition)) \
{ \
__NL_ASSERT_MAYBE_RUN_PRE_ACTION_TRIGGERS((flags), \
NL_ASSERT_PREFIX_STRING, \
NL_ASSERT_COMPONENT_STRING, \
#condition, \
#label, \
NL_ASSERT_FILE, \
__LINE__, \
0); \
{ \
action; \
} \
__NL_ASSERT_MAYBE_RUN_POST_ACTION_TRIGGERS((flags), \
NL_ASSERT_PREFIX_STRING, \
NL_ASSERT_COMPONENT_STRING, \
#condition, \
#label, \
NL_ASSERT_FILE, \
__LINE__, \
0); \
goto label; \
} \
} while (0)
#define __nlEXPECT_ACTION_PRINT(flags, condition, label, action, message) \
do \
{ \
if (__nlSHOULD_ASSERT(condition)) \
{ \
__NL_ASSERT_MAYBE_RUN_PRE_ACTION_TRIGGERS((flags), \
NL_ASSERT_PREFIX_STRING, \
NL_ASSERT_COMPONENT_STRING, \
#condition, \
#label, \
NL_ASSERT_FILE, \
__LINE__, \
message); \
{ \
action; \
} \
__NL_ASSERT_MAYBE_RUN_POST_ACTION_TRIGGERS((flags), \
NL_ASSERT_PREFIX_STRING, \
NL_ASSERT_COMPONENT_STRING, \
#condition, \
#label, \
NL_ASSERT_FILE, \
__LINE__, \
message); \
goto label; \
} \
} while (0)
#define __nlEXPECT_SUCCESS(flags, status, label) __nlEXPECT(flags, status == 0, label)
#define __nlEXPECT_SUCCESS_PRINT(flags, status, label, message) __nlEXPECT_PRINT(flags, status == 0, label, message)
#define __nlEXPECT_SUCCESS_ACTION(flags, status, label, action) __nlEXPECT_ACTION(flags, status == 0, label, action)
#define __nlEXPECT_SUCCESS_ACTION_PRINT(flags, status, label, action, message) __nlEXPECT_ACTION_PRINT(flags, status == 0, label, action, message)
#define __nlNEXPECT(flags, condition, label) __nlEXPECT(flags, !(condition), label)
#define __nlNEXPECT_PRINT(flags, condition, label, message) __nlEXPECT_PRINT(flags, !(condition), label, message)
#define __nlNEXPECT_ACTION(flags, condition, label, action) __nlEXPECT_ACTION(flags, !(condition), label, action)
#define __nlNEXPECT_ACTION_PRINT(flags, condition, label, action, message) __nlEXPECT_ACTION_PRINT(flags, !(condition), label, action, message)
// __nlCHECK
//
// NOTE: Some of these macros take a C statement as a parameter. The unusual
// "else do {} while(0)" construct allows those macros to work properly when
// that parameter is set to "continue" or "break" (which isn't unexpected in
// an assert macro).
//
// If the macros were written in the standard way by wrapping them in a
// "do/while(0)", they could fail silently: A "continue" or "break" statement,
// intended to break out of one or all iterations of the loop containing the
// macro invocation -- would instead just break out of the macro's internal
// do-while.
#define __nlCHECK(flags, condition) \
if (__nlSHOULD_ASSERT(condition)) \
{ \
__NL_ASSERT_MAYBE_RUN_TRIGGERS((flags), \
NL_ASSERT_PREFIX_STRING, \
NL_ASSERT_COMPONENT_STRING, \
#condition, \
0, \
NL_ASSERT_FILE, \
__LINE__, \
0); \
} \
else do {} while (0)
#define __nlCHECK_ACTION(flags, condition, action) \
if (__nlSHOULD_ASSERT(condition)) \
{ \
__NL_ASSERT_MAYBE_RUN_PRE_ACTION_TRIGGERS((flags), \
NL_ASSERT_PREFIX_STRING, \
NL_ASSERT_COMPONENT_STRING, \
#condition, \
0, \
NL_ASSERT_FILE, \
__LINE__, \
0); \
{ \
action; \
} \
__NL_ASSERT_MAYBE_RUN_POST_ACTION_TRIGGERS((flags), \
NL_ASSERT_PREFIX_STRING, \
NL_ASSERT_COMPONENT_STRING, \
#condition, \
0, \
NL_ASSERT_FILE, \
__LINE__, \
0); \
} \
else do {} while (0) /* This is explained in the comment above __nlCHECK */
#define __nlCHECK_PRINT(flags, condition, message) \
if (__nlSHOULD_ASSERT(condition)) \
{ \
__NL_ASSERT_MAYBE_RUN_TRIGGERS((flags), \
NL_ASSERT_PREFIX_STRING, \
NL_ASSERT_COMPONENT_STRING, \
#condition, \
0, \
NL_ASSERT_FILE, \
__LINE__, \
message); \
} \
else do {} while (0) /* This is explained in the comment above __nlCHECK */
#define __nlCHECK_SUCCESS(flags, status) __nlCHECK(flags, status == 0)
#define __nlCHECK_SUCCESS_ACTION(flags, status, action) __nlCHECK_ACTION(flags, status == 0, action)
#define __nlCHECK_SUCCESS_PRINT(flags, status, message) __nlCHECK_PRINT(flags, status == 0, message)
#define __nlNCHECK(flags, condition) __nlCHECK(flags, !(condition))
#define __nlNCHECK_ACTION(flags, condition, action) __nlCHECK_ACTION(flags, !(condition), action)
#define __nlNCHECK_PRINT(flags, condition, message) __nlCHECK_PRINT(flags, !(condition), message)
// __nlVERIFY
#define __nlVERIFY(flags, condition) __nlCHECK(flags, condition)
#define __nlVERIFY_ACTION(flags, condition, action) __nlCHECK_ACTION(flags, condition, action)
#define __nlVERIFY_PRINT(flags, condition, message) __nlCHECK_PRINT(flags, condition, message)
#define __nlVERIFY_SUCCESS(flags, status) __nlCHECK_SUCCESS(flags, status)
#define __nlVERIFY_SUCCESS_ACTION(flags, status, action) __nlCHECK_SUCCESS_ACTION(flags, status, action)
#define __nlVERIFY_SUCCESS_PRINT(flags, status, message) __nlCHECK_SUCCESS_PRINT(flags, status, message)
#define __nlNVERIFY(flags, condition) __nlNCHECK(flags, condition)
#define __nlNVERIFY_ACTION(flags, condition, action) __nlNCHECK_ACTION(flags, condition, action)
#define __nlNVERIFY_PRINT(flags, condition, message) __nlNCHECK_PRINT(flags, condition, message)
// __nlPRECONDITION
#define __nlPRECONDITION(flags, condition, termination) \
if (__nlSHOULD_ASSERT(condition)) \
{ \
__NL_ASSERT_MAYBE_RUN_TRIGGERS((flags), \
NL_ASSERT_PREFIX_STRING, \
NL_ASSERT_COMPONENT_STRING, \
#condition, \
0, \
NL_ASSERT_FILE, \
__LINE__, \
0); \
{ \
termination; \
} \
} \
else do {} while (0) /* This is explained in the comment above __nlCHECK */
#define __nlPRECONDITION_ACTION(flags, condition, termination, action) \
if (__nlSHOULD_ASSERT(condition)) \
{ \
__NL_ASSERT_MAYBE_RUN_PRE_ACTION_TRIGGERS((flags), \
NL_ASSERT_PREFIX_STRING, \
NL_ASSERT_COMPONENT_STRING, \
#condition, \
0, \
NL_ASSERT_FILE, \
__LINE__, \
0); \
{ \
action; \
} \
__NL_ASSERT_MAYBE_RUN_POST_ACTION_TRIGGERS((flags), \
NL_ASSERT_PREFIX_STRING, \
NL_ASSERT_COMPONENT_STRING, \
#condition, \
0, \
NL_ASSERT_FILE, \
__LINE__, \
0); \
{ \
termination; \
} \
} \
else do {} while (0) /* This is explained in the comment above __nlCHECK */
#define __nlPRECONDITION_PRINT(flags, condition, termination, message) \
if (__nlSHOULD_ASSERT(condition)) \
{ \
__NL_ASSERT_MAYBE_RUN_TRIGGERS((flags), \
NL_ASSERT_PREFIX_STRING, \
NL_ASSERT_COMPONENT_STRING, \
#condition, \
0, \
NL_ASSERT_FILE, \
__LINE__, \
message); \
{ \
termination; \
} \
} \
else do {} while (0) /* This is explained in the comment above __nlCHECK */
#define __nlPRECONDITION_SUCCESS(flags, status, termination) __nlPRECONDITION(flags, status == 0, termination)
#define __nlPRECONDITION_SUCCESS_ACTION(flags, status, termination, action) __nlPRECONDITION_ACTION(flags, status == 0, termination, action)
#define __nlPRECONDITION_SUCCESS_PRINT(flags, status, termination, message) __nlPRECONDITION_PRINT(flags, status == 0, termination, message)
#define __nlNPRECONDITION(flags, condition, termination) __nlPRECONDITION(flags, !(condition), termination)
#define __nlNPRECONDITION_ACTION(flags, condition, termination, action) __nlPRECONDITION_ACTION(flags, !(condition), termination, action)
#define __nlNPRECONDITION_PRINT(flags, condition, termination, message) __nlPRECONDITION_PRINT(flags, !(condition), termination, message)
// __nlABORT
#define __nlABORT(flags, condition) __nlPRECONDITION(flags, condition, NL_ASSERT_ABORT())
#define __nlABORT_ACTION(flags, condition, action) __nlPRECONDITION_ACTION(flags, condition, NL_ASSERT_ABORT(), action)
#endif /* NLASSERT_INTERNAL_H */