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" |
The X Macro is mentioned above. The tutorials I found on the internet in the past said, it is an old trick that few know about. Then they give examples that aren't very interesting, like auto generate an enum and strings for debugging.
I was reading an old AI book by Buckland where the text is easy to understand, except he really bought into object oriented, C++ templates, and UML diagrams. So I thought I'd use Casey's method of using a fat struct and a switch statement to translate his C++ code to C.
What I found while converting his state machine example, was code that needs to be close together so you can reason about it, is far apart when using a big switch statement in different functions. Then it occurred to me to use X Macros to auto generate the switch statements with function calls, so I can put related code close together in functions.
This is the list of items the X Macros need to do their thing:
#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)
The first X Macro generates an enum, the second makes strings that match the enum.
enum entity_state { #define X(name) name, ENTITY_STATE_LIST #undef X ENTITY_STATES_MAX }; static const char *entity_state_str[] = { #define X(name) "name", ENTITY_STATE_LIST #undef X "ENTITY_STATES_MAX" };
Which generates these:
enum entity_state { ENTITY_STATE_NONE, ENTITY_STATE_ENTER_MINE_AND_DIG_FOR_NUGGET, ENTITY_STATE_VISIT_BANK_AND_DEPOSIT_GOLD, ENTITY_STATE_GO_HOME_AND_SLEEP_TIL_RESTED, ENTITY_STATE_QUENCH_THIRST, ENTITY_STATE_EAT_STEW, ENTITY_STATE_WIFES_GLOBAL, ENTITY_STATE_DO_HOUSE_WORK, ENTITY_STATE_VISIT_BATHROOM, ENTITY_STATE_COOK_STEW, ENTITY_STATES_MAX }; static const char *entity_state_str[] = { "ENTITY_STATE_NONE", "ENTITY_STATE_ENTER_MINE_AND_DIG_FOR_NUGGET", "ENTITY_STATE_VISIT_BANK_AND_DEPOSIT_GOLD", "ENTITY_STATE_GO_HOME_AND_SLEEP_TIL_RESTED", "ENTITY_STATE_QUENCH_THIRST", "ENTITY_STATE_EAT_STEW", "ENTITY_STATE_WIFES_GLOBAL", "ENTITY_STATE_DO_HOUSE_WORK", "ENTITY_STATE_VISIT_BATHROOM", "ENTITY_STATE_COOK_STEW", "ENTITY_STATES_MAX" };
Here are two X Macros that make switch statements that call functions related to the enum above:
switch (state) { #define X(name) case name: name##_enter(entity); break; ENTITY_STATE_LIST #undef X case ENTITY_STATES_MAX: break; } switch (state) { #define X(name) case name: name##_execute(entity); break; ENTITY_STATE_LIST #undef X case ENTITY_STATES_MAX: break; }
Which makes these:
switch (state) { case ENTITY_STATE_NONE: ENTITY_STATE_NONE_enter(entity); break; case ENTITY_STATE_ENTER_MINE_AND_DIG_FOR_NUGGET: ENTITY_STATE_ENTER_MINE_AND_DIG_FOR_NUGGET_enter(entity); break; ... } switch (state) { case ENTITY_STATE_NONE: ENTITY_STATE_NONE_execute(entity); break; case ENTITY_STATE_ENTER_MINE_AND_DIG_FOR_NUGGET: ENTITY_STATE_ENTER_MINE_AND_DIG_FOR_NUGGET_execute(entity); break; ... }
Then in another file if you want, you write the functions the generated switch statements call.
internal void ENTITY_STATE_NONE_enter(struct entity *entity) { // Your code here } internal void ENTITY_STATE_NONE_execute(struct entity *entity) { // Your code here } internal void ENTITY_STATE_ENTER_MINE_AND_DIG_FOR_NUGGET_enter(struct entity *entity) { // Your code here } internal void ENTITY_STATE_ENTER_MINE_AND_DIG_FOR_NUGGET_execute(struct entity *entity) { // Your code here }
When looking around this forum I found a blog by Cakelisp that mentioned them, then I found a link that actually has some good examples.
https://en.wikibooks.org/wiki/C_Programming/Preprocessor_directives_and_macros#X-Macros
So C has inheritance just as easy to read as C++, without using pointers. Well the X Macro part is ugly, but it is the same thing over and over.
Mods, the above post is messed up. Is there a page here that gives instructions as to what tags to use to mark code?
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)
```
Defer for C:
#define DEFER while (true) bool do_some_other_tests(void *a) { return true; } void * defer1(void) { void **a = NULL; /* while (true) { */ DEFER { a = malloc(sizeof *a); if (a == NULL) break; *a = malloc(1024); if (*a == NULL) break; if (!do_some_other_tests(a)) break; return a; } /* Cleanup */ if (a == NULL) return NULL; if (a != NULL) free(*a); free(a); return NULL; }
By quelsolaar from here: https://news.ycombinator.com/item?id=25417211
A handy macro is offsetof() found in #include <stddef.h>
It gets the offsets of variables in structs.
I use it when passing a struct variable into a function as void *, but need to access one of its members.
typedef struct hash32_query_elem hash32_query_elem; struct hash32_query_elem { uint64_t Key; hash32_query_elem *Prev; hash32_query_elem *Next; }; typedef struct entity entity; struct entity { int Data1; char Data2; hash32_query_elem HqElem; }; Hq->HqElemOffset = offsetof(entity, hash32_query_elem); internal hash32_query_elem * Hash32QueryElem(const hash32_query * Hq, void *Data) { return (hash32_query_elem *) ((char *)Data + Hq->ElemOffset); } internal void * Hash32QueryElemOwner(const hash32_query * Hq, hash32_query_elem * Elem) { return ((char *)Elem - Hq->ElemOffset); }
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);) { }
"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."
For that, I was thinking about about pushing function pointers or ids and their parameters onto a stack, and after the defer statement, pop them off the stack and call them. There could be a special function that does the push, or you could make wrappers for the functions and call the push inside them. I haven't tried it yet, because I haven't needed defer yet, so I don't know if it would work.
For that it would look something like:
DEFER_BEGIN { NormalFunctionA(Param1, Param2, ...); DeferWrapperFunction(Param1, Param2, ...); // Push onto stack NormalFunctionB(Param1, Param2, ...); } DEFER_END; // Pop stack and call deferred functions
What I like about about quelsolaar's solution and this, is you don't have to think backwards, or in a swirly whirly way, like they do when they use the goto solution for this problem, which is Jen's argument for a defer for C.
https://gustedt.wordpress.com/2020/12/14/a-defer-mechanism-for-c/
https://hal.inria.fr/hal-03090771/document
Listing 2. Emulation of defer by goto
{ void *const p = malloc(25); if (!p) goto DEFER0; if (false) { DEFER1: free(p); goto DEFER0; } void *const q = malloc(25); if (!q) goto DEFER1; if (false) { DEFER2: free(q); goto DEFER1; } if (mtx_lock(&mut) == thrd_error) goto DEFER2; if (false) { DEFER3: mtx_unlock(&mut); goto DEFER2; } // all resources acquired goto DEFER3; DEFER0:; }
Listing 3. A linearization
{ void *const p = malloc(25); if (!p) goto DEFER0; void *const q = malloc(25); if (!q) goto DEFER1; if (mtx_lock(&mut) == thrd_error) goto DEFER2; // all resources acquired mtx_unlock(&mut); DEFER2: free(q); DEFER1: free(p); DEFER0:; }