├── .gitignore ├── Makefile ├── main.c ├── include └── cresult.h └── src └── cresult.c /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall -Iinclude -Wextra -Wpedantic 3 | SRC = main.c src/cresult.c 4 | OUT = build/main 5 | 6 | all: 7 | $(CC) $(CFLAGS) $(SRC) -o $(OUT); ./build/main 8 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "cresult.h" 6 | 7 | typedef struct { 8 | char *name; 9 | } person; 10 | 11 | void person_destroy(person *p) { 12 | if (!p) return; 13 | if (p->name) { 14 | free(p->name); 15 | } 16 | free(p); 17 | } 18 | 19 | cresult_result person_create(char *name) { 20 | person *p = malloc(sizeof(person)); 21 | if (strlen(name) == 0) { 22 | return CRESULT_FAILURE("no empty names allowed"); 23 | } 24 | p->name = strdup(name); 25 | return cresult_result_success(p); 26 | } 27 | 28 | void cresult_test_failure() { 29 | cresult_result result = person_create(""); 30 | result = cresult_result_on_error_exit(&result); 31 | person *p = (person *)cresult_result_take(&result); 32 | printf("%s\n", p->name); 33 | person_destroy(p); 34 | } 35 | 36 | void cresult_test_success() { 37 | cresult_result result = person_create("bob"); 38 | result = cresult_result_on_error_exit(&result); 39 | person *p = (person *)cresult_result_take(&result); 40 | printf("%s\n", p->name); 41 | person_destroy(p); 42 | } 43 | 44 | void cresult_test_fallback() { 45 | cresult_result result = person_create(""); 46 | cresult_result fallback_result = person_create("anonymous"); 47 | result = cresult_result_on_error_fallback(&result, &fallback_result); 48 | person *p = (person *)cresult_result_take(&result); 49 | printf("%s\n", p->name); // prints "anonymous" 50 | person_destroy(p); 51 | } 52 | 53 | int main(void) { 54 | cresult_test_success(); 55 | cresult_test_fallback(); 56 | cresult_test_failure(); // must be last test ran 57 | return EXIT_SUCCESS; 58 | } 59 | -------------------------------------------------------------------------------- /include/cresult.h: -------------------------------------------------------------------------------- 1 | #ifndef CRESULT_H 2 | #define CRESULT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // Error type 9 | typedef struct { 10 | char *message; 11 | char *file_name; 12 | size_t line_number; 13 | } cresult_error; 14 | 15 | // Creates an error with file and line info 16 | cresult_error cresult_error_create(char *message, char *file_name, size_t line_number); 17 | 18 | #define CRESULT_ERROR(message) (cresult_error_create(message, __FILE__, __LINE__)) 19 | 20 | // Prints a cresult_error to stderr 21 | void cresult_error_print(cresult_error err); 22 | 23 | // Result type 24 | typedef struct { 25 | void *data; 26 | bool is_err; 27 | cresult_error err; 28 | bool is_spent; 29 | } cresult_result; 30 | 31 | // Constructs a success result 32 | cresult_result cresult_result_success(void *data); 33 | 34 | // Constructs a failure result 35 | cresult_result cresult_result_failure(cresult_error err); 36 | 37 | // Macro to generate a failed result 38 | #define CRESULT_FAILURE(message) (cresult_result_failure(cresult_error_create((message), __FILE__, __LINE__))) 39 | 40 | // Inspects the result: exits if error, returns result otherwise 41 | cresult_result cresult_result_on_error_exit(cresult_result *result); 42 | 43 | // Inspects the result: warns if error, returns result otherwise 44 | cresult_result cresult_result_on_error_warn(cresult_result *result); 45 | 46 | // Inspects the result: warns if error, returns result otherwise 47 | cresult_result cresult_result_on_error_fallback(cresult_result *result, cresult_result *fallback_result); 48 | 49 | // Transfers ownership from the result type to the caller 50 | void *cresult_result_take(cresult_result *result); 51 | 52 | #endif // CRESULT_H 53 | -------------------------------------------------------------------------------- /src/cresult.c: -------------------------------------------------------------------------------- 1 | #include "cresult.h" 2 | #include 3 | #include 4 | 5 | cresult_error cresult_error_create(char *message, char *file_name, size_t line_number) { 6 | cresult_error err; 7 | err.message = message; 8 | err.file_name = file_name; 9 | err.line_number = line_number; 10 | return err; 11 | } 12 | 13 | void cresult_error_print(cresult_error err) { 14 | printf("ERROR MESSAGE: %s\n", err.message); 15 | printf("FILE NAME: %s\n", err.file_name); 16 | printf("LINE NUMBER: %ld\n", err.line_number); 17 | } 18 | 19 | cresult_result cresult_result_success(void *data) { 20 | cresult_result result; 21 | result.data = data; 22 | result.is_err = false; 23 | result.is_spent = false; 24 | return result; 25 | } 26 | 27 | cresult_result cresult_result_failure(cresult_error err) { 28 | cresult_result result; 29 | result.data = NULL; 30 | result.is_err = true; 31 | result.is_spent = true; 32 | result.err = err; 33 | return result; 34 | } 35 | 36 | cresult_result cresult_result_on_error_exit(cresult_result *result) { 37 | if (result->is_err) { 38 | cresult_error_print(result->err); 39 | exit(EXIT_FAILURE); 40 | } 41 | return *result; 42 | } 43 | 44 | cresult_result cresult_result_on_error_warn(cresult_result *result) { 45 | if (result->is_err) { 46 | cresult_error_print(result->err); 47 | result->is_spent = true; 48 | } 49 | return *result; 50 | } 51 | 52 | cresult_result cresult_result_on_error_fallback(cresult_result *result, cresult_result *fallback_result) { 53 | if (result->is_err) { 54 | if (fallback_result->is_err) { 55 | printf("CRESULT ERROR: provided a fallback result which itself contains an error\n"); 56 | printf("here is the fallback result error:\n\n"); 57 | cresult_error_print(fallback_result->err); 58 | exit(EXIT_FAILURE); 59 | } 60 | return *fallback_result; 61 | } 62 | return *result; 63 | } 64 | 65 | void *cresult_result_take(cresult_result *result) { 66 | if (result->is_spent) { 67 | cresult_error err = CRESULT_ERROR("CRESULT ERROR: attempted to take from a spent cresult_result"); 68 | cresult_error_print(err); 69 | exit(EXIT_FAILURE); 70 | } 71 | result->is_spent = true; 72 | void *data = result->data; 73 | result->data = NULL; 74 | return data; 75 | } 76 | --------------------------------------------------------------------------------