added defer macro
This commit is contained in:
parent
fe9e44a660
commit
d04aac567f
81
include/tlibc/defer.h
Normal file
81
include/tlibc/defer.h
Normal 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__ */
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user