/** * @file nstdc_assert.c * @brief アサーションを扱うモジュール * @author Nomura Kei * @copyright 2003 - 2017 Nomura Kei * License: New BSD License (3-cclause BSD license) */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <nstdc_assert.h> /** デフォルトハンドラの出力先 */ #define DEFAULT_OUTPUT stderr /* ----------------------------------------------------------------------------- * 内部関数 プロトタイプ宣言 * ----------------------------------------------------------------------------- */ static void default_handler (long val, const char* vname, const char* file, int line, const char* func); static void default_handler_num(long v1, long v2, const char* vname, const char* file, int line, const char* func); static void default_handler_str(const char* v1, const char* v2 , const char* vname, const char* file, int line, const char* func); static void default_handler_mem(const void* v1, const void* v2, size_t size, const char* vname, const char* file, int line, const char* func); static void default_handler_bef(bool is_error); static void default_handler_aft(bool is_error); static void assert_init(void); /* ----------------------------------------------------------------------------- * 内部変数 * ----------------------------------------------------------------------------- */ /** アサーションエラー発生時に呼び出されるハンドラ関数. */ static NstdcAssertHandlers assert_handlers; /** * アサーション発生時に呼び出されるハンドラ関数を設定します. * 指定がないハンドラは, デフォルトのハンドラが使用されます. * * @param handlers アサーション発生時に呼び出されるハンドラ */ void nstdc_assert_set_handlers(NstdcAssertHandlers* handlers) { if (handlers->handler != NULL) { assert_handlers.handler = handlers->handler; } if (handlers->handler_num != NULL) { assert_handlers.handler_num = handlers->handler_num; } if (handlers->handler_str != NULL) { assert_handlers.handler_str = handlers->handler_str; } if (handlers->handler_mem != NULL) { assert_handlers.handler_mem = handlers->handler_mem; } if (handlers->handler_bef != NULL) { assert_handlers.handler_bef = handlers->handler_bef; } if (handlers->handler_aft != NULL) { assert_handlers.handler_aft = handlers->handler_aft; } } /** * 指定された val が偽の場合, 予め登録されているハンドラ関数 (handler) を呼び出します. * ※handler_bef, handler_aft は常に呼び出されます. * * @param val 値 * @param vname 変数名 * @param file ソースファイル名 * @param line 行番号 * @param func 関数名 */ void nstdc_assert_(long val, const char* vname, const char* file, int line, const char* func) { bool is_error = !val; assert_init(); assert_handlers.handler_bef(is_error); if (is_error) { assert_handlers.handler(val, vname, file, line, func); } assert_handlers.handler_aft(is_error); } /** * 指定された v1, v2 が異なる場合, 予め登録されているハンドラ関数 (handler_num) を呼び出します. * ※handler_bef, handler_aft は常に呼び出されます. * * @param v1 比較する値1 * @param v2 比較する値2 * @param vname 変数名 * @param file ソースファイル名 * @param line 行番号 * @param func 関数名 */ void nstdc_assert_num_(long v1 , long v2, const char* vname, const char* file, int line, const char* func) { bool is_error = (v1 != v2); assert_init(); assert_handlers.handler_bef(is_error); if (is_error) { assert_handlers.handler_num(v1, v2, vname, file, line, func); } assert_handlers.handler_aft(is_error); } /** * 指定された v1, v2 が異なる場合, 予め登録されているハンドラ関数 (handler_str) を呼び出します. * ※handler_bef, handler_aft は常に呼び出されます. * * @param v1 比較する文字列1 * @param v2 比較する文字列2 * @param vname 変数名 * @param file ソースファイル名 * @param line 行番号 * @param func 関数名 */ void nstdc_assert_str_(const char* v1 , const char* v2, const char* vname, const char* file, int line, const char* func) { int ret; bool is_error; assert_init(); if ((v1 != NULL) && (v2 != NULL)) { /* v1, v2 どちらも NULL でなければ文字列比較 */ ret = strcmp(v1, v2); is_error = (ret != 0); } else if ((v1 == NULL) && (v2 == NULL)) { /* v1, v2 どちらも NULL であれば一致とする */ is_error = false; } else { /* v1, v2 どちらかが NULL のため不一致 */ is_error = true; } /* ハンドラ関数を呼び出す */ assert_handlers.handler_bef(is_error); if (is_error) { assert_handlers.handler_str(v1, v2, vname, file, line, func); } assert_handlers.handler_aft(is_error); } /** * 指定された v1, v2 が異なる場合, 予め登録されているハンドラ関数 (handler_mem) を呼び出します. * ※handler_bef, handler_aft は常に呼び出されます. * * @param v1 比較するデータ1 * @param v2 比較するデータ2 * @param size 比較するデータサイズ * @param vname 変数名 * @param file ソースファイル名 * @param line 行番号 * @param func 関数名 */ void nstdc_assert_mem_(const char* v1, const char* v2, size_t size, const char* vname, const char* file, int line, const char* func) { int ret; bool is_error; assert_init(); if ((v1 != NULL) && (v2 != NULL)) { /* v1, v2 どちらも NULL でなければデータ比較 */ ret = memcmp(v1, v2, size); is_error = (ret != 0); } else if ((v1 == NULL) && (v2 == NULL)) { /* v1, v2 どちらも NULL であれば一致とする */ is_error = false; } else { /* v1, v2 どちらかが NULL のため不一致 */ is_error = true; } /* ハンドラ関数を呼び出す */ assert_handlers.handler_bef(is_error); if (is_error) { assert_handlers.handler_mem(v1, v2, size, vname, file, line, func); } assert_handlers.handler_aft(is_error); } /* ----------------------------------------------------------------------------- * 以下, 内部関数実装 * ----------------------------------------------------------------------------- */ /** * デフォルトのハンドラ関数(真偽). * * @param val 値 * @param vname 変数名 * @param file ソースファイル名 * @param line 行番号 * @param func 関数名 */ static void default_handler(long val, const char* vname, const char* file, int line, const char* func) { fprintf(DEFAULT_OUTPUT, "%s:%d:%s %s is not true (val = %ld)\n", file, line, func, vname, val); } /** * デフォルトのハンドラ関数(数値比較). * * @param v1 比較する値1 * @param v2 比較する値2 * @param vname 変数名 * @param file ソースファイル名 * @param line 行番号 * @param func 関数名 */ static void default_handler_num(long v1, long v2, const char* vname, const char* file, int line, const char* func) { fprintf(DEFAULT_OUTPUT, "%s:%d:%s %s is not %ld (v2 = %ld)\n", file, line, func, vname, v1, v2); } /** * デフォルトのハンドラ関数(文字列比較). * * @param v1 比較する文字列1 * @param v2 比較する文字列2 * @param vname 変数名 * @param file ソースファイル名 * @param line 行番号 * @param func 関数名 */ static void default_handler_str(const char* v1, const char* v2, const char* vname, const char* file, int line, const char* func) { const char* v1str = (v1 == NULL) ? "(NULL)" : v1; const char* v2str = (v2 == NULL) ? "(NULL)" : v2; fprintf(DEFAULT_OUTPUT, "%s:%d:%s %s is not %s (v2 = %s)\n", file, line, func, vname, v1str, v2str); } /** * デフォルトのハンドラ関数(メモリ比較). * * @param v1 比較するデータ1 * @param v2 比較するデータ2 * @param vname 変数名 * @param file ソースファイル名 * @param line 行番号 * @param func 関数名 */ static void default_handler_mem(const void* v1, const void* v2, size_t size, const char* vname, const char* file, int line, const char* func) { UNUSED_VARIABLE(size); if (v1 == NULL) { /* v1 が NULL で v2 が NULL でない */ fprintf(DEFAULT_OUTPUT, "%s:%d:%s %s is not NULL\n", file, line, func, vname); } else if (v2 == NULL) { /* v2 が NULL で v1 が NULL でない */ fprintf(DEFAULT_OUTPUT, "%s:%d:%s %s is NULL\n", file, line, func, vname); } else { /* v1, v2 共に NULL でなく不一致 */ fprintf(DEFAULT_OUTPUT, "%s:%d:%s %s is not an expected value\n", file, line, func, vname); } } /** * アサーションの前処理として呼び出されるデフォルトハンドラ関数. * 本関数ではなにもしません. * * @param is_error true/false (アサーションエラー発生/エラーなし) */ static void default_handler_bef(bool is_error) { /* Nothing to do. */ UNUSED_VARIABLE(is_error); } /** * アサーションの後処理として呼び出されるハンドラ関数. * アサーションエラー発生時に, abort する. * * @param is_error true/false (アサーションエラー発生/エラーなし) */ static void default_handler_aft(bool is_error) { if (is_error) { /* アサーションエラー発生時は abort */ abort(); } } /** * アサーションの初期化を実施します. * 初期化では, アサーションエラー発生時のデフォルトハンドラを設定します. * 初期化は一度だけ実施されます. */ static void assert_init(void) { static bool isinit = false; if (!isinit) { assert_handlers.handler = default_handler; assert_handlers.handler_num = default_handler_num; assert_handlers.handler_str = default_handler_str; assert_handlers.handler_mem = default_handler_mem; assert_handlers.handler_bef = default_handler_bef; assert_handlers.handler_aft = default_handler_aft; isinit = true; } }