Handmade Hero»Forums»Code
100 posts
MACRO Magic, questions!

I'm a bit surprised this thing can be done like in handmade_debug_interface.h, I've never seen this before !! (ominous nerd laughter)

#define RecordDebugEvent(EventType, GUIDInit, NameInit)           \
u64 ArrayIndex_EventIndex = AtomicAddU64(&GlobalDebugTable->EventArrayIndex_EventIndex, GlobalDebugTable->RecordIncrement); \
u32 EventIndex = ArrayIndex_EventIndex & 0xFFFFFFFF;            \
Assert(EventIndex < ArrayCount(GlobalDebugTable->Events[0]));   \
debug_event *Event = GlobalDebugTable->Events[ArrayIndex_EventIndex >> 32] + EventIndex; \
Event->Clock = __rdtsc();                       \
Event->Type = (u8)EventType;                                    \
Event->CoreIndex = 0;                                           \
Event->ThreadID = (u16)GetThreadID();                         \
Event->GUID = GUIDInit;                                       \
Event->Name = NameInit

////

#define TIMED_BLOCK__(GUID, Name) timed_block TimedBlock_##Number(GUID, Name)
///

#define BEGIN_BLOCK_(GUID, Name) {RecordDebugEvent(DebugType_BeginBlock, GUID, Name);}
#define END_BLOCK_(GUID, Name) {RecordDebugEvent(DebugType_EndBlock, GUID, Name);}

#define BEGIN_BLOCK(Name) BEGIN_BLOCK_(DEBUG_NAME(Name), Name)
#define END_BLOCK() END_BLOCK_(DEBUG_NAME("END_BLOCK_"), "END_BLOCK_")

struct timed_block
{
    timed_block(char *GUID, char *Name)
    {
        BEGIN_BLOCK_(GUID, Name);
    }
    
    ~timed_block()
    {
        END_BLOCK();
    }
};

This is literally sorcery to me. How exactly TimedBlock_##Number(GUID, Name) becomes instantiated? I understand that is a trick to create an actual sort of dynamic variable naming on compile time, but how's that thing spitting back the name AND constructing the struct if you are not passing the parameters? Is there any definition of Number(GUID, Name) elsewhere that does that?

And why putting this as a struct? Won't you need to destruct the object upon exiting the block anyways? Why not just calling END_BLOCK()?

Ha, I'm asking too many questions in forums recently, sry about that!! 😁

Mārtiņš Možeiko
2559 posts / 2 projects
MACRO Magic, questions!
Edited by Mārtiņš Možeiko on

That's a mistake in code during editing/refactoring. This macro is not used anymore. Before it had Number in argument, but that somehow got lost in editing process: https://github.com/HandmadeHero/cpp/blob/day356/code/handmade_debug_interface.h#L130

#define TIMED_BLOCK__(GUID, Name, Number, ...) timed_block TimedBlock_##Number(GUID, Name, ## __VA_ARGS__)

Why use destructor? because it makes writing hierarchical things easier - without worrying that you forget manual END_BLOCK() call. Like this:

void f()
{
    TIMED_BLOCK("FunctionBlock", xxx);
    {
        TIMED_BLOCK("SubBlock1", yyy);
        ... // code
    }
    {
        TIMED_BLOCK("SubBlock2", zzz);
        ...
        for (int i=0; i<100; i++)
        {
            TIMED_BLOCK("LoopIteration", wwww);
            ...
        }
        ...
    }
}

Just having scope closing bracket } will automatically close timed block. But calling END_BLOCK() in each place would produce identical results.

100 posts
MACRO Magic, questions!
Replying to mmozeiko (#25836)

Ok gotcha, thanks Martins.

Just having scope closing bracket } will automatically close timed block.

Sorry don't get it, how that would auto call destructor?

Mārtiņš Možeiko
2559 posts / 2 projects
MACRO Magic, questions!
Replying to da447m (#25837)

That's how destructors work - objects that go out of scope call destructor when object is released. In stack that happens on } scope end. See: https://godbolt.org/z/fefG6f5s8

100 posts
MACRO Magic, questions!
Replying to mmozeiko (#25839)

Sure this will happen for void f(), but why would it happen for any of the curly braced TIMED_BLOCK macros? That's what you want right? Getting __rdtsc() ticks for each?

(or the point here is Event->Clock = actually pushes them to some container and you diff each from previous?)

Mārtiņš Možeiko
2559 posts / 2 projects
MACRO Magic, questions!
Edited by Mārtiņš Možeiko on
Replying to da447m (#25841)

Think of S in my code example as timed_block.

TIMED_BLOCK macro expands to timed_block var(args...) - which means var destructor will be called on } scope where TIMED_BLOCK is placed.

100 posts
MACRO Magic, questions!
Replying to mmozeiko (#25842)

Sry Im dumb and didn't click godbolt, now I just learned that that is actually a block scoped thing and not only syntactic sugary.

Thanks again 😉