From d04aac567f105cc566db7b9b8e201c1a04e4ecea Mon Sep 17 00:00:00 2001 From: Timerix Date: Sat, 9 Aug 2025 21:38:40 +0300 Subject: [PATCH] added defer macro --- include/tlibc/defer.h | 81 ++++++++++++++++++++++++++++++++++++++++++ include/tlibc/errors.h | 52 ++++++++++++++++++--------- src/filesystem/dir.c | 15 ++++---- 3 files changed, 126 insertions(+), 22 deletions(-) create mode 100644 include/tlibc/defer.h diff --git a/include/tlibc/defer.h b/include/tlibc/defer.h new file mode 100644 index 0000000..2c469e2 --- /dev/null +++ b/include/tlibc/defer.h @@ -0,0 +1,81 @@ +/* +Based on https://github.com/moon-chilled/Defer + +Usage: + +void foo(){ + Deferral(16); + void* p = malloc(8); + Defer(free(p)); + Return; +} + +*/ + +#pragma once + +#if defined(__GNUC__) || defined(__TINYC__) + +#define Deferral(MAX_DEFER_STATEMENTS) \ + unsigned char _num_deferrals = 0; \ + void *_defer_return_loc = 0, *_deferrals[MAX_DEFER_STATEMENTS] = {0}; + +# define Defer(block) _Defer(block, __LINE__) +# define Return _Return(__LINE__) + +#define _defer_token_cat(a, b) a ## b + +#define _Defer(block, n) do { \ + _deferrals[_num_deferrals++] = && _defer_token_cat(_defer_ini, n); \ + if (0) { \ + _defer_token_cat(_defer_ini, n): \ + block; \ + if (_num_deferrals) { \ + goto *_deferrals[--_num_deferrals]; \ + } else { \ + goto *_defer_return_loc; \ + } \ + } \ +} while (0) + +#define _Return(n) \ + if (_num_deferrals \ + /* this nonsense disables warning Wunused-but-set-variable */ \ + && _defer_return_loc == _defer_return_loc ) \ + { \ + _defer_return_loc = && _defer_token_cat(_defer_fini_, n); \ + goto *_deferrals[--_num_deferrals]; \ + } else _defer_token_cat(_defer_fini_, n): \ + return + +#else /* !__GNUC__ && !__TINYCC__ */ + +#include + +#ifdef _MSC_VER +# pragma message("You are using the unsafe longjmp()-based defer implementation. Expect bugs if you don't know what you're doing.") +#else +# warning You are using the unsafe longjmp()-based defer implementation. Expect bugs if you don't know what you're doing. +#endif + +#define Deferral(MAX_DEFER_STATEMENTS) \ +volatile unsigned char _num_deferrals = 0; \ +jmp_buf _defer_return_loc = {0}, _deferrals[MAX_DEFER_STATEMENTS] = {0}; + +#define Defer(block) do { \ + if (setjmp(_deferrals[_num_deferrals++])) { \ + block; \ + if (_num_deferrals) { \ + longjmp(_deferrals[--_num_deferrals], 1); \ + } else { \ + longjmp(_defer_return_loc, 1); \ + } \ + } \ +} while (0) + +#define Return \ + if (!setjmp(_defer_return_loc)) { \ + if (_num_deferrals) longjmp(_deferrals[--_num_deferrals], 1); \ + } else return + +#endif /* __GNUC__ */ diff --git a/include/tlibc/errors.h b/include/tlibc/errors.h index c796611..335ccab 100755 --- a/include/tlibc/errors.h +++ b/include/tlibc/errors.h @@ -2,6 +2,7 @@ #include "std.h" #include "string/str.h" #include "collections/List.h" +#include "defer.h" typedef struct ErrorCallPos { i32 line; @@ -46,22 +47,41 @@ typedef struct Result_ { #define RESULT_VOID (Result_){ .error = NULL, .u = 0 } #define RESULT_VALUE(FIELD, V) (Result_){ .error = NULL, .FIELD = V } -#define try(VAR, RSLT_CALL, DEFER_CODE) \ - Result_ VAR = RSLT_CALL;\ - if(VAR.error){\ - Error_addCallPos(VAR.error, ErrorCallPos_here());\ - DEFER_CODE;\ - return VAR;\ - }; +#define _rname(N) __r_##N -#define try_void(RSLT_CALL, DEFER_CODE) { try(__result_void, RSLT_CALL, DEFER_CODE) }; +#define try(VAR, RESULT_FIELD, RSLT_CALL) _try(VAR, RESULT_FIELD, RSLT_CALL, __LINE__) +#define try_void(RSLT_CALL) _try_void(RSLT_CALL, __LINE__) +#define try_fatal(VAR, RESULT_FIELD, RSLT_CALL) _try_fatal(VAR, RESULT_FIELD, RSLT_CALL, __LINE__) +#define try_fatal_void(RSLT_CALL) _try_fatal_void(RSLT_CALL, __LINE__) -#define try_fatal(VAR, RSLT_CALL, DEFER_CODE) \ - Result_ VAR = RSLT_CALL;\ - if(VAR.error){\ - Error_addCallPos(VAR.error, ErrorCallPos_here());\ - DEFER_CODE;\ - Error_printAndExit(VAR.error);\ - }; +#define _try(VAR, RESULT_FIELD, RSLT_CALL, N) \ + Result_ _rname(N) = RSLT_CALL;\ + if(_rname(N).error){\ + Error_addCallPos(_rname(N).error, ErrorCallPos_here());\ + Return _rname(N);\ + };\ + VAR = _rname(N).RESULT_FIELD; -#define try_fatal_void(RSLT_CALL, DEFER_CODE) { try_fatal(__result_void, RSLT_CALL, DEFER_CODE) }; +#define _try_void(RSLT_CALL, N) do {\ + Result_ _rname(N) = RSLT_CALL;\ + if(_rname(N).error){\ + Error_addCallPos(_rname(N).error, ErrorCallPos_here());\ + Return _rname(N);\ + };\ +} while(0) + +#define _try_fatal(VAR, RESULT_FIELD, RSLT_CALL, N) \ + Result_ _rname(N) = RSLT_CALL;\ + if(_rname(N).error){\ + Error_addCallPos(_rname(N).error, ErrorCallPos_here());\ + Error_printAndExit(_rname(N).error);\ + };\ + VAR = _rname(N).RESULT_FIELD; + +#define _try_fatal_void(RSLT_CALL, N) do {\ + Result_ _rname(N) = RSLT_CALL;\ + if(_rname(N).error){\ + Error_addCallPos(_rname(N).error, ErrorCallPos_here());\ + Error_printAndExit(_rname(N).error);\ + };\ +} while(0) diff --git a/src/filesystem/dir.c b/src/filesystem/dir.c index 1bbe334..06a0dcd 100644 --- a/src/filesystem/dir.c +++ b/src/filesystem/dir.c @@ -23,20 +23,23 @@ bool dir_exists(cstr path){ } Result(bool) dir_create(cstr path){ - if (dir_exists(path)) - return RESULT_VALUE(i, false); + Deferral(4); + if (dir_exists(path)){ + Return RESULT_VALUE(i, false); + } char* parentDir= str_copy(path_dirname(str_from_cstr((void*)path))).data; - try_void(dir_create(parentDir), free(parentDir)); - free(parentDir); + Defer(free(parentDir)); + try_void(dir_create(parentDir)); + #if TLIBC_FS_USE_WINDOWS_H if(!CreateDirectory(path, NULL)) #else if(mkdir(path, 0777) == -1) #endif { - return RESULT_ERROR_FMT("Can't create dicectory '%s': %s", path, strerror(errno)); + Return RESULT_ERROR_FMT("Can't create dicectory '%s': %s", path, strerror(errno)); } - return RESULT_VALUE(i, true); + Return RESULT_VALUE(i, true); }