cmaybe/cmaybe.h

98 lines
2.9 KiB
C

#ifndef CMAYBE_H
#define CMAYBE_H
#define MAYBE_TYPE(name, type) struct cmaybe_maybe_##name {type value; char is_value;}
#define MAYBE(name) struct cmaybe_maybe_##name
#define ENABLE_RETURN(name) MAYBE(name) cmaybe_return_value
#define RETURN_VALUE(x) do {\
cmaybe_return_value.is_value = 1;\
cmaybe_return_value.value = x;\
return cmaybe_return_value;\
} while(0)
#define RETURN_NOTHING() do {\
cmaybe_return_value.is_value = 0;\
return cmaybe_return_value;\
} while(0)
#define IS_VALUE(x) if ((x).is_value)
#define IS_NOT_VALUE(x) if (!(x).is_value)
#define VALUE(x) (x).value
struct cmaybe_free_list {
struct cmaybe_free_list *next;
void *ptr;
void (*freer)(void*);
};
#define ENABLE_TRY() \
struct cmaybe_free_list *cmaybe_allocations = NULL, *cmaybe_free_list_node;\
void *cmaybe_allocation;\
jmp_buf cmaybe_try_fail_jmp_buf;\
if (setjmp(cmaybe_try_fail_jmp_buf)) do {\
TRY_FAIL_HANDLE();\
RETURN_NOTHING();\
} while(0)
#define TRY_TYPE(name) MAYBE(name) cmaybe_try_tmp_##name
#define TRY(name, maybe_value) (\
cmaybe_try_tmp_##name = maybe_value,\
(cmaybe_try_tmp_##name.is_value ? 0 :\
longjmp(cmaybe_try_fail_jmp_buf, 1)\
),\
cmaybe_try_tmp_##name.value\
)
#define TRY_HOF_HANDLE(freer_func, pointer) (\
cmaybe_allocation = pointer,\
cmaybe_free_list_node = malloc(sizeof(struct cmaybe_free_list)),\
(cmaybe_free_list_node == NULL ? (\
free(cmaybe_allocation),\
longjmp(cmaybe_try_fail_jmp_buf, 1)\
) : (\
cmaybe_free_list_node->next = cmaybe_allocations,\
cmaybe_free_list_node->ptr = cmaybe_allocation,\
cmaybe_free_list_node->freer = freer_func,\
cmaybe_allocations = cmaybe_free_list_node\
)),\
cmaybe_allocation\
)
#define FREE_ON_TRY_FAIL(pointer) TRY_HOF_HANDLE(free, pointer)
// free() is passed as an argument to avoid errors in case the program uses
// no TRY functionality and doesn't include stdlib.h
static void cmaybe_remove_free_list(struct cmaybe_free_list **head, void *ptr, void (*free)(void*)) {
struct cmaybe_free_list **current = head;
while (*current != NULL) {
if (current[0]->ptr == ptr) {
struct cmaybe_free_list *deleted = *current;
*current = deleted->next;
free(deleted);
break;
}
current = &current[0]->next;
}
}
#define TRY_HOF_REMOVE(allocation) cmaybe_remove_free_list(&cmaybe_allocations, allocation, free)
#define TRY_FAIL_HANDLE() \
while (cmaybe_allocations != NULL) {\
struct cmaybe_free_list *current = cmaybe_allocations;\
void (*freer)(void*) = current->freer;\
freer(current->ptr);\
cmaybe_allocations = current->next;\
free(current);\
}
static void cmaybe_replace_free_list(struct cmaybe_free_list **head, void *from, void *to) {
struct cmaybe_free_list **current = head;
while (*current != NULL) {
if (current[0]->ptr == from) {
current[0]->ptr = to;
break;
}
current = &current[0]->next;
}
}
#define TRY_HOF_REPLACE(old_ptr, new_ptr) cmaybe_replace_free_list(&cmaybe_allocations, old_ptr, new_ptr)
#endif