blob: f1a4d07c02df0e2840bd7ea23ffa8d3c3127a0c8 [file] [log] [blame]
// Copyright 2020 The Pigweed Authors
//
// 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
//
// https://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.
// =============================================================================
//
// This file describes Pigweed's public user-facing assert API.
//
// THIS API IS NOT STABLE OR COMPLETE! NEITHER FACADE NOR BACKEND API!
//
#pragma once
#include "pw_preprocessor/macro_arg_count.h"
// The pw_assert public API:
//
// Trigger a crash with a message. Replaces LOG_FATAL() in other systems.
// PW_CRASH(msg, ...)
//
// Asserts the condition, crashes on failure. Equivalent to assert.
// PW_CHECK(condition) or
// PW_CHECK(condition, msg, ...)
//
// In many cases an assert is a binary comparison. In those cases, using the
// special binary assert macros below for <, <=, >, >=, == enables reporting
// the values of the operands in addition to the string of the condition.
//
// In all cases, the message argument is optional:
// PW_CHECK_INT_LE(x, y) or
// PW_CHECK_INT_LE(x, y, "Was booting %s subsystem", subsystem_name)
//
// Binary comparison asserts for 'int' type ("%d" in format strings):
// PW_CHECK_INT_LE (a, b, msg, ...) Asserts a <= b
// PW_CHECK_INT_LT (a, b, msg, ...) Asserts a < b
// PW_CHECK_INT_GE (a, b, msg, ...) Asserts a >= b
// PW_CHECK_INT_GT (a, b, msg, ...) Asserts a > b
// PW_CHECK_INT_EQ (a, b, msg, ...) Asserts a == b
//
// Binary comparison asserts for 'unsigned int' type ("%u" in format strings):
// PW_CHECK_UINT_LE (a, b, msg, ...) Asserts a <= b
// PW_CHECK_UINT_LT (a, b, msg, ...) Asserts a < b
// PW_CHECK_UINT_GE (a, b, msg, ...) Asserts a >= b
// PW_CHECK_UINT_GT (a, b, msg, ...) Asserts a > b
// PW_CHECK_UINT_EQ (a, b, msg, ...) Asserts a == b
//
// Binary comparison asserts for 'float' type ("%f" in format strings):
// PW_CHECK_FLOAT_LE(a, b, msg, ...) Asserts a <= b
// PW_CHECK_FLOAT_LT(a, b, msg, ...) Asserts a < b
// PW_CHECK_FLOAT_GE(a, b, msg, ...) Asserts a >= b
// PW_CHECK_FLOAT_GT(a, b, msg, ...) Asserts a > b
// PW_CHECK_FLOAT_EQ(a, b, msg, ...) Asserts a == b
//
// Note: PW_CRASH is the equivalent of LOG_FATAL in other systems, where a
// device crash is triggered with a message. In Pigweed, logging and
// crashing/asserting are separated. There is a LOG_CRITICAL level in the
// logging module, but it does not have side effects; for LOG_FATAL, instead
// use this macro (PW_CRASH).
//
// The pw_assert_backend must provide these macros:
//
// PW_HANDLE_CRASH(msg, ...)
// PW_HANDLE_ASSERT_FAILURE(condition, msg, ...)
// PW_HANDLE_ASSERT_BINARY_COMPARE_FAILURE(a, op, b, type_fmt, msg, ...)
//
// The low level functionality of triggering a crash, rebooting a device,
// collecting information, or hanging out in a while(1) loop, must be
// provided by the underlying assert backend as part of the crash or assert
// failure handling.
//
// Note that for the assert failures, the handler should assume the assert
// has already failed (the facade checks the condition before delegating).
//
#include "pw_assert_backend/assert_backend.h"
// PW_CRASH - Crash the system, with a message.
#define PW_CRASH(message, ...) \
PW_HANDLE_CRASH(message PW_COMMA_ARGS(__VA_ARGS__))
// PW_CHECK - If condition evaluates to false, crash. Message optional.
#define PW_CHECK(condition, ...) \
do { \
if (!(condition)) { \
_PW_CHECK_SELECT_MACRO( \
#condition, PW_HAS_ARGS(__VA_ARGS__), __VA_ARGS__); \
} \
} while (0)
// PW_CHECK_<type>_<comparison> - if
// Checks for int: LE, LT, GE, GT, EQ.
#define PW_CHECK_INT_LE(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, int, "%d", __VA_ARGS__)
#define PW_CHECK_INT_LT(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, <, argb, int, "%d", __VA_ARGS__)
#define PW_CHECK_INT_GE(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, int, "%d", __VA_ARGS__)
#define PW_CHECK_INT_GT(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, >, argb, int, "%d", __VA_ARGS__)
#define PW_CHECK_INT_EQ(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, int, "%d", __VA_ARGS__)
// Checks for unsigned int: LE, LT, GE, GT, EQ.
#define PW_CHECK_UINT_LE(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, unsigned int, "%u", __VA_ARGS__)
#define PW_CHECK_UINT_LT(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, <, argb, unsigned int, "%u", __VA_ARGS__)
#define PW_CHECK_UINT_GE(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, unsigned int, "%u", __VA_ARGS__)
#define PW_CHECK_UINT_GT(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, >, argb, unsigned int, "%u", __VA_ARGS__)
#define PW_CHECK_UINT_EQ(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, unsigned int, "%u", __VA_ARGS__)
// Checks for float: LE, LT, GE, GT, EQ.
#define PW_CHECK_FLOAT_LE(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, float, "%f", __VA_ARGS__)
#define PW_CHECK_FLOAT_LT(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, <, argb, float, "%f", __VA_ARGS__)
#define PW_CHECK_FLOAT_GE(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, float, "%f", __VA_ARGS__)
#define PW_CHECK_FLOAT_GT(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, >, argb, float, "%f", __VA_ARGS__)
#define PW_CHECK_FLOAT_EQ(arga, argb, ...) \
_PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, float, "%f", __VA_ARGS__)
// =========================================================================
// Implementation for PW_CHECK
// TODO: Explain why we must expand another time.
#define _PW_CHECK_SELECT_MACRO(condition, has_args, ...) \
_PW_CHECK_SELECT_MACRO_EXPANDED(condition, has_args, __VA_ARGS__)
// Delegate to the macro
#define _PW_CHECK_SELECT_MACRO_EXPANDED(condition, has_args, ...) \
_PW_CHECK_HAS_MSG_##has_args(condition, __VA_ARGS__)
// PW_CHECK version 1: No message or args
#define _PW_CHECK_HAS_MSG_0(condition, ignored_arg) \
PW_HANDLE_ASSERT_FAILURE(condition, "")
// PW_CHECK version 2: With message (and maybe args)
#define _PW_CHECK_HAS_MSG_1(condition, ...) \
PW_HANDLE_ASSERT_FAILURE(condition, __VA_ARGS__)
// =========================================================================
// Implementation for PW_CHECK_<type>_<comparison>
// TODO: Explain why we must expand another time.
#define _PW_CHECK_BINARY_COMPARISON_SELECT_MACRO(argument_a_str, \
argument_a_val, \
comparison_op_str, \
argument_b_str, \
argument_b_val, \
type_fmt, \
has_args, \
...) \
_PW_CHECK_SELECT_BINARY_COMPARISON_MACRO_EXPANDED(argument_a_str, \
argument_a_val, \
comparison_op_str, \
argument_b_str, \
argument_b_val, \
type_fmt, \
has_args, \
__VA_ARGS__)
// Delegate to the macro
#define _PW_CHECK_SELECT_BINARY_COMPARISON_MACRO_EXPANDED(argument_a_str, \
argument_a_val, \
comparison_op_str, \
argument_b_str, \
argument_b_val, \
type_fmt, \
has_args, \
...) \
_PW_CHECK_BINARY_COMPARISON_HAS_MSG_##has_args(argument_a_str, \
argument_a_val, \
comparison_op_str, \
argument_b_str, \
argument_b_val, \
type_fmt, \
__VA_ARGS__)
// PW_CHECK_BINARY_COMPARISON version 1: No message or args
#define _PW_CHECK_BINARY_COMPARISON_HAS_MSG_0(argument_a_str, \
argument_a_val, \
comparison_op_str, \
argument_b_str, \
argument_b_val, \
type_fmt, \
ignored_arg) \
PW_HANDLE_ASSERT_BINARY_COMPARE_FAILURE(argument_a_str, \
argument_a_val, \
comparison_op_str, \
argument_b_str, \
argument_b_val, \
type_fmt, \
"");
// PW_CHECK_BINARY_COMPARISON version 2: With message (and maybe args)
//
// TODO(pwbug/117): Passing __VA_ARGS__ below should instead use
// PW_COMMA_ARGS(), but for some reason this isn't working. For now, leave it.
#define _PW_CHECK_BINARY_COMPARISON_HAS_MSG_1(argument_a_str, \
argument_a_val, \
comparison_op_str, \
argument_b_str, \
argument_b_val, \
type_fmt, \
...) \
PW_HANDLE_ASSERT_BINARY_COMPARE_FAILURE(argument_a_str, \
argument_a_val, \
comparison_op_str, \
argument_b_str, \
argument_b_val, \
type_fmt, \
__VA_ARGS__);
// For the binary assertions, this private macro is re-used for all the
// variants. Due to limitations of C formatting, it is necessary to have
// separate macros for the types.
//
// The macro avoids evaluating the arguments multiple times at the cost of some
// macro complexity.
//
// TODO: Concat names with __LINE__; requires an extra layer of macros.
#define _PW_CHECK_BINARY_CMP_IMPL( \
argument_a, comparison_op, argument_b, type_decl, type_fmt, ...) \
do { \
type_decl evaluated_argument_a = (type_decl)(argument_a); \
type_decl evaluated_argument_b = (type_decl)(argument_b); \
if (!(evaluated_argument_a comparison_op evaluated_argument_b)) { \
_PW_CHECK_BINARY_COMPARISON_SELECT_MACRO(#argument_a, \
evaluated_argument_a, \
#comparison_op, \
#argument_b, \
evaluated_argument_b, \
type_fmt, \
PW_HAS_ARGS(__VA_ARGS__), \
__VA_ARGS__); \
} \
} while (0)