tlibc/include/tlibc/errors.h

131 lines
4.0 KiB
C
Executable File

#pragma once
#include "std.h"
#include "string/str.h"
#include "collections/List.h"
#include "defer.h"
typedef struct ErrorCallPos {
i32 line;
cstr file;
cstr func;
} ErrorCallPos;
#define ErrorCallPos_here() (ErrorCallPos){\
.line = __LINE__,\
.file = __FILE__,\
.func = __func__\
}
typedef struct Error {
str msg;
bool is_msg_on_heap;
u16 error_code_page;
u32 error_code;
List(ErrorCallPos) call_stack;
} Error;
Error* Error_create(const char* msg, bool is_msg_on_heap, ErrorCallPos p,
u16 error_code_page, u32 error_code);
void Error_free(Error* e);
void Error_addCallPos(Error* e, ErrorCallPos p);
str Error_toStr(Error* e);
void Error_printAndExit(Error* e) ATTRIBUTE_NORETURN;
#define ErrorCodePage_name(name) ErrorCodePage_##name
#define ErrorCodePage_declare(name) extern u16 ErrorCodePage_name(name);
#define ErrorCodePage_define(name) u16 ErrorCodePage_name(name) = 0;
void _ErrorCodePage_register(u16* error_code_page_ptr);
#define ErrorCodePage_register(name) _ErrorCodePage_register(&ErrorCodePage_name(name));
typedef enum TlibcError {
TLIBC_ERROR_UNKNOWN
} TlibcError;
#define ErrorCodePage_NONE 0
ErrorCodePage_declare(TLIBC);
ErrorCodePage_declare(LIBC_ERRNO);
typedef struct Result_ {
Error* error;
union {
u64 u;
i64 i;
f32 f;
f64 d;
void* p;
};
} Result_;
///Use this macro only to specify function return type.
/// To declare variable, use ResultVar().
/// Warning can be suppressed by IGNORE_RESULT
#define Result(T) Result_ ATTRIBUTE_WARN_UNUSED_RESULT
#define ResultVar(T) Result_
///USAGE: IGNORE_RESULT trySomething();
#define IGNORE_RESULT Result_ __ignored_##__LINE__ ATTRIBUTE_UNUSED =
#define RESULT_ERROR_CODE(CODE_PAGE, CODE, MSG, IS_MSG_ON_HEAP) (Result_){ \
.error = Error_create(MSG, IS_MSG_ON_HEAP, ErrorCallPos_here(), \
ErrorCodePage_name(CODE_PAGE), CODE), \
.u = 0 \
}
#define RESULT_ERROR_CODE_FMT(CODE_PAGE, CODE, FORMAT, ARGS...) \
RESULT_ERROR_CODE(CODE_PAGE, CODE, sprintf_malloc(FORMAT ,##ARGS), true)
#define RESULT_ERROR(MSG, IS_MSG_ON_HEAP) \
RESULT_ERROR_CODE(NONE, 0, MSG, IS_MSG_ON_HEAP);
#define RESULT_ERROR_FMT(FORMAT, ARGS...) \
RESULT_ERROR_CODE_FMT(NONE, 0, FORMAT ,##ARGS)
#define RESULT_ERROR_ERRNO() \
RESULT_ERROR_CODE(LIBC_ERRNO, errno, strerror_malloc(errno), true)
#define RESULT_VOID (Result_){ .error = NULL, .u = 0 }
#define RESULT_VALUE(FIELD, V) (Result_){ .error = NULL, .FIELD = V }
#define _rname(N) __r_##N
#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(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_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)
#define try_stderrcode(CALL) do {\
int r = CALL;\
if(r != 0){\
Return RESULT_ERROR_CODE(LIBC_ERRNO, r, strerror_malloc(r), true);\
}\
} while(0)
#define try_assert(EXPR) if(!(EXPR)) { Return RESULT_ERROR(("try_assert(" #EXPR ")"), false); }