1 2 | auto name = filenames[i]; defer { gamelib_free_string(name); }; |
mrmixer
Does anyone knows how Jonathan Blow's defer macro works ? Example of how he uses it at the end of this blog post.
1 2 auto name = filenames[i]; defer { gamelib_free_string(name); };
I found those two pages that show how to make defer macros, but I'm interested to know how Jonathan's curly braces syntax works.
http://the-witness.net/news/2012/11/scopeexit-in-c11/
http://www.gingerbill.org/article/defer-in-cpp.html
1 2 3 4 5 6 7 8 | struct DEFER_TAG {}; template< class Func > ScopeExit< Func > operator+( DEFER_TAG, Func&& func ) { return MakeScopeExit< Func >( std::forward< Func >( func ) ); } #define defer auto STRING_JOIN2(scope_exit_, __LINE__) = DEFER_TAG() + [&]() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include <utility> template <typename F> struct Defer { Defer( F f ) : f( f ) {} ~Defer( ) { f( ); } F f; }; template <typename F> Defer<F> makeDefer( F f ) { return Defer<F>( f ); }; #define __defer( line ) defer_ ## line #define _defer( line ) __defer( line ) struct defer_dummy { }; template<typename F> Defer<F> operator+( defer_dummy, F&& f ) { return makeDefer<F>( std::forward<F>( f ) ); } #define defer auto _defer( __LINE__ ) = defer_dummy( ) + [ & ]( ) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | TRAP(0x00,__handle_de) TRAP_SPC(0x01,__handle_db) TRAP_IST2(0x02,__handle_nmi) USER_TRAP(0x03,__handle_np) USER_TRAP(0x04,__handle_of) USER_TRAP(0x05,__handle_br) TRAP(0x06,__handle_ud) TRAP(0x07,__handle_nm) TRAP_IST1(0x08,__handle_df) TRAP(0x09,__handle_fpu_of) TRAP_ERR(0x0a,__handle_ts) TRAP_IST1(0x0b,__handle_np) TRAP_IST1(0x0c,__handle_ss) TRAP_IST1(0x0d,__handle_gp) TRAP_SPC(0x0e,__handle_pf) TRAP(0x0f,__handle_trap_0f) TRAP(0x10,__handle_mp) TRAP_ERR(0x11,__handle_ac) TRAP_IST1(0x12,__handle_mc) // ...and so on for all 256 possible interrrupts... INTERRUPT(0xf8) INTERRUPT(0xf9) INTERRUPT(0xfa) TRAP(0xfb,__handle_ipi_slow) TRAP(0xfc,__handle_ipi_fast) TRAP(0xfd,__handle_apic_error) TRAP_SPC(0xfe,__handle_apic_spurious) TRAP(0xff,handle_ipi_reschedule) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #define TRAP_IST1(n, f) \ extern void f(); \ idt_set_trap(n, f, 0, 1); #define TRAP_IST2(n, f) \ extern void f(); \ idt_set_trap(n, f, 0, 2); #define TRAP(n, f) \ extern void f(); \ idt_set_trap(n, f, 0, 3); #define TRAP_ERR TRAP #define USER_TRAP TRAP #define TRAP_SPC TRAP #define USER_TRAP_SPC TRAP #define INTERRUPT(n) \ extern void __irq_##n (); \ idt_set_int(n, __irq_##n, 0, 3); #include "interrupt_table.inc" |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #define TRAP_ERR(n, f) \ ENTRY(f) ; \ INT_HANDLER_BODY(n, handle_trap) #define TRAP(n, f) \ ENTRY(f) ; \ pushq $0 ; \ INT_HANDLER_BODY(n, handle_trap) #define INTERRUPT(irqn) \ ENTRY(__irq_##irqn) ; \ pushq $0 ; \ INT_HANDLER_BODY(irqn, handle_interrupt) #define USER_TRAP TRAP // We don't want automatically-generated stubs for these cases. #define TRAP_SPC(n, f) #define TRAP_IST1(n, f) #define TRAP_IST2(n, f) #define USER_TRAP_SPC(n, f) #include "interrupt_table.inc" |
We support markdown, so you can do the usual triple-backtick. For example:
```c
#define ENTITY_STATE_LIST \ X(ENTITY_STATE_NONE) \ X(ENTITY_STATE_ENTER_MINE_AND_DIG_FOR_NUGGET) \ X(ENTITY_STATE_VISIT_BANK_AND_DEPOSIT_GOLD) \ X(ENTITY_STATE_GO_HOME_AND_SLEEP_TIL_RESTED) \ X(ENTITY_STATE_QUENCH_THIRST) \ X(ENTITY_STATE_EAT_STEW) \ X(ENTITY_STATE_WIFES_GLOBAL) \ X(ENTITY_STATE_DO_HOUSE_WORK) \ X(ENTITY_STATE_VISIT_BATHROOM) \ X(ENTITY_STATE_COOK_STEW)
```
Your "defer" example looks more like "exception" handling than what I think of a defer statement. I expect a defer statement to allow me to keep the cleaning code next to the creation code. In your example it's the opposite that is happening.
I try to rewrite it just to experiment a bit with loop breaking which I never thought of using that way. I think it's a little bit more readable but still not something I find useful.
#define skipable while ( true ) #define skip_if_0( condition ) if ( !( condition ) ) break; #define skip_if_not_0( condition ) if ( condition ) break; #define no_skip( ) goto end #define catch_if_0( condition ) if ( !( condition ) ) #define catch_if_not_0( condition ) if ( !( condition ) ) #define skipable_end( ) end: void* alloc_test( void ) { void **result = 0; skipable { result = malloc( sizeof( result ) ); skip_if_0( result ); ( *result ) = malloc( 1024 ); skip_if_0( *result ); skip_if_0( do_some_other_tests( result ) ); no_skip( ); } catch_if_not_0( result ) { catch_if_not_0( *result ) { free( *result ); *result = 0; } free( result ); result = 0; } skipable_end( ); return result; }
In Ryan Fleury's articles about GUI they showed something that might look like a defer in C but I believe it can only do a single statement. It's useful when you need to have begin
and end
function calls.
#define concatenate( a, b ) a ## b #define scope_start_end_2( start, end, count ) \ for ( u32 concatenate( scope_start_end_i_, count ) = ( start, 1 ); \ concatenate( scope_start_end_i_, count ) ; \ concatenate( scope_start_end_i_, count )--, end ) #define scope_start_end_1( start, end, count ) scope_start_end_2( start, end, count ) #define scope_start_end( start, end ) scope_start_end_1( ( start ), ( end ), __COUNTER__ ) void func( void ) { u8* buffer = 0; // Allocate at the start of the scope, free when exiting the scope. scope_start_end( buffer = malloc( 1024 ), free( buffer ) ) { buffer[ 0 ] = 1; } }
A small trick I realized I could use a few days ago is to use compound literals with __VA_ARGS__
to create an array and get it's item count (only in C). Previously I was creating the array before calling the function which prevented me to pass the return value of the function "through" the macro.
#define array_count( array ) ( ( umm ) ( sizeof( array ) / sizeof( ( array )[ 0 ] ) ) ) #define equal_any( value, ... ) internal_equal_any( value, ( umm [] ) { __VA_ARGS__ }, array_count( ( umm [] ) { __VA_ARGS__ } ) ) stu b32 internal_equal_any( umm value, umm* array, umm count ) { b32 result = false; for ( umm i = 0; !result && i < count; i++ ) { result = ( array[ i ] == value ); } return result; } if ( equal_any( some_var, flag_a, flag_b, flag_c, flag_w ) ) { // ... }
I see ryan's thing as part of a more general (macro less!) idea, where instead of interpreting for as
for (init; loop condition; increment)
you interpret it as
for (init; before loop body expr; after loop body expr)
which can lead to some nice iterator type code like
for (IterState s = iter_begin(param); iter_next(&s);) { // s has some useful state here }
and both the loop condition logic and the increment are done inside iter_next
. And a lot of things look like iterators when you have an iterator hammer, e.g.
for (ScopedAlloc a = scoped_alloc(size); scoped_alloc_with(&a);) { }
Sort of very bad defer in C.
It's most likely not worth it.
#include <stdio.h> #include <stdlib.h> #define defer_scope for ( int first_time = 1, defer_count = 1, defer_id = 0; first_time || defer_count; first_time = 0, defer_id = 0, defer_count-- ) #define defer if ( first_time ) defer_count++; defer_id++; if ( !first_time && ( defer_id ) == defer_count ) #define now if ( first_time ) char* read_file( const char* file ); int main( int argc, char **argv ) { char* mem = 0; defer_scope { now { printf( "alloc\n" ); mem = ( char* ) malloc( 1024 ); } defer { printf( "free\n" ); free( mem ); } now { printf( "Some other stuff\n" ); } defer { printf( "Should happen before free\n" ); } now { printf( "If you add \"continue;\" before closing all defer scopes you don't need a now scope here.\n" ); } } char* file = read_file( "main.c" ); return 0; } char* read_file( const char* file ) { char* result = 0; FILE* f = 0; defer_scope { now { f = fopen( file, "rb" ); } defer { fclose( f ); continue; } fseek( f, 0, SEEK_END ); size_t size = ftell( f ); fseek( f, 0, SEEK_SET ); result = ( char* ) malloc( size ); fread( result, size, 1, f ); } return result; }