added defer macro

This commit is contained in:
Timerix 2025-08-09 21:38:40 +03:00
parent fe9e44a660
commit d04aac567f
3 changed files with 126 additions and 22 deletions

81
include/tlibc/defer.h Normal file
View File

@ -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 <setjmp.h>
#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__ */

View File

@ -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)

View File

@ -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);
}