├── test ├── CMakeLists.txt └── basic.c ├── package.json ├── .gitignore ├── CMakeLists.txt ├── .clang-format ├── LICENSE ├── defer.c ├── README.md └── include ├── defer_macros.h └── defer.h /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(basic basic.c) 2 | target_link_libraries(basic defer) 3 | 4 | add_test(deferBasic basic) 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "libdefer", 3 | "version": "0.0.1", 4 | "repo": "trws/libdefer", 5 | "description": "go-style defer goodness for C", 6 | "keywords": ["defer", "goland", "convenience"], 7 | "license": "MIT", 8 | "src": ["defer.c", "include/defer.h", "include/defer_macros.h"] 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | *.su 34 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(defer) 4 | 5 | enable_testing() 6 | 7 | set(CMAKE_C_STANDARD 11) 8 | 9 | include_directories(include) 10 | 11 | add_library(defer defer.c) 12 | target_link_libraries(defer pthread) 13 | 14 | add_subdirectory(test) 15 | 16 | install(TARGETS defer 17 | LIBRARY DESTINATION lib 18 | ARCHIVE DESTINATION lib) 19 | install(DIRECTORY include/ 20 | DESTINATION include 21 | FILES_MATCHING PATTERN "*") 22 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle : google 2 | # SpaceBeforeParens : Always 3 | IndentWidth : 4 4 | BreakBeforeBraces : Attach 5 | UseTab: Never 6 | AllowShortIfStatementsOnASingleLine : false 7 | ConstructorInitializerAllOnOneLineOrOnePerLine : true 8 | AllowShortFunctionsOnASingleLine : false 9 | AllowShortLoopsOnASingleLine : false 10 | BinPackParameters : false 11 | AllowAllParametersOfDeclarationOnNextLine : false 12 | AlignTrailingComments : true 13 | ColumnLimit : 80 14 | PenaltyBreakBeforeFirstCallParameter : 100 15 | PenaltyReturnTypeOnItsOwnLine : 65000 16 | PenaltyBreakString : 10 17 | 18 | # These improve formatting results but require clang 3.6/7 or higher 19 | # BreakBeforeBinaryOperators : NonAssignment 20 | # AlignAfterOpenBracket: true 21 | # BinPackArguments : false 22 | # AlignOperands : true 23 | -------------------------------------------------------------------------------- /test/basic.c: -------------------------------------------------------------------------------- 1 | #ifdef NDEBUG 2 | #undef NDEBUG 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int ctr = 0; 11 | 12 | void add(void* i) { 13 | intptr_t n = (intptr_t) i; 14 | ctr+=n; 15 | } 16 | 17 | void add_one() { 18 | add((void*)1); 19 | } 20 | 21 | void check_order(void *n) { 22 | assert(ctr == (intptr_t)n); 23 | printf("order observed %ld\n", ctr - (intptr_t)n); 24 | } 25 | 26 | DEFER_SCOPED_VOID(t1, ()){ 27 | deferi(check_order, 1); 28 | defer_noarg(add_one); 29 | assert(ctr == 0); 30 | } 31 | 32 | DEFER_SCOPED_VOID(t2, (int, a)){ 33 | assert(ctr == 1); 34 | deferi(check_order, 1+a); 35 | deferi(add, a); 36 | assert(ctr == 1); 37 | } 38 | 39 | int main(int argc, char *argv[]) 40 | { 41 | t1(); 42 | t2(5); 43 | assert(ctr == 6); 44 | 45 | return ctr - 6; 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Thomas Richard William Scogland 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /defer.c: -------------------------------------------------------------------------------- 1 | #include "defer.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct defer { 11 | struct defer* next; 12 | union { 13 | deferable_free_like fn; 14 | deferable_noarg noarg; 15 | }; 16 | void* data; 17 | bool arg; 18 | }; 19 | 20 | static pthread_once_t dss_init_once = PTHREAD_ONCE_INIT; 21 | 22 | #if __STDC_VERSION__ >= 201112L 23 | 24 | #define USE_THREAD_LOCAL 1 25 | 26 | _Thread_local defer_scope_t* defer_scope_stack = 0; 27 | #define get_dss() (defer_scope_stack) 28 | #define set_dss(NEW) (defer_scope_stack = (NEW)) 29 | 30 | #else //__STDC_VERSION__ >= 201112L 31 | 32 | static pthread_key_t defer_scope_stack; 33 | static inline defer_scope_t* get_dss() { 34 | return (defer_scope_t*)pthread_getspecific(defer_scope_stack); 35 | } 36 | static inline void set_dss(defer_scope_t* new) { 37 | pthread_setspecific(defer_scope_stack, new); 38 | } 39 | 40 | #endif //__STDC_VERSION__ >= 201112L 41 | 42 | static void execute_all_deferred(void* dscope /* defer_scope_t * */); 43 | 44 | static void final_cleanup(void); 45 | 46 | static void init_dss(void) { 47 | #if !USE_THREAD_LOCAL 48 | pthread_key_create(&defer_scope_stack, execute_all_deferred); 49 | pthread_setspecific(defer_scope_stack, 0); 50 | #endif 51 | 52 | atexit(final_cleanup); 53 | } 54 | 55 | static inline void execute_deferred(defer_t* d) { 56 | while (d) { 57 | if (!d->fn) { 58 | fprintf(stderr, "Invalid defer encountered, aborting\n"); 59 | abort(); 60 | } 61 | if (d->arg) { 62 | d->fn(d->data); 63 | } else { 64 | d->noarg(); 65 | } 66 | defer_t* tmp = d; 67 | d = d->next; 68 | free(tmp); 69 | } 70 | } 71 | 72 | defer_scope_t* defer_scope_new(void) { 73 | defer_scope_t* ds = (defer_scope_t*)malloc(sizeof *ds); 74 | ds->parent = NULL; 75 | ds->routines = NULL; 76 | return ds; 77 | } 78 | 79 | defer_scope_t* defer_scope_push(defer_scope_t* ds) { 80 | pthread_once(&dss_init_once, init_dss); 81 | if (!ds) 82 | ds = defer_scope_new(); 83 | defer_scope_t* top = get_dss(); 84 | ds->parent = top; 85 | if (top == NULL) { 86 | // Starting from the bottom register thread cleanup on fork 87 | pthread_atfork(NULL, NULL, final_cleanup); 88 | } 89 | set_dss(ds); 90 | return ds; 91 | } 92 | 93 | void defer_scope_pop(defer_scope_t* ds) { 94 | defer_scope_t* top = get_dss(); 95 | assert(top); 96 | defer_scope_t* until = 97 | ds ? (ds == (defer_scope_t*)1 ? NULL : ds->parent) : top->parent; 98 | while (top != until && top != NULL) { 99 | execute_deferred(top->routines); 100 | defer_scope_t* tmp = top->parent; 101 | free(top); 102 | top = tmp; 103 | } 104 | set_dss(top); 105 | } 106 | 107 | defer_scope_t* defer_scope_begin(void) { 108 | return defer_scope_push(NULL); 109 | } 110 | 111 | void defer_scope_end(void) { 112 | defer_scope_pop(NULL); 113 | } 114 | 115 | #ifdef __GNUC__ 116 | #define INIT(X) void X(void) __attribute__((constructor)) 117 | #else 118 | #warning You must manually create a scope before deferring a value 119 | #endif 120 | INIT(defer_scope_auto_init); 121 | 122 | void defer_scope_auto_init(void) { 123 | defer_scope_begin(); 124 | } 125 | 126 | static void execute_all_deferred(void* dscope /* defer_scope_t * */) { 127 | defer_scope_pop((defer_scope_t*)dscope); 128 | } 129 | 130 | static void final_cleanup(void) { 131 | defer_scope_pop((defer_scope_t*)1); 132 | } 133 | 134 | static void defer_append(defer_scope_t* s, defer_t* d) { 135 | d->next = s->routines; 136 | s->routines = d; 137 | } 138 | 139 | void defer_specific(defer_scope_t* ds, deferable_free_like fn, void* p) { 140 | assert(fn); 141 | defer_t* nd = (defer_t*)malloc(sizeof *nd); 142 | *nd = (defer_t){.arg = 1, .fn = fn, .data = p}; 143 | defer_append(ds, nd); 144 | } 145 | 146 | void defer_specific_noarg(defer_scope_t* ds, deferable_noarg fn) { 147 | assert(fn); 148 | defer_t* nd = (defer_t*)malloc(sizeof *nd); 149 | nd->arg = 0; 150 | nd->noarg = fn; 151 | nd->data = NULL; 152 | defer_append(ds, nd); 153 | } 154 | 155 | void defer(deferable_free_like fn, void* p) { 156 | defer_specific(get_dss(), fn, p); 157 | } 158 | 159 | void defer_noarg(deferable_noarg fn) { 160 | defer_specific_noarg(get_dss(), fn); 161 | } 162 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libdefer: Go-style defer for C 2 | 3 | libdefer replicates much of the functionality of go's `defer` in C99. When 4 | linked in, on supporting platforms, a defer context is automatically created 5 | before main is called, and cleaned up after exit or return from main. Scopes 6 | are thread-local, and completely under user control, so you can make the scopes 7 | as small or large as you like (even unstructured or un-nested!) 8 | 9 | ## Build and Install 10 | 11 | ```bash 12 | $ mkdir build 13 | $ cd build 14 | $ cmake .. 15 | $ make install 16 | ``` 17 | 18 | ## Example 19 | 20 | One of golang's most iconic features is the `defer` keyword. Lacking scoped 21 | destruction, it's one of the best ways to clean up resources in the face of 22 | panics and without having to worry about multiple returns. A canonical example 23 | is closing a pair of files in the face of error handling: 24 | 25 | ```golang 26 | // Example courtesy of https://blog.golang.org/defer-panic-and-recover 27 | func CopyFile(dstName, srcName string) (written int64, err error) { 28 | src, err := os.Open(srcName) 29 | if err != nil { 30 | return 31 | } 32 | defer src.Close() 33 | 34 | dst, err := os.Create(dstName) 35 | if err != nil { 36 | return 37 | } 38 | defer dst.Close() 39 | 40 | return io.Copy(dst, src) 41 | } 42 | ``` 43 | 44 | Doing this in C is normally a matter of replicating cleanup code or use of the dreaded `goto`. For example, a similar C function might be: 45 | 46 | ```c 47 | int64_t copy_file(const char *dst_name, const char *src_name) { 48 | FILE *dst = fopen(dst_name, "w"); 49 | if (!dst) { 50 | return -1; 51 | } 52 | FILE *src = fopen(src_name, "r"); 53 | if (!src) { 54 | fclose(dst); // cleanup #1 55 | return -1; 56 | } 57 | 58 | int64_t ret = copy_between_streams(dst, src); 59 | 60 | // cleanup #2 61 | fclose(dst); 62 | fclose(src); 63 | return ret; 64 | } 65 | ``` 66 | 67 | The replicated cleanup is small, but it's also quite easy to miss. If the copy_between_streams code is inlined and adds one or more returns it gets yet more complicated. This is what you can do with libdefer: 68 | 69 | ```c 70 | #include 71 | int64_t copy_file(const char *dst_name, const char *src_name) { 72 | defer_scope_begin(); 73 | FILE *dst = fopen(dst_name, "w"); 74 | if (!dst) { 75 | DEFER_POP1_RETURN(-1); 76 | } 77 | defer(fclose, dst); 78 | FILE *src = fopen(src_name, "r"); 79 | if (!src) { 80 | DEFER_POP1_RETURN(-1); 81 | } 82 | defer(fclose, src); 83 | 84 | DEFER_POP1_RETURN(copy_between_streams(dst, src)); 85 | } 86 | ``` 87 | 88 | Now, the close is closer to the open, and handled regardless of exit. That 89 | said, we still have the issue of easy mistakes. Every return has been replaced 90 | with a cleanup macro, this is not really what we're after. This is why 91 | libdefer also provides function definition wrapper macros: 92 | 93 | ```c 94 | #include 95 | DEFER_SCOPED(int64_t, copy_file, (const char *, dst_name, const char, *src_name)) { 96 | FILE *dst = fopen(dst_name, "w"); 97 | if (!dst) { 98 | return -1; 99 | } 100 | defer(fclose, dst); 101 | FILE *src = fopen(src_name, "r"); 102 | if (!src) { 103 | return -1; 104 | } 105 | defer(fclose, src); 106 | 107 | return copy_between_streams(dst, src); 108 | } 109 | ``` 110 | 111 | The definition line is a little longer, but the body is now completely 112 | surrounded by a guaranteed defer block that actually does a little more work 113 | than the one in the previous version. With a decent compiler, the extra static 114 | inline function generated here will optimize away, but either way the function 115 | gets its cleanup without visible handling in the body. Also, the signature of 116 | the function *has not changed*, the prototype in headers can remain the same, 117 | just the definition needs the macro treatment. 118 | 119 | ## Licensing 120 | 121 | libdefer is released under a permissive MIT license. Essentially, you may do 122 | with it what you will, but include the copyright, and if it breaks something of 123 | yours into two pieces, you're welcome to keep both pieces. See the LICENSE 124 | file for more details. 125 | 126 | ## Limitations 127 | 128 | * A scope is not auto-created with new threads, the easiest way to deal with 129 | this is to wrap the entry function in a `DEFER_SCOPED` macro 130 | * Only functions with no arguments or a single pointer sized argument can be 131 | deferred at this time, if you have a good reason to defer functions that don't 132 | look like `free`, feel free to post an issue 133 | * The library is currently un-optimized, and is not cost free. Each defer 134 | entails a `malloc` and a `free` on cleanup, this will likely be fixed, but be 135 | aware 136 | * Use of `setjmp` and `longjmp` for ad-hoc exceptions is actually supported, 137 | but does not execute all defers during unwinding like C++ would, rather all of 138 | the scope creation functions return a handle for that scope, allowing the code 139 | around the setjmp to get a handle that can clean up all scopes below that 140 | context. Using the handles is a good way to protect against unmatched push/pop 141 | pairs in inner scopes as well. 142 | -------------------------------------------------------------------------------- /include/defer_macros.h: -------------------------------------------------------------------------------- 1 | #ifndef __FU_MACROS_H 2 | #define __FU_MACROS_H 3 | 4 | #define xstr(X) #X 5 | #define STRINGIFY(X) xstr (X) 6 | 7 | // Check if macro __VA_ARGS__ is empty 8 | #define _ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15 9 | #define HAS_COMMA(...) _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) 10 | #define _TRIGGER_PARENTHESIS_(...) , 11 | 12 | /* IFNEMPTY courtesy of 13 | * https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/ 14 | */ 15 | #define IFNEMPTY(...) \ 16 | _IFNEMPTY( \ 17 | /* test if there is just one argument, eventually an empty \ 18 | one */ \ 19 | HAS_COMMA(__VA_ARGS__), \ 20 | /* test if _TRIGGER_PARENTHESIS_ together with the argument \ 21 | adds a comma */ \ 22 | HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__), \ 23 | /* test if the argument together with a parenthesis \ 24 | adds a comma */ \ 25 | HAS_COMMA(__VA_ARGS__ (/*empty*/)), \ 26 | /* test if placing it between _TRIGGER_PARENTHESIS_ and the \ 27 | parenthesis adds a comma */ \ 28 | HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/)) \ 29 | ) 30 | #define BLANK_COMMA(...) _ARG16(__VA_ARGS__, , , , , , , , , , , , , , , ()) 31 | 32 | #define PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4 33 | #define _IFNEMPTY(_0, _1, _2, _3) PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3) 34 | #define _IS_EMPTY_CASE_0000(X) X 35 | #define _IS_EMPTY_CASE_0001(X) 36 | #define _IS_EMPTY_CASE_0010(X) X 37 | #define _IS_EMPTY_CASE_0011(X) X 38 | #define _IS_EMPTY_CASE_0100(X) X 39 | #define _IS_EMPTY_CASE_0101(X) X 40 | #define _IS_EMPTY_CASE_1000(X) X 41 | #define _IS_EMPTY_CASE_1001(X) X 42 | #define _IS_EMPTY_CASE_1010(X) X 43 | #define _IS_EMPTY_CASE_1011(X) X 44 | #define _IS_EMPTY_CASE_1100(X) X 45 | #define _IS_EMPTY_CASE_1101(X) X 46 | #define _IS_EMPTY_CASE_1110(X) X 47 | #define _IS_EMPTY_CASE_1111(X) X 48 | // end check 49 | 50 | // Make a FOREACH macro 51 | #define FE_1(WHAT, X) WHAT(X) 52 | #define FE_2(WHAT, X, ...) WHAT(X)FE_1(WHAT, __VA_ARGS__) 53 | #define FE_3(WHAT, X, ...) WHAT(X)FE_2(WHAT, __VA_ARGS__) 54 | #define FE_4(WHAT, X, ...) WHAT(X)FE_3(WHAT, __VA_ARGS__) 55 | #define FE_5(WHAT, X, ...) WHAT(X)FE_4(WHAT, __VA_ARGS__) 56 | #define FE_6(WHAT, X, ...) WHAT(X)FE_5(WHAT, __VA_ARGS__) 57 | #define FE_7(WHAT, X, ...) WHAT(X)FE_6(WHAT, __VA_ARGS__) 58 | #define FE_8(WHAT, X, ...) WHAT(X)FE_7(WHAT, __VA_ARGS__) 59 | #define FE_9(WHAT, X, ...) WHAT(X)FE_8(WHAT, __VA_ARGS__) 60 | #define FE_10(WHAT, X, ...) WHAT(X)FE_9(WHAT, __VA_ARGS__) 61 | #define FE_11(WHAT, X, ...) WHAT(X)FE_10(WHAT, __VA_ARGS__) 62 | #define FE_12(WHAT, X, ...) WHAT(X)FE_11(WHAT, __VA_ARGS__) 63 | #define FE_13(WHAT, X, ...) WHAT(X)FE_12(WHAT, __VA_ARGS__) 64 | #define FE_14(WHAT, X, ...) WHAT(X)FE_13(WHAT, __VA_ARGS__) 65 | #define FE_15(WHAT, X, ...) WHAT(X)FE_14(WHAT, __VA_ARGS__) 66 | #define FE_16(WHAT, X, ...) WHAT(X)FE_15(WHAT, __VA_ARGS__) 67 | #define FE_17(WHAT, X, ...) WHAT(X)FE_16(WHAT, __VA_ARGS__) 68 | #define FE_18(WHAT, X, ...) WHAT(X)FE_17(WHAT, __VA_ARGS__) 69 | #define FE_19(WHAT, X, ...) WHAT(X)FE_18(WHAT, __VA_ARGS__) 70 | #define FE_20(WHAT, X, ...) WHAT(X)FE_19(WHAT, __VA_ARGS__) 71 | //... repeat as needed 72 | 73 | // Make an ENUMERATE macro 74 | #define EN_1(WHAT, X) WHAT(0, X) 75 | #define EN_2(WHAT, X, ...) WHAT(1, X)EN_1(WHAT, __VA_ARGS__) 76 | #define EN_3(WHAT, X, ...) WHAT(2, X)EN_2(WHAT, __VA_ARGS__) 77 | #define EN_4(WHAT, X, ...) WHAT(3, X)EN_3(WHAT, __VA_ARGS__) 78 | #define EN_5(WHAT, X, ...) WHAT(4, X)EN_4(WHAT, __VA_ARGS__) 79 | #define EN_6(WHAT, X, ...) WHAT(5, X)EN_5(WHAT, __VA_ARGS__) 80 | #define EN_7(WHAT, X, ...) WHAT(6, X)EN_6(WHAT, __VA_ARGS__) 81 | #define EN_8(WHAT, X, ...) WHAT(7, X)EN_7(WHAT, __VA_ARGS__) 82 | #define EN_9(WHAT, X, ...) WHAT(8, X)EN_8(WHAT, __VA_ARGS__) 83 | #define EN_10(WHAT, X, ...) WHAT(9, X)EN_9(WHAT, __VA_ARGS__) 84 | #define EN_11(WHAT, X, ...) WHAT(10, X)EN_10(WHAT, __VA_ARGS__) 85 | #define EN_12(WHAT, X, ...) WHAT(11, X)EN_11(WHAT, __VA_ARGS__) 86 | #define EN_13(WHAT, X, ...) WHAT(12, X)EN_12(WHAT, __VA_ARGS__) 87 | #define EN_14(WHAT, X, ...) WHAT(13, X)EN_13(WHAT, __VA_ARGS__) 88 | #define EN_15(WHAT, X, ...) WHAT(14, X)EN_14(WHAT, __VA_ARGS__) 89 | #define EN_16(WHAT, X, ...) WHAT(15, X)EN_15(WHAT, __VA_ARGS__) 90 | #define EN_17(WHAT, X, ...) WHAT(16, X)EN_16(WHAT, __VA_ARGS__) 91 | #define EN_18(WHAT, X, ...) WHAT(17, X)EN_17(WHAT, __VA_ARGS__) 92 | #define EN_19(WHAT, X, ...) WHAT(18, X)EN_18(WHAT, __VA_ARGS__) 93 | #define EN_20(WHAT, X, ...) WHAT(19, X)EN_19(WHAT, __VA_ARGS__) 94 | //... repeat as needed 95 | 96 | // Make a for pairs macro 97 | #define FP_2(WHAT, X, Y) WHAT(X, Y) 98 | #define FP_4(WHAT, X, Y, ...) WHAT(X, Y)FP_2(WHAT, __VA_ARGS__) 99 | #define FP_6(WHAT, X, Y, ...) WHAT(X, Y)FP_4(WHAT, __VA_ARGS__) 100 | #define FP_8(WHAT, X, Y, ...) WHAT(X, Y)FP_6(WHAT, __VA_ARGS__) 101 | #define FP_10(WHAT, X, Y, ...) WHAT(X, Y)FP_8(WHAT, __VA_ARGS__) 102 | #define FP_12(WHAT, X, Y, ...) WHAT(X, Y)FP_10(WHAT, __VA_ARGS__) 103 | #define FP_14(WHAT, X, Y, ...) WHAT(X, Y)FP_12(WHAT, __VA_ARGS__) 104 | #define FP_16(WHAT, X, Y, ...) WHAT(X, Y)FP_14(WHAT, __VA_ARGS__) 105 | #define FP_18(WHAT, X, Y, ...) WHAT(X, Y)FP_16(WHAT, __VA_ARGS__) 106 | #define FP_20(WHAT, X, Y, ...) WHAT(X, Y)FP_18(WHAT, __VA_ARGS__) 107 | #define FP_22(WHAT, X, Y, ...) WHAT(X, Y)FP_20(WHAT, __VA_ARGS__) 108 | #define FP_24(WHAT, X, Y, ...) WHAT(X, Y)FP_22(WHAT, __VA_ARGS__) 109 | #define FP_26(WHAT, X, Y, ...) WHAT(X, Y)FP_24(WHAT, __VA_ARGS__) 110 | #define FP_28(WHAT, X, Y, ...) WHAT(X, Y)FP_26(WHAT, __VA_ARGS__) 111 | #define FP_30(WHAT, X, Y, ...) WHAT(X, Y)FP_28(WHAT, __VA_ARGS__) 112 | #define FP_32(WHAT, X, Y, ...) WHAT(X, Y)FP_30(WHAT, __VA_ARGS__) 113 | #define FP_34(WHAT, X, Y, ...) WHAT(X, Y)FP_32(WHAT, __VA_ARGS__) 114 | #define FP_36(WHAT, X, Y, ...) WHAT(X, Y)FP_34(WHAT, __VA_ARGS__) 115 | #define FP_38(WHAT, X, Y, ...) WHAT(X, Y)FP_36(WHAT, __VA_ARGS__) 116 | #define FP_40(WHAT, X, Y, ...) WHAT(X, Y)FP_38(WHAT, __VA_ARGS__) 117 | #define FP_42(WHAT, X, Y, ...) WHAT(X, Y)FP_40(WHAT, __VA_ARGS__) 118 | #define FP_44(WHAT, X, Y, ...) WHAT(X, Y)FP_42(WHAT, __VA_ARGS__) 119 | #define FP_46(WHAT, X, Y, ...) WHAT(X, Y)FP_44(WHAT, __VA_ARGS__) 120 | #define FP_48(WHAT, X, Y, ...) WHAT(X, Y)FP_46(WHAT, __VA_ARGS__) 121 | #define FP_50(WHAT, X, Y, ...) WHAT(X, Y)FP_48(WHAT, __VA_ARGS__) 122 | #define FP_52(WHAT, X, Y, ...) WHAT(X, Y)FP_50(WHAT, __VA_ARGS__) 123 | #define FP_54(WHAT, X, Y, ...) WHAT(X, Y)FP_52(WHAT, __VA_ARGS__) 124 | #define FP_56(WHAT, X, Y, ...) WHAT(X, Y)FP_54(WHAT, __VA_ARGS__) 125 | #define FP_58(WHAT, X, Y, ...) WHAT(X, Y)FP_56(WHAT, __VA_ARGS__) 126 | #define FP_60(WHAT, X, Y, ...) WHAT(X, Y)FP_58(WHAT, __VA_ARGS__) 127 | #define FP_62(WHAT, X, Y, ...) WHAT(X, Y)FP_60(WHAT, __VA_ARGS__) 128 | #define FP_64(WHAT, X, Y, ...) WHAT(X, Y)FP_62(WHAT, __VA_ARGS__) 129 | #define FP_66(WHAT, X, Y, ...) WHAT(X, Y)FP_64(WHAT, __VA_ARGS__) 130 | #define FP_68(WHAT, X, Y, ...) WHAT(X, Y)FP_66(WHAT, __VA_ARGS__) 131 | #define FP_70(WHAT, X, Y, ...) WHAT(X, Y)FP_68(WHAT, __VA_ARGS__) 132 | #define FP_72(WHAT, X, Y, ...) WHAT(X, Y)FP_70(WHAT, __VA_ARGS__) 133 | #define FP_74(WHAT, X, Y, ...) WHAT(X, Y)FP_72(WHAT, __VA_ARGS__) 134 | #define FP_76(WHAT, X, Y, ...) WHAT(X, Y)FP_74(WHAT, __VA_ARGS__) 135 | #define FP_78(WHAT, X, Y, ...) WHAT(X, Y)FP_76(WHAT, __VA_ARGS__) 136 | #define FP_80(WHAT, X, Y, ...) WHAT(X, Y)FP_78(WHAT, __VA_ARGS__) 137 | #define FP_82(WHAT, X, Y, ...) WHAT(X, Y)FP_80(WHAT, __VA_ARGS__) 138 | #define FP_84(WHAT, X, Y, ...) WHAT(X, Y)FP_82(WHAT, __VA_ARGS__) 139 | #define FP_86(WHAT, X, Y, ...) WHAT(X, Y)FP_84(WHAT, __VA_ARGS__) 140 | #define FP_88(WHAT, X, Y, ...) WHAT(X, Y)FP_86(WHAT, __VA_ARGS__) 141 | //... repeat as needed 142 | 143 | #define GET_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME 144 | #define FOR_EACH(action,...) \ 145 | IFNEMPTY(__VA_ARGS__)(GET_MACRO(__VA_ARGS__,FE_20,FE_19,FE_18,FE_17,FE_16,FE_15,FE_14,FE_13,FE_12,FE_11,FE_10,FE_9,FE_8,FE_7,FE_6,FE_5,FE_4,FE_3,FE_2,FE_1,)(action,__VA_ARGS__)) 146 | 147 | #define ENUMERATE(action,...) \ 148 | IFNEMPTY(__VA_ARGS__)(GET_MACRO(__VA_ARGS__,EN_20,EN_19,EN_18,EN_17,EN_16,EN_15,EN_14,EN_13,EN_12,EN_11,EN_10,EN_9,EN_8,EN_7,EN_6,EN_5,EN_4,EN_3,EN_2,EN_1,)(action,__VA_ARGS__)) 149 | 150 | #define FOR_PAIRS(action,...) \ 151 | IFNEMPTY(__VA_ARGS__)(GET_MACRO(__VA_ARGS__,FP_20,FP_19,FP_18,FP_17,FP_16,FP_15,FP_14,FP_13,FP_12,FP_11,FP_10,FP_9,FP_8,FP_7,FP_6,FP_5,FP_4,FP_3,FP_2,FP_1,)(action,__VA_ARGS__)) 152 | 153 | // ENUMERATE TARGETS, use for function argument generation from type list 154 | #define AS_ARG_NAMES(T, N) , N 155 | 156 | #define AS_ARGS(X, Y) , X Y 157 | 158 | #define AS_TYPES(T, N) , T 159 | 160 | #endif /* __FU_MACROS_H */ 161 | -------------------------------------------------------------------------------- /include/defer.h: -------------------------------------------------------------------------------- 1 | #ifndef __DEFER_H 2 | #define __DEFER_H 1 3 | #include "defer_macros.h" 4 | 5 | #include 6 | 7 | typedef struct defer defer_t; 8 | typedef struct defer_scope { 9 | struct defer_scope* parent; 10 | defer_t* routines; 11 | } defer_scope_t; 12 | 13 | typedef void (*deferable_free_like)(void*); 14 | typedef void (*deferable_noarg)(void); 15 | 16 | /** 17 | * @brief Begin a new defer scope for this thread here 18 | * 19 | * @return A handle to the new scope or NULL 20 | */ 21 | defer_scope_t* defer_scope_begin(void); 22 | 23 | /** 24 | * @brief Execute the deferred functions in the current defer scope and pop it 25 | * off the stack 26 | */ 27 | void defer_scope_end(void); 28 | 29 | /** 30 | * @brief Create a wrapper function that creates a defer scope around the 31 | * function being defined, only use for void return functions. 32 | * 33 | * @param NAME function name to define 34 | * @param ARG_LIST argument list, types and names, all separated by commas, 35 | * see example 36 | * 37 | * The main advantage to using one of the wrapper macros to define 38 | * defer-scoped functions is that however you leave the function so wrapped, 39 | * the defers will run. No calls or return macros are required inside the 40 | * function when it is defined this way. The only exception to this is using 41 | * setjmp/longjmp, that must be handled by use of pop to clean up all scopes 42 | * back to a known point around the setjmp point. 43 | * 44 | * Use like this: 45 | * DEFER_SCOPED_VOID(test_fn, (int, a, char *, b)) { 46 | * defer(free, b); 47 | * } 48 | * 49 | * int main(int argc, char *argv[]){ 50 | * test_fn(5, strdup("string")); 51 | * return 0; 52 | * } 53 | */ 54 | #define DEFER_SCOPED_VOID(NAME, ARG_LIST) \ 55 | static inline void __defer_inner##NAME __DEFER_ARGS ARG_LIST; \ 56 | void NAME __DEFER_ARGS ARG_LIST { \ 57 | defer_scope_t* ds = defer_scope_begin(); \ 58 | __defer_inner##NAME __DEFER_CALL ARG_LIST; \ 59 | defer_scope_pop(ds); \ 60 | } \ 61 | static inline void __defer_inner##NAME __DEFER_ARGS ARG_LIST 62 | 63 | /** 64 | * @brief Create a wrapper function that creates a defer scope around the 65 | * function being defined, only use for non-void return functions. 66 | * 67 | * @param RET return type of the function to be defined 68 | * @param NAME function name to define 69 | * @param ARG_LIST argument list, types and names, all separated by commas, 70 | * see example 71 | * 72 | */ 73 | #define DEFER_SCOPED(RET, NAME, ARG_LIST) \ 74 | static inline RET __defer_inner##NAME __DEFER_ARGS ARG_LIST; \ 75 | RET NAME __DEFER_ARGS ARG_LIST { \ 76 | defer_scope_t* ds = defer_scope_begin(); \ 77 | RET r = __defer_inner##NAME __DEFER_CALL ARG_LIST; \ 78 | defer_scope_pop(ds); \ 79 | return r; \ 80 | } \ 81 | static inline RET __defer_inner##NAME __DEFER_ARGS ARG_LIST 82 | 83 | /** 84 | * @brief DEFER_SCOPED using stack memory for the defer_scope structure, this 85 | * saves some allocation overhead, but is only safe when longjmp and similar 86 | * are impossible in the contained function 87 | * 88 | * @param RET return type of the function to be defined 89 | * @param NAME function name to define 90 | * @param ARG_LIST argument list, types and names, all separated by commas, 91 | * see example 92 | * 93 | * @return function's return value 94 | */ 95 | #define DSNE(RET, NAME, ARG_LIST) \ 96 | static inline RET __defer_inner##NAME __DEFER_ARGS ARG_LIST; \ 97 | RET NAME __DEFER_ARGS ARG_LIST { \ 98 | defer_scope_t ds[1]; \ 99 | defer_scope_push(ds); \ 100 | RET r = __defer_inner##NAME __DEFER_CALL ARG_LIST; \ 101 | defer_scope_pop(ds); \ 102 | return r; \ 103 | } \ 104 | static inline RET __defer_inner##NAME __DEFER_ARGS ARG_LIST 105 | 106 | #define DSNEV(NAME, ARG_LIST) \ 107 | static inline void __defer_inner##NAME __DEFER_ARGS ARG_LIST; \ 108 | void NAME __DEFER_ARGS ARG_LIST { \ 109 | defer_scope_t ds[1]; \ 110 | defer_scope_push(ds); \ 111 | __defer_inner##NAME __DEFER_CALL ARG_LIST; \ 112 | defer_scope_pop(ds); \ 113 | } \ 114 | static inline void __defer_inner##NAME __DEFER_ARGS ARG_LIST 115 | 116 | /** 117 | * @brief Convenience macro to pop the scope and return 118 | * 119 | * @param DS defer scope to pop to 120 | * @param RET expression to return from the current function 121 | * 122 | * @return 123 | */ 124 | #define DEFER_POP_RETURN(DS, RET) \ 125 | do { \ 126 | defer_scope_pop(DS); \ 127 | return (RET); \ 128 | } while (0) 129 | 130 | /** 131 | * @brief Convenience macro to end the current scope and return 132 | * 133 | * @param RET expression to return from the current function 134 | * 135 | * @return 136 | */ 137 | #define DEFER_POP1_RETURN(RET) \ 138 | do { \ 139 | defer_scope_end(); \ 140 | return RET; \ 141 | } while (0) 142 | 143 | /** 144 | * @brief Push a new scope onto the stack 145 | * 146 | * @param ds An initialized scope from defer_scope_new or NULL to create a new 147 | * scope 148 | * 149 | * @return The scope added to the top of the stack 150 | */ 151 | defer_scope_t* defer_scope_push(defer_scope_t* ds); 152 | 153 | /** 154 | * @brief Execute all defers in the stack until the passed scope is 155 | * popped and 156 | * executed. 157 | * 158 | * @param ds The scope to pop to or NULL for innermost scope 159 | * 160 | * This function can be used to handle multi-level cleanup. If your 161 | * project 162 | * uses setjmp/longjmp to implement exception-like behavior, or 163 | * exceptions for 164 | * that matter, along with this library, it can be useful to clear 165 | * all the way 166 | * back to a known scope rather than just the innermost. 167 | */ 168 | void defer_scope_pop(defer_scope_t* ds); 169 | 170 | /** 171 | * @brief Defer execution of the free-like function fn, with 172 | * argument p, until 173 | * the nearest enclosing scope is cleared. 174 | * 175 | * @param fn The free-like function to execute on cleanup 176 | * @param p The user-defined value to pass to the function 177 | */ 178 | void defer(deferable_free_like fn, void* p); 179 | 180 | /** 181 | * @brief Defer execution of the free-like function fn, with intptr_t argument 182 | * p, until 183 | * the nearest enclosing scope is cleared. 184 | * 185 | * @param fn The free-like function to execute on cleanup 186 | * @param p The user-defined value to pass to the function 187 | */ 188 | static inline void deferi(deferable_free_like fn, intptr_t p) { 189 | defer(fn, (void*)p); 190 | } 191 | 192 | /** 193 | * @brief Defer execution of the void(void) function fn until the 194 | * nearest 195 | * enclosing scope is cleared 196 | * 197 | * @param fn The function to execute when the scope ends 198 | */ 199 | void defer_noarg(deferable_noarg fn); 200 | 201 | /** 202 | * @brief Explicitly create a new scope that *is not on the stack* 203 | * 204 | * @return A new but un-registered defer scope 205 | */ 206 | defer_scope_t* defer_scope_new(void); 207 | 208 | /** 209 | * @brief Explicitly execute and clear scope. This may be safely 210 | * used on a 211 | * scope that has been pushed to execute cleanup early. 212 | * 213 | * This should be most useful as either early cleanup, or to attach 214 | * cleanup of 215 | * something that had an undefined lifetime to the cleanup routine 216 | * of a scope 217 | * after which it should be cleared. Note that this can safely be 218 | * passed to 219 | * `defer`, as in `defer(defer_scope_clear, ds)`, as can 220 | * `defer_scope_delete`. 221 | * 222 | * @param ds The defer scope to clear 223 | */ 224 | void defer_scope_clear(defer_scope_t* ds); 225 | 226 | /** 227 | * @brief Explicitly execute, clear and free scope. This *must not* 228 | * be used 229 | * on a scope that has been pushed onto the stack. 230 | * 231 | * @param ds A new but un-registered defer scope 232 | */ 233 | void defer_scope_delete(defer_scope_t* ds); 234 | 235 | /** 236 | * @brief Defer execution of the free-like function fn, with 237 | * argument p, until 238 | * the scope ds is cleared, this can be across an un-structured 239 | * region 240 | * 241 | * @param ds scope to append to 242 | * @param fn function to execute on cleanup 243 | * @param p argument to be passed to fn 244 | */ 245 | void defer_specific(defer_scope_t* ds, deferable_free_like fn, void* p); 246 | 247 | /** 248 | * @brief Defer execution of the void(void) function fn, until the 249 | * scope ds 250 | * is cleared, this can be across an un-structured region 251 | * 252 | * @param ds scope to append to 253 | * @param fn function to execute on cleanup 254 | */ 255 | void defer_specific_noarg(defer_scope_t* ds, deferable_noarg fn); 256 | 257 | /* HELPER MACROS, IGNORE THESE... */ 258 | 259 | #define __DEFER_ARGS_INNER(T1, V1, ...) T1 V1 FOR_PAIRS(AS_ARGS, __VA_ARGS__) 260 | 261 | #define __DEFER_ARGS(...) \ 262 | (IFNEMPTY(__VA_ARGS__)(__DEFER_ARGS_INNER(__VA_ARGS__))) 263 | 264 | #define __DEFER_CALL_INNER(T1, V1, ...) V1 FOR_PAIRS(AS_ARG_NAMES, __VA_ARGS__) 265 | 266 | #define __DEFER_CALL(...) \ 267 | (IFNEMPTY(__VA_ARGS__)(__DEFER_CALL_INNER(__VA_ARGS__))) 268 | 269 | #endif 270 | --------------------------------------------------------------------------------