├── .gitignore ├── LICENSE ├── Makefile ├── README ├── defer.h └── demo.c /.gitignore: -------------------------------------------------------------------------------- 1 | demo.o 2 | demo 3 | 4 | .*.swp 5 | .*.swo 6 | .*.swn 7 | .*.swm 8 | .*.swl 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, Elijah Stone 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall -Wextra -std=c89 2 | CC ?= cc 3 | 4 | demo: demo.o 5 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This library implements a 'Defer' statement in c, allowing statements to run at 2 | function return time, generally to do cleanup. It is similar to 'defer' in Go 3 | and to 'scope(exit)' in D, but note that it does not care about lexical scopes. 4 | Integrating it into your codebase is simple; see demo.c for example usage. 5 | 6 | Note: this is likely to be noticeably faster with compilers that support the GNU 7 | C extensions; if you are on windows and performance is something you care about, 8 | try Clang or MinGW. 9 | 10 | Note: at the moment, a maximum of 32 deferred statements per function is 11 | supported. To change this number, #define DEFER_MAX_DEFERRED_STATEMENTS to a 12 | different value before including defer.h (or via a compiler flag). 13 | -------------------------------------------------------------------------------- /defer.h: -------------------------------------------------------------------------------- 1 | #ifndef DEFER_H 2 | #define DEFER_H 3 | 4 | #ifndef DEFER_MAX_DEFERRED_STATEMENTS 5 | # define DEFER_MAX_DEFERRED_STATEMENTS 32 6 | #endif 7 | 8 | #if defined(__GNUC__) || defined(__TINYC__) 9 | 10 | #define Deferral \ 11 | unsigned char _num_deferrals = 0; \ 12 | void *_defer_return_loc = 0, *_deferrals[DEFER_MAX_DEFERRED_STATEMENTS] = {0}; 13 | 14 | #ifdef __PCC__ 15 | # define Defer(block) _Defer(block, __LINE__) 16 | # define Return _Return(__LINE__) 17 | #else 18 | # define Defer(block) _Defer(block, __COUNTER__) 19 | # define Return _Return(__COUNTER__) 20 | #endif 21 | 22 | #define _defer_tokpaste(a, b) a ## b 23 | 24 | #define _Defer(block, n) do { \ 25 | _deferrals[_num_deferrals++] = && _defer_tokpaste(_defer_ini, n); \ 26 | if (0) { \ 27 | _defer_tokpaste(_defer_ini, n): \ 28 | block; \ 29 | if (_num_deferrals) { \ 30 | goto *_deferrals[--_num_deferrals]; \ 31 | } else { \ 32 | goto *_defer_return_loc; \ 33 | } \ 34 | } \ 35 | } while (0) 36 | 37 | #define _Return(n) \ 38 | if (_num_deferrals) { \ 39 | _defer_return_loc = && _defer_tokpaste(_defer_fini_, n); \ 40 | goto *_deferrals[--_num_deferrals]; \ 41 | } else _defer_tokpaste(_defer_fini_, n): return 42 | 43 | #else /* !__GNUC__ && !__TINYCC__ */ 44 | 45 | #include 46 | 47 | #ifdef _MSC_VER 48 | # pragma message("You are using the unsafe longjmp()-based defer implementation. Expect bugs if you don't know what you're doing.") 49 | #else 50 | # warning You are using the unsafe longjmp()-based defer implementation. Expect bugs if you don't know what you're doing. 51 | #endif 52 | 53 | #define Deferral \ 54 | volatile unsigned char _num_deferrals = 0; \ 55 | jmp_buf _defer_return_loc = {0}, _deferrals[DEFER_MAX_DEFERRED_STATEMENTS] = {0}; 56 | 57 | #define Defer(block) do { \ 58 | if (setjmp(_deferrals[_num_deferrals++])) { \ 59 | block; \ 60 | if (_num_deferrals) { \ 61 | longjmp(_deferrals[--_num_deferrals], 1); \ 62 | } else { \ 63 | longjmp(_defer_return_loc, 1); \ 64 | } \ 65 | } \ 66 | } while (0) 67 | 68 | #define Return \ 69 | if (!setjmp(_defer_return_loc)) { \ 70 | if (_num_deferrals) longjmp(_deferrals[--_num_deferrals], 1); \ 71 | } else return 72 | 73 | #endif /* __GNUC__ */ 74 | 75 | #endif /*DEFER_H*/ 76 | -------------------------------------------------------------------------------- /demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "defer.h" 6 | 7 | void test1(void) {Deferral 8 | char *x = malloc(0x100); 9 | Defer(free(x)); 10 | 11 | /* legal because deconstruction happens in reverse order */ 12 | Defer({ 13 | printf("x is %s\n", x); 14 | x[1]++; 15 | printf("now %s\n", x); 16 | }); 17 | 18 | strcpy(x, "Hello world"); 19 | 20 | Return; 21 | } 22 | 23 | int test2(void) {Deferral 24 | puts("1"); 25 | 26 | /* caveat: unlike in other programming languages, the return expression 27 | * is evaluated after deferred statements. Unfortunately, there is no 28 | * good way around this. 29 | */ 30 | Defer(puts("2")); 31 | Return puts("3"); 32 | } 33 | 34 | int main(void) { 35 | test1(); 36 | return test2(); 37 | } 38 | --------------------------------------------------------------------------------