Skip to content

Instantly share code, notes, and snippets.

@DBJDBJ
Last active March 8, 2025 09:31
Show Gist options
  • Save DBJDBJ/c1dc33cc6df2e14f5d8840445f65f5ee to your computer and use it in GitHub Desktop.
Save DBJDBJ/c1dc33cc6df2e14f5d8840445f65f5ee to your computer and use it in GitHub Desktop.
Defer and Begin/End. For any C version. With Gobolt demo and proof of concept.
#ifndef DBJ_DEFER_BEGIN_END_H
#define DBJ_DEFER_BEGIN_END_H
/*
comment this in to see the demo
#define DBJ_DEFER_BEGIN_END_DEMO 1
*/
/*
2025-MAR [email protected] https://godbolt.org/z/adYP4ach4
first seen it here https://youtu.be/QpAhX-gsHMs
2021-APR [email protected] added macro defer()
Two Macros. Usage:
beginend( on_scope_begin(), on_scope_end() ) { }
defer( on_scope_end() ) { }
See the ad-hoc-demo bellow
Q: What might be good about this two macros?
A: They work on any C version, using any compiler.
There is no trick: Both macros are for(){ } loop that loops once
I see no reason for this not to work on any C version and compiler
*/
#define _POSIX_C_SOURCE 200112L
#include <stdlib.h>
#undef macro_concat_
#undef macro_concat
#undef macro_var
#define macro_concat_(a,b) a ## b
#define macro_concat(a,b) macro_concat_(a,b)
#define macro_var(name) macro_concat( name, __LINE__)
#define beginend( start,end) \
for ( int macro_var(_i_) = ( start, 0 ) ; ! macro_var(_i_) ; ( macro_var(_i_) += 1, end) )
static inline void do_nothing_ (void) {}
// #define defer( at_scope_end ) beginend( __LINE__, at_scope_end )
#define defer( at_scope_end) for ( int macro_var(_i_) = 0;\
! macro_var(_i_) ; ( macro_var(_i_) += 1, at_scope_end) )
#endif // DBJ_DEFER_BEGIN_END_H
/*
Ad-hoc demo
*/
#if DBJ_DEFER_BEGIN_END_DEMO
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#undef B
#undef P
#undef M
// NOTE: F must be a string literal
#define B(X_) (X_) ? "true" : "false"
#define P(F,X_) fprintf( stdout, "\n%04d : %16s :\t" F, __LINE__, #X_, (X_))
#define M(S) fprintf( stdout, "\n%04d : %16s :\t%s", __LINE__, " ", S)
static void make_ ( char ** ptr, size_t sze_ ) { *ptr= (char*)calloc(sze_, sizeof(char)); P("made (%p)", (void*)*ptr); P("char [%zu]", sze_); }
static void free_ ( void * ptr ) {P("freed (%p)", ptr ); free(ptr); ptr = NULL; }
static void close_file ( FILE * fp_ ) {
if ( fp_ == NULL) { M("close_file(): Avoiding NULL file pointer");
return ; }
if ( ferror(fp_)) perror("I/O error");
P("closing FILE * (%p)", (void*)fp_);
fclose(fp_ ) ;
}
static void here_ (void) { M("for() {} block Begin"); }
static void there_ (void) { M("for() {} block End"); }
int main (void)
{
M("Very simmple demo -----------------------------------");
beginend( here_(), there_() )
{
M("Inside for(){} block");
}
char * bufy_ = 0 ;
printf("\n");
M("Mandatory auto free demo ----------------------------");
beginend( make_( &bufy_, 0xFF ), free_( bufy_ ) )
{
memcpy( bufy_, "DATA", 5 );
P("using (%p)", (void*)bufy_); P("\"%s\"", bufy_);
}
printf("\n");
M("Defer file close demo -------------------------------");
FILE * dummsy_ = tmpfile();
defer( close_file( dummsy_ )) {
P("temp FILE * (%p)", (void*)dummsy_);
}
return 42 ;
}
#undef B
#undef P
#undef M
#endif // DBJ_DEFER_BEGIN_END_DEMO
@DBJDBJ
Copy link
Author

DBJDBJ commented Jul 16, 2021

Once more: arguments are function calls. But: not executed because they are macro arguments.

   // make() and free_() are *not* executed on this line
    beginend( make_( &bufy_, 0xFF ), free_( bufy_ ) )
    {
         memcpy( bufy_, "DATA", 5 );
         printf("\nusing (%p) : \"%s\"", (void*)bufy_, bufy_);
    }

That is because beginend is a macro, not a function. And macro is just a text replacement mechanism. Ditto, the above is translated into this:

// __LINE__ was 123
for ( int _i_123 = ( make_( &bufy_, 0xFF ) , 0 ) ; ! _i_123 ; ( _i_123 += 1,  free_( bufy_ )) )
{
         memcpy( bufy_, "DATA", 5 );
         printf("\nusing (%p) : \"%s\"", (void*)bufy_, bufy_);
}

Notice the comma operators in the for() above 😉

@DBJDBJ
Copy link
Author

DBJDBJ commented Mar 5, 2025

gcc 10.3: https://godbolt.org/z/3dWKdsadr

gcc 14.2 also OK

args for both: -s -Wall -pedantic

Ah yes, this does not work for GCC 14.2 C++ , or clang 19.1.0?

39 | for ( int macro_var(i) ... error: 'i' was not declared in this scope

strange, macro expansion rules may have changed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment