Skip to content

Instantly share code, notes, and snippets.

@eloraiby
Last active July 31, 2020 11:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eloraiby/f64fcba0d489f0d31aa544d66cbfd7a6 to your computer and use it in GitHub Desktop.
Save eloraiby/f64fcba0d489f0d31aa544d66cbfd7a6 to your computer and use it in GitHub Desktop.
defer implementation in Clang/Gcc
//
// Copyright 2020-Present(c) Wael El Oraiby
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//
// clang:
// $ sudo apt-get install libblocksruntime-dev
// $ clang -fblocks main.c -o testclang -lBlocksRuntime
//
// gcc:
// $ gcc -o testgcc main.c
//
// References:
// 1. https://fdiv.net/2015/10/08/emulating-defer-c-clang-or-gccblocks
// 2. https://echorand.me/site/notes/articles/c_cleanup/cleanup_attribute_c.html
//
// caveats:
// * gcc nested functions will pass captured variables by reference
// * clang blocks will pass captured variable by value (mutating them will
// NOT change their value inside the block)
//
// Always consider passing non mutable variables to be compatible with both
//
#include <stdio.h>
#ifdef __clang__
static inline void defer_cleanup(void (^*b)(void)) { (*b)(); }
#define __defer3__(LINE, DEFER) \
__attribute__((cleanup(defer_cleanup))) void (^dummy_ ## LINE)(void) = ^{DEFER}
#define __defer2__(DEFER, L) __defer3__(L, DEFER)
#define defer(DEFER) __defer2__(DEFER, __LINE__)
#else
#define __defer3__(LINE, DEFER) \
void clean_ ## LINE () { DEFER } \
int __attribute__((unused)) dummy_ ## LINE __attribute__((__cleanup__(clean_ ## LINE))) = 0
#define __defer2__(DEFER, L) __defer3__(L, DEFER)
#define defer(DEFER) __defer2__(DEFER, __LINE__)
#endif
int
main(int argc, char **argv) {
int i =0, j = 0;
for(i = 0; i < 10; ++i) {
defer({
printf("i: Cleaning up %d\n", __LINE__);
printf("i: Final value: i:%d - j:%d\n",i, j);
});
for(j = 0; j < 4; ++j) {
defer({
printf("j: Cleaning up %d\n", __LINE__);
printf("j: Final value: %d\n",j);
});
if(i == 3 && j == 2) goto final;
}
}
final:
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment