#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 = ¤t[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 = ¤t[0]->next; } } #define TRY_HOF_REPLACE(old_ptr, new_ptr) cmaybe_replace_free_list(&cmaybe_allocations, old_ptr, new_ptr) #endif