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 "std.h"
|
||||||
#include "string/str.h"
|
#include "string/str.h"
|
||||||
#include "collections/List.h"
|
#include "collections/List.h"
|
||||||
|
#include "defer.h"
|
||||||
|
|
||||||
typedef struct ErrorCallPos {
|
typedef struct ErrorCallPos {
|
||||||
i32 line;
|
i32 line;
|
||||||
@ -46,22 +47,41 @@ typedef struct Result_ {
|
|||||||
#define RESULT_VOID (Result_){ .error = NULL, .u = 0 }
|
#define RESULT_VOID (Result_){ .error = NULL, .u = 0 }
|
||||||
#define RESULT_VALUE(FIELD, V) (Result_){ .error = NULL, .FIELD = V }
|
#define RESULT_VALUE(FIELD, V) (Result_){ .error = NULL, .FIELD = V }
|
||||||
|
|
||||||
#define try(VAR, RSLT_CALL, DEFER_CODE) \
|
#define _rname(N) __r_##N
|
||||||
Result_ VAR = RSLT_CALL;\
|
|
||||||
if(VAR.error){\
|
|
||||||
Error_addCallPos(VAR.error, ErrorCallPos_here());\
|
|
||||||
DEFER_CODE;\
|
|
||||||
return VAR;\
|
|
||||||
};
|
|
||||||
|
|
||||||
#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) \
|
#define _try(VAR, RESULT_FIELD, RSLT_CALL, N) \
|
||||||
Result_ VAR = RSLT_CALL;\
|
Result_ _rname(N) = RSLT_CALL;\
|
||||||
if(VAR.error){\
|
if(_rname(N).error){\
|
||||||
Error_addCallPos(VAR.error, ErrorCallPos_here());\
|
Error_addCallPos(_rname(N).error, ErrorCallPos_here());\
|
||||||
DEFER_CODE;\
|
Return _rname(N);\
|
||||||
Error_printAndExit(VAR.error);\
|
};\
|
||||||
};
|
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){
|
Result(bool) dir_create(cstr path){
|
||||||
if (dir_exists(path))
|
Deferral(4);
|
||||||
return RESULT_VALUE(i, false);
|
if (dir_exists(path)){
|
||||||
|
Return RESULT_VALUE(i, false);
|
||||||
|
}
|
||||||
|
|
||||||
char* parentDir= str_copy(path_dirname(str_from_cstr((void*)path))).data;
|
char* parentDir= str_copy(path_dirname(str_from_cstr((void*)path))).data;
|
||||||
try_void(dir_create(parentDir), free(parentDir));
|
Defer(free(parentDir));
|
||||||
free(parentDir);
|
try_void(dir_create(parentDir));
|
||||||
|
|
||||||
#if TLIBC_FS_USE_WINDOWS_H
|
#if TLIBC_FS_USE_WINDOWS_H
|
||||||
if(!CreateDirectory(path, NULL))
|
if(!CreateDirectory(path, NULL))
|
||||||
#else
|
#else
|
||||||
if(mkdir(path, 0777) == -1)
|
if(mkdir(path, 0777) == -1)
|
||||||
#endif
|
#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