Handmade Network»Forums
Andre
15 posts
Post your C/C++ macro tricks
GNU's online docs on the C Preprocessor are pretty thorough if you're interested in learning about the kinds of things that macros can do. Even if you only ever use MSVC, there's lots of good info there.
Simon Anciaux
1266 posts
Post your C/C++ macro tricks
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
TM
13 posts
Post your C/C++ macro tricks
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

You typically need a dummy struct and an operator overload to do this.
I think he is doing this:
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() + [&]()

This enables you to have curly brackets after the macro, which is actually the definition of a lambda function which is then used to construct a ScopeExit object, which executes it at the end of the scope.
Simon Anciaux
1266 posts
Post your C/C++ macro tricks
Edited by Simon Anciaux on Reason: Was missing a line
Thanks.
Here is the complete code:
 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( ) + [ & ]( )
Andrew Bromage
183 posts / 1 project
Research engineer, resident maths nerd (Erdős number 3).
Post your C/C++ macro tricks
Edited by Andrew Bromage on
A variant on the X macro is the "header file of macro calls", which I learned from the Clang source code. I'm very much a convert to this style of boilerplate-scrapping, and it's saved my bacon a few times.

Here's an example from Homebrew OS. The interrupt table is kind of complex, because it has to deal with machine exceptions/traps, hardware interrupts, and software-defined interrupts. For some traps, the CPU pushes extra information onto the stack (e.g. page fault pushes information about the memory access onto the stack), so the stack frame may not be the same in all cases. Some traps need special care (e.g. non-maskable interrupts or the machine-check exception).

To handle all the complexity, I define the interrupt table a header file which contains a big block of macro calls, called 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
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)


To use this, you do a bunch of #defines to say what all the cases mean, then #include the file.

So, for example, here is the code to set up the interrupt descriptor table (in C):

 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"


And here is the code to automatically generate interrupt handler stubs (in assembler):

 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"


The reason why this approach work well is that there are 256 cases which need to be kept consistent between different source files, even written in different languages. The "header file of macro calls" keeps it all in one place.
Asaf Gartner
63 posts / 1 project
Handmade Network Staff
Post your C/C++ macro tricks
Edited by Asaf Gartner on

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) 

```

Simon Anciaux
1266 posts
Post your C/C++ macro tricks

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 ) ) {
    // ...
}

39 posts
Post your C/C++ macro tricks

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);) {

}
Simon Anciaux
1266 posts
Post your C/C++ macro tricks

Sort of very bad defer in C.

  • Requires to use "defer_scope";
  • Requires non-deferred code to be in a "now" scope, unless it's the code after every defer scopes and every defer scopes contains a "continue" statement;
  • Requires variables to be declared outside of the "now" scopes (if you want to use it after the now scope), which is really bad because if you just put the variable in the defer_scope without initializing it the compiler will complain about the potential use of non initialized variables, and if you initialized it, it will get re initialized before the defer scopes are executed. So you either need to declare them as static, or declare them outside the defer_scope. In every way it's annoying.
  • If you exit the "defer_scope" using break, return, or goto the defers aren't executed;
  • Uses multiple statements macros;
  • Can't be nested;
  • Use a loop and ifs so there is extra state/code;
  • Potential variable name collisions;

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;
}