# HG changeset patch # User David Demelier # Date 1585570041 -7200 # Node ID e82eca4f86066e16c5cd3d42c9fc7e7c4e80ae75 # Parent ed72843a719484b71a2cd8d523b1180b2409b080 core: simplify error/panic routines diff -r ed72843a7194 -r e82eca4f8606 doxygen/Doxyfile --- a/doxygen/Doxyfile Mon Mar 30 13:34:42 2020 +0200 +++ b/doxygen/Doxyfile Mon Mar 30 14:07:21 2020 +0200 @@ -21,6 +21,8 @@ PROJECT_NUMBER = "0.1.0" PROJECT_BRIEF = "2D RPG game in C" PROJECT_LOGO = +ENABLE_PREPROCESSING = YES +PREDEFINED = DOXYGEN OUTPUT_DIRECTORY = doxygen ALLOW_UNICODE_NAMES = YES STRIP_FROM_PATH = src diff -r ed72843a7194 -r e82eca4f8606 doxygen/page-howto-initialization.c --- a/doxygen/page-howto-initialization.c Mon Mar 30 13:34:42 2020 +0200 +++ b/doxygen/page-howto-initialization.c Mon Mar 30 14:07:21 2020 +0200 @@ -28,12 +28,12 @@ * \section example Example * * Init the core and create a window of Full HD resolution. The function \ref - * error_fatal will print the error and exit if needed. + * panic will all the panic handler. * * \code * if (!sys_init()) - * error_fatal(); + * panic(); * if (!window_init("My Awesome Game", 1920, 1080)) - * error_fatal(); + * panic(); * \endcode */ diff -r ed72843a7194 -r e82eca4f8606 src/adventure/main.c --- a/src/adventure/main.c Mon Mar 30 13:34:42 2020 +0200 +++ b/src/adventure/main.c Mon Mar 30 14:07:21 2020 +0200 @@ -60,9 +60,9 @@ init(void) { if (!sys_init()) - error_fatal(); + panic(); if (!window_init("Molko's Adventure", WINDOW_WIDTH, WINDOW_HEIGHT)) - error_fatal(); + panic(); /* Init unrecoverable panic state. */ panic_state_init(); @@ -82,11 +82,11 @@ int panic_trigger = 0; if (!(font_open(&font, sys_datapath("fonts/DejaVuSans.ttf"), 15))) - error_fatal(); + panic(); if (!(font_open(&debug_font, sys_datapath("fonts/DejaVuSans.ttf"), 10))) - error_fatal(); + panic(); if (!(image_open(&frame, sys_datapath("images/message.png")))) - error_fatal(); + panic(); debug_options.default_font = &debug_font; debug_options.enable = true; @@ -110,7 +110,7 @@ ev.type == EVENT_KEYDOWN && ev.key.key == KEY_F12) { if (++panic_trigger == 3) - panic("User panic request."); + panicf("User panic request."); } game_handle(&ev); diff -r ed72843a7194 -r e82eca4f8606 src/adventure/mainmenu_state.c --- a/src/adventure/mainmenu_state.c Mon Mar 30 13:34:42 2020 +0200 +++ b/src/adventure/mainmenu_state.c Mon Mar 30 14:07:21 2020 +0200 @@ -63,7 +63,7 @@ struct font font = { 0 }; if (!font_open(&font, sys_datapath("fonts/pirata-one.ttf"), 30)) - error_fatal(); + panic(); substate = SUBSTATE_MOVING; x = splashscreen_state_data.x; @@ -91,11 +91,11 @@ { /* Prepare map. */ if (!map_open(&map_state_data.map.map, sys_datapath("maps/test.map"))) - error_fatal(); + panic(); /* Prepare image and sprite. */ if (!(image_open(&image, sys_datapath("sprites/test-walk.png")))) - error_fatal(); + panic(); sprite_init(&map_state_data.player.sprite, &image, 48, 48); game_switch(&map_state, false); diff -r ed72843a7194 -r e82eca4f8606 src/adventure/panic_state.c --- a/src/adventure/panic_state.c Mon Mar 30 13:34:42 2020 +0200 +++ b/src/adventure/panic_state.c Mon Mar 30 14:07:21 2020 +0200 @@ -65,6 +65,20 @@ static struct label lerror; static void +die(const char *fmt, ...) +{ + assert(fmt); + + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "abort: "); + vfprintf(stderr, fmt, ap); + va_end(ap); + exit(1); +} + +static void enter(void) { } @@ -143,7 +157,7 @@ font_render(&data.font, &labels[i].texture, labels[i].text); if (!texture_ok(&labels[i].texture)) - error_fatal(); + die("%s\n", error()); } } @@ -209,5 +223,5 @@ * on the console. */ if (!(font_open(&data.font, sys_datapath(FONT), FONT_SZ))) - error_fatal(); + die("%s", error()); } diff -r ed72843a7194 -r e82eca4f8606 src/adventure/splashscreen_state.c --- a/src/adventure/splashscreen_state.c Mon Mar 30 13:34:42 2020 +0200 +++ b/src/adventure/splashscreen_state.c Mon Mar 30 14:07:21 2020 +0200 @@ -16,17 +16,17 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "error.h" -#include "font.h" -#include "game.h" -#include "image.h" -#include "map.h" -#include "map_state.h" -#include "painter.h" -#include "state.h" -#include "sys.h" -#include "texture.h" -#include "window.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "splashscreen_state.h" #include "mainmenu_state.h" @@ -43,9 +43,9 @@ }; if (!(font_open(&font, sys_datapath("fonts/teutonic1.ttf"), 130))) - error_fatal(); + panic(); if (!(font_render(&font, &splashscreen_state_data.text, "Molko's Adventure"))) - error_fatal(); + panic(); /* Compute position. */ const unsigned int w = splashscreen_state_data.text.w; diff -r ed72843a7194 -r e82eca4f8606 src/core/error.c --- a/src/core/error.c Mon Mar 30 13:34:42 2020 +0200 +++ b/src/core/error.c Mon Mar 30 14:07:21 2020 +0200 @@ -16,6 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include #include @@ -45,6 +46,8 @@ bool error_printf(const char *fmt, ...) { + assert(fmt); + va_list ap; va_start(ap, fmt); @@ -57,35 +60,13 @@ bool error_vprintf(const char *fmt, va_list ap) { + assert(fmt); + vsnprintf(buffer, sizeof (buffer), fmt, ap); return false; } -noreturn void -error_fatal(void) -{ - fprintf(stderr, "%s\n", buffer); - exit(1); -} - -noreturn void -error_fatalf(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - error_vfatalf(fmt, ap); - va_end(ap); -} - -noreturn void -error_vfatalf(const char *fmt, va_list ap) -{ - fprintf(stderr, fmt, ap); - exit(1); -} - /* private: error_p.h */ bool diff -r ed72843a7194 -r e82eca4f8606 src/core/error.h --- a/src/core/error.h Mon Mar 30 13:34:42 2020 +0200 +++ b/src/core/error.h Mon Mar 30 14:07:21 2020 +0200 @@ -27,12 +27,13 @@ #include #include -#include + +#include "plat.h" /** * Get the last error returned. * - * \return the error string + * \return The error string. */ const char * error(void); @@ -41,7 +42,7 @@ * Convenient helper that sets last error from global C errno and then return * false. * - * \return false + * \return Always false. */ bool error_errno(void); @@ -49,43 +50,22 @@ /** * Set the game error with a printf-like format. * + * \pre fmt != NULL * \param fmt the format string - * \return false + * \return Always false. */ bool -error_printf(const char *fmt, ...); +error_printf(const char *fmt, ...) PLAT_PRINTF(1, 2); /** * Similar to \ref error_printf. * + * \pre fmt != NULL * \param fmt the format stinrg * \param ap the variadic arguments pointer - * \return false + * \return Always false. */ bool -error_vprintf(const char *fmt, va_list ap); - -/** - * Print last registered error and exit with code 1. - */ -noreturn void -error_fatal(void); - -/** - * Prints an error to stderr and exit with code 1. - * - * \param fmt the format string - */ -noreturn void -error_fatalf(const char *fmt, ...); - -/** - * Similar to \ref error_fatalf. - * - * \param fmt the format string - * \param ap the variadic arguments pointer - */ -noreturn void -error_vfatalf(const char *fmt, va_list ap); +error_vprintf(const char *fmt, va_list ap) PLAT_PRINTF(1, 0); #endif /* !MOLKO_ERROR_H */ diff -r ed72843a7194 -r e82eca4f8606 src/core/panic.c --- a/src/core/panic.c Mon Mar 30 13:34:42 2020 +0200 +++ b/src/core/panic.c Mon Mar 30 14:07:21 2020 +0200 @@ -17,30 +17,60 @@ */ #include +#include +#include #include "error.h" #include "panic.h" -void (*panic_handler)(void) = error_fatal; +static noreturn void +terminate(void) +{ + fprintf(stderr, "%s", error()); + exit(1); +} -void -panic(const char *fmt, ...) +void (*panic_handler)(void) = terminate; + +noreturn void +panicf(const char *fmt, ...) { assert(fmt); va_list ap; + /* + * Store the error before calling panic because va_end would not be + * called. + */ va_start(ap, fmt); - panicv(fmt, ap); + error_vprintf(fmt, ap); va_end(ap); + + panic(); } -void -panicv(const char *fmt, va_list ap) +noreturn void +vpanicf(const char *fmt, va_list ap) { assert(fmt); assert(panic_handler); error_vprintf(fmt, ap); + panic(); +} + +noreturn void +panic(void) +{ + assert(panic_handler); + panic_handler(); + + /* + * This should not happen, if it does it means the user did not fully + * satisfied the constraint of panic_handler. + */ + fprintf(stderr, "abort: panic handler returned"); + exit(1); } diff -r ed72843a7194 -r e82eca4f8606 src/core/panic.h --- a/src/core/panic.h Mon Mar 30 13:34:42 2020 +0200 +++ b/src/core/panic.h Mon Mar 30 14:07:21 2020 +0200 @@ -39,11 +39,19 @@ */ #include +#include + +#include "plat.h" /** * \brief Global panic handler. * - * The default implementation shows the last error and exit with code 1. + * The default implementation shows the last error and exit with code 1. The + * function must not return so you have to implement a setjmp/longjmp or a + * exception to be thrown. + * + * If the user defined function returns, panic routines will finally exit with + * code 1. */ extern void (*panic_handler)(void); @@ -56,17 +64,24 @@ * \pre fmt != NULL * \param fmt the printf(3) format string */ -void -panic(const char *fmt, ...); +noreturn void +panicf(const char *fmt, ...) PLAT_PRINTF(1, 2); /** - * Similar to \ref panic but with a va_list argument instead. + * Similar to \ref panicf but with a arguments pointer. * * \pre fmt != NULL * \param fmt the printf(3) format string * \param ap the arguments pointer */ -void -panicv(const char *fmt, va_list ap); +noreturn void +vpanicf(const char *fmt, va_list ap) PLAT_PRINTF(1, 0); + +/** + * Similar to \ref panicf but use last error stored using \ref error.h + * routines. + */ +noreturn void +panic(void); #endif /* !MOLKO_PANIC_H */ diff -r ed72843a7194 -r e82eca4f8606 src/core/plat.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/core/plat.h Mon Mar 30 14:07:21 2020 +0200 @@ -0,0 +1,49 @@ +/* + * plat.h -- non-portable platform specific code + * + * Copyright (c) 2020 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MOLKO_PLAT_H +#define MOLKO_PLAT_H + +/** + * \file plat.h + * \brief Non-portable platform specific code. + */ + +/* + * This block is used for doxygen documentation, the macros here are never + * exposed. + */ +#if defined(DOXYGEN) + +/** + * Printf specifier for function supporting the printf(3) syntax. This is + * currently only supported on GCC/Clang + */ +#define PLAT_PRINTF(p1, p2) + +#else + +#if defined(__GNUC__) +#define PLAT_PRINTF(p1, p2) __attribute__ ((format (printf, p1, p2))) +#else +#define PLAT_PRINTF(p1, p2) +#endif + +#endif /* !DOXYGEN */ + +#endif /* !MOLKO_PLAT_H */ diff -r ed72843a7194 -r e82eca4f8606 src/core/util.c --- a/src/core/util.c Mon Mar 30 13:34:42 2020 +0200 +++ b/src/core/util.c Mon Mar 30 14:07:21 2020 +0200 @@ -16,6 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include #include @@ -31,7 +32,7 @@ void *mem; if (!(mem = malloc(size))) - panic("%s\n", strerror(errno)); + panicf("%s", strerror(errno)); return mem; } @@ -42,7 +43,7 @@ void *mem; if (!(mem = calloc(n, size))) - panic("%s\n", strerror(errno)); + panicf("%s", strerror(errno)); return mem; } @@ -53,11 +54,53 @@ void *mem; if (!(mem = malloc(size))) - panic("%s\n", strerror(errno)); + panicf("%s", strerror(errno)); return memcpy(mem, ptr, size); } +char * +eprintf(const char *fmt, ...) +{ + assert(fmt); + + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = evprintf(fmt, ap); + va_end(ap); + + return ret; +} + +char * +evprintf(const char *fmt, va_list args) +{ + assert(fmt); + + va_list ap; + int size; + char *ret; + + /* Count number of bytes required. */ + va_copy(ap, args); + + if ((size = vsnprintf(NULL, 0, fmt, ap)) < 0) + panicf("%s", strerror(errno)); + + /* Do actual copy. */ + ret = emalloc(size + 1); + va_copy(ap, args); + + if (vsnprintf(ret, size, fmt, ap) != size) { + free(ret); + panicf("%s", strerror(errno)); + } + + return ret; +} + void delay(unsigned int ms) { diff -r ed72843a7194 -r e82eca4f8606 src/core/util.h --- a/src/core/util.h Mon Mar 30 13:34:42 2020 +0200 +++ b/src/core/util.h Mon Mar 30 14:07:21 2020 +0200 @@ -30,8 +30,11 @@ * for convenience. */ +#include #include +#include "plat.h" + /** * Get the number of elements in a static array. * @@ -74,6 +77,24 @@ ememdup(const void *ptr, size_t size); /** + * Create a dynamically allocated string in the printf(3) format string. + * + * \pre fmt != NULL + * \return The heap allocated string. + * \post Returned string will never be NULL. + */ +char * +eprintf(const char *fmt, ...) PLAT_PRINTF(1, 2); + +/** + * Similar to \ref eprintf with arguments pointer. + * + * \copydoc eprintf + */ +char * +evprintf(const char *fmt, va_list ap) PLAT_PRINTF(1, 0); + +/** * Put the thread to sleep for a given amount of milliseconds. * * \param ms the number of milliseconds to wait diff -r ed72843a7194 -r e82eca4f8606 tests/test-map.c --- a/tests/test-map.c Mon Mar 30 13:34:42 2020 +0200 +++ b/tests/test-map.c Mon Mar 30 14:07:21 2020 +0200 @@ -29,9 +29,9 @@ (void)data; if (!sys_init()) - error_fatal(); + panic(); if (!window_init("Test map", 100, 100)) - error_fatal(); + panic(); } static void diff -r ed72843a7194 -r e82eca4f8606 tests/test-panic.c --- a/tests/test-panic.c Mon Mar 30 13:34:42 2020 +0200 +++ b/tests/test-panic.c Mon Mar 30 14:07:21 2020 +0200 @@ -38,7 +38,7 @@ panic_handler = handler; handler_called = false; - panic("this is an error"); + panicf("this is an error"); GREATEST_ASSERT(handler_called); GREATEST_ASSERT_STR_EQ(error(), "this is an error"); GREATEST_PASS();