handmade.network » Forums » Post your C/C++ macro tricks
MandleBro
Jack Mott
96 posts
1 project

Web Developer by day, game hobbyist by night. Fond of C and F#

#7172 Post your C/C++ macro tricks
1 year ago Edited by Jack Mott on June 7, 2016, 8:52 p.m. Reason: code

I learned of a neat macro trick today, maybe old hat to most of you but maybe not

"#" is a "stringify" operator, which you can use to sort of replicate reflection-ish features.

For instance say you wanted a utility function to serialize something to json, you can do something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#define writeInt(x) _writeInt(#x,x)

writeInt(mass);

//and it will expand to:

_writeInt("mass",mass);


#define loadInt(x,json) x = _loadInt(#x,json)

//expands to:

mass = _loadInt("mass",json);



Saves us a ton of boilerplate, and prevents you from miss typing string literals.

ZaKlaus
Dominik Madarász
7 posts

ReGuider Developer

#7174 Post your C/C++ macro tricks
1 year ago Edited by Dominik Madarász on June 8, 2016, 1:50 p.m. Reason: added macro.

Honestly I'm a big fan of macros, sometimes I even unnecessarily overuse them.

I either use it to add some reflection or I substitute common code snippets to make it easier for me when coding.

There is one particular macro I used to do, but then found out it's unpractical and also quite useless for me since I've left OOP for now:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#define CLASS(p) class p\
{\
	inline virtual std::string _GetClassName_() { return #p; }

//which then could be used as
CLASS(TestClass)
    void DoStuff();
    //...
};

//due to missing brace in code, it broke some VS functionalities and highlighting (which breaks on everything now, excuse me for highlighting).
//I've got rid of this macro and added a factory (which is not related, see below), vector holding pair of string (class name) and class constructor.
//but tbh, I still use simplified version of CLASS macro for ex. as:
class BigDoor : Door
{
public:
    REGCLASS("BigDoor") // inline virtual std::string __GetClassName__() { return "BigDoor"; }
    //...
};
// and so on..


When it comes to macros, I do silly ones often, then there's common stuff like:
1
2
3
4
5
#define SCALL(x, c) if (x != nullptr) c

//or
#include <stdio.h>
#define ASSERT(p,q) if(!p) { fprintf(stderr, "ERROR: \"" ## q ## "\" IN " __FILE__ " AT LINE %d!\n", __LINE__); *(int*)0=0; } 


Which is pretty self-explanatory and maybe useless, I guess.

I'd like to see some interesting tricks, there's always a time to learn something new.

Hobbyist C/C++ developer and occasional graphics designer.
MandleBro
Jack Mott
96 posts
1 project

Web Developer by day, game hobbyist by night. Fond of C and F#

#7175 Post your C/C++ macro tricks
1 year ago

I like the SCALL! That could come in handy.

I just stumbled on another reflection-ish trick with enums, that I am really excited about because I spent hours trying to find a good way to get a ENUM tied to the strings it represents:

http://stackoverflow.com/question...convert-enum-names-to-string-in-c
ZaKlaus
Dominik Madarász
7 posts

ReGuider Developer

#7177 Post your C/C++ macro tricks
1 year ago

Thanks, I'll look into that one. It's nice to have this topic, always stuff to learn.

Hobbyist C/C++ developer and occasional graphics designer.
mmozeiko
Mārtiņš Možeiko
1349 posts
1 project
#7185 Post your C/C++ macro tricks
1 year ago

I dislike macros that create multiple statements. Doing them for definitions or declarations is fine. But I'm strongly against mult-statement macros. Why? Because there is no easy way to debug them, meaning - you cannot step statement by statement and examing values in debugger.
Horrowind
6 posts
#7192 Post your C/C++ macro tricks
1 year ago

Not too interesting, but funny:

1
#define private public
ratchetfreak
267 posts
#7194 Post your C/C++ macro tricks
1 year ago

Horrowind:
Not too interesting, but funny:

1
#define private public


That's not complete without

1
2
#define class struct
#define protected public
Evine
Daniel Rasmussen
5 posts
#7196 Post your C/C++ macro tricks
1 year ago

Here's a little trick to creating generic functions and types. Multiple include with the same file, and you just switch the VAR_TYPE and VAR_NAME between includes.
This is a bounded/sized array implementation that only requires 3 lines of code to create a new type.
1
2
3
#define VAR_TYPE float // the type you want.
#define VAR_NAME f32 // type name appended to the struct / function.
#include "bound_array.cpp"



 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
// "main.cpp"
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>

#define CONCAT_(a,b) a##b
#define CONCAT(a,b) CONCAT_(a,b)

#define VAR_TYPE float
#define VAR_NAME f32
#include "bound_array.cpp"

#define VAR_TYPE int64_t
#define VAR_NAME s64
#include "bound_array.cpp"

int main()
{
    Array_f32 eple = Array_f32_init(32);
    eple[31] = 91;
    printf("eple.size = %lu\neple[31] = %f\n", eple.size, eple[31]);
    
    
    Array_s64 pera = Array_s64_init(54);
    pera[24] = 3771;
    printf("pera.size = %lu\npera[24] = %li\n", pera.size, pera[24]);
    
    return 0;
}


 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
31
32
33
// "bound_array.cpp"
#ifndef VAR_TYPE
#error "VAR_TYPE is not defined."
#endif
#ifndef VAR_NAME
#error "VAR_NAME is not defined."
#endif

#define ARRAY_TYPE CONCAT(Array_, VAR_NAME)
struct ARRAY_TYPE{
    uint64_t size;
    VAR_TYPE *element;
    
    VAR_TYPE &operator [](uint64_t index)
    {
        assert(index < size);
        return element[index];
    }
};

static
ARRAY_TYPE CONCAT(ARRAY_TYPE, _init) (uint64_t size_in)
{
    ARRAY_TYPE result;
    result.element = (VAR_TYPE *)malloc(size_in * sizeof(VAR_TYPE));
    assert(result.element);
    result.size = size_in;
    return result;
}

#undef ARRAY_TYPE
#undef VAR_TYPE
#undef VAR_NAME
Mr4thDimention
Allen Webster
240 posts
2 projects

Heyo

#7198 Post your C/C++ macro tricks
1 year ago Edited by Allen Webster on June 9, 2016, 4:24 p.m. Reason: Make parameters explicit

My favorite macro trick is for making something that looks like a resumable function call, sort of like a really light weight coroutine. So you can write code that says "ahh at this point I would like to return to the user some info, but then when they call me I want to resume right after the return point".


 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
31
32
33
34
35
struct State{
    int _pc_;
    
    // other variables that need to carry their value
    // over multiple resumes of the function call.
};

#define DrYield(c,v) do{ state->_pc_ = c; return(v); resume_##c:; } while(false)
#define DrReturn(v) do{ state->_pc_ = -1; return(v); } while(false)
#define DrCase(c) case c: goto resume_##c

void
foo(State *s, void *new_memory, File_Data file){
    switch (s->_pc_){
        DrCase(1);
        // ...
        DrCase(5);
    }
    
    // ... code stuff ...
    for (begin();check();increment()){
        if (need_memory_from_user()){
            DrYield(1, NeedMemory);
            // do something with new_memory
        }
        if (need_file_from_user()){
            DrYield(2, NeedFile);
            // do something with file
        }
        // ... code stuff ...
    }
    // ... code stuff ...
    
    DrReturn(state->success);
}


I did a video trying it out here if you want to see it in detail without using goto. The goto version is required if your resumable function might want to use switches itself though.

The only issue I don't like is having to list all the cases and giving each yield point a unique integer. I haven't found away around that using the goto version though.
ratchetfreak
267 posts
#7199 Post your C/C++ macro tricks
1 year ago

Mr4thDimention:
My favorite macro trick is for making something that looks like a resumable function call, sort of like a really light weight coroutine. So you can write code that says "ahh at this point I would like to return to the user some info, but then when they call me I want to resume right after the return point".


 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
31
32
33
34
35
struct State{
    int _pc_;
    
    // other variables that need to carry their value
    // over multiple resumes of the function call.
};

#define DrYield(c,v) do{ state->_pc_ = c; return(v); resume_##c:; } while(false)
#define DrReturn(v) do{ state->_pc_ = -1; return(v); } while(false)
#define DrCase(c) case c: goto resume_##c

void
foo(State *s, void *new_memory, File_Data file){
    switch (s->_pc_){
        DrCase(1);
        // ...
        DrCase(5);
    }
    
    // ... code stuff ...
    for (begin();check();increment()){
        if (need_memory_from_user()){
            DrYield(1, NeedMemory);
            // do something with new_memory
        }
        if (need_file_from_user()){
            DrYield(2, NeedFile);
            // do something with file
        }
        // ... code stuff ...
    }
    // ... code stuff ...
    
    DrReturn(state->success);
}


I did a video trying it out here if you want to see it in detail without using goto. The goto version is required if your resumable function might want to use switches itself though.

The only issue I don't like is having to list all the cases and giving each yield point a unique integer. I haven't found away around that using the goto version though.


if you don't mind using non-standard GCC extension you can use label as value: http://stackoverflow.com/q/1777990/731620

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
struct State{
    void* _pc_;
    //...
}
#define DrYield(v) do{ state->_pc_ = &&resume_## __LINE__; return(v); resume_##__LINE__:; } while(false)
#define DrReturn(v) do{ state->_pc_ = 0; return(v); } while(false)

void
foo(State *s, void *new_memory, File_Data file){
    if(s->_pc_) goto *s->_pc_;
    //...
}
mmozeiko
Mārtiņš Možeiko
1349 posts
1 project
#7202 Post your C/C++ macro tricks
1 year ago Edited by Mārtiņš Možeiko on June 9, 2016, 10:07 p.m.

Computed goto statements are a very nice thing. They allow to create lower-overhead interpreters than a regular switch statement: http://eli.thegreenplace.net/2012...oto-for-efficient-dispatch-tables
Original Android Dalvik interpreter used computed goto (and few other tricks) to get better performance: https://android.googlesource.com/...2415b48bdef63/vm/mterp/README.txt

Unfortunately no MSVC support, until we get clang fronted :(
cmuratori
Casey Muratori
799 posts
1 project

Casey Muratori is a programmer at Molly Rocket on the game 1935 and is the host of the educational programming series Handmade Hero.

#7206 Post your C/C++ macro tricks
1 year ago

I am skeptical that these computed gotos are necessary to get the "improved" performance. For example, in MSVC you would just place an __assume(0) in the default: case of a switch and it would no longer emit the bounds-checking code, right? Has anyone done a more thorough analysis as to whether these are interesting?

- Casey
Quarter
stephen goglin
20 posts

aka Quartertron

#7207 Post your C/C++ macro tricks
1 year ago

One of my favourites is the X macro. A simple example is:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#define MY_ENUMS \
    E(A)         \
    E(B)         \
    E(C)

#define E(e) Enum_ ## e,
typedef enum {
MY_ENUMS
} MyEnums;
#undef E

#define E(e) #e,
const char *MyEnumStrings[] = {
MY_ENUMS
};
#undef E


And of course you can do more complicated things.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#define STATE_MACHINE                                 \
    X(Start,StartFunction,ParseState)                 \
    X(Parse,ParseSomeStuff,PrintState)                \
    X(Print,MaybeThisShouldBeGeneratedToo,ParseState) \
    /* yadda yadda yadda */

#define X(s,f,n) State_ ##s,
typedef enum {
    STATE_MACHINE
} MyStates;
#undef X

// much later in the code
#define X(s,f,n) case State_ ##s: if (f()) CurrentState = n; break;
    switch (CurrentState) {
        STATE_MACHINE
        default: exit(-42);
    }
#undef X


And so on. You can generate all kinds of different things from the initial list.

None
mmozeiko
Mārtiņš Možeiko
1349 posts
1 project
#7209 Post your C/C++ macro tricks
1 year ago Edited by Mārtiņš Možeiko on June 10, 2016, 6:17 p.m.

I have used computed goto for gcc compiler before and it does creates better code.

Here's a micro-benchmark: https://gist.github.com/mmozeiko/...681dde202bd9bfe8fe185844858f04eaf
It's a tiny VM that performs "--x" operation billion times.

For gcc 5.3.0 it creates ~10% faster code for computed goto:
1
2
3
switch = 2132
goto   = 1913
improvement = 10%


Unfortunately gcc generates range check even with __builtin_unreachable in default case.
gingerBill
Ginger Bill
196 posts
2 projects

I am ginger thus have no soul.

#7213 Post your C/C++ macro tricks
1 year ago Edited by Ginger Bill on June 10, 2016, 9:03 a.m.

Here are some of my favourites:

 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
31
32
// Token to String
#define TO_STRING(x) #x

// Static Assertion
#define STATIC_ASSERT3(cond, msg) typedef char static_assertion_##msg[(!!(cond))*2-1]
#define STATIC_ASSERT2(cond, line) STATIC_ASSERT3(cond, static_assertion_at_line_##line)
#define STATIC_ASSERT1(cond, line) STATIC_ASSERT2(cond, line)
#define STATIC_ASSERT(cond)        STATIC_ASSERT1(cond, __LINE__)

// *_of "operators"
typedef ptrdiff_t isize;
// Signed sizeof is more useful
#define size_of(x) (isize)(sizeof(x))
// Prevents C++ arrays from being passed
#define count_of(x) ((size_of(x)/size_of(0[x])) / ((isize)(!(size_of(x) % size_of(0[x])))))
#define offset_of(Type, element) ((isize)&(((Type *)0)->element))
// C-Only
#define align_of(Type) offset_of(struct { char c; Type member; }, member)


// Swap
#define swap(Type, a, b) do { Type tmp = (a); (a) = (b); (b) = tmp; } while (0)


// Bit cast: Very similar to doing `*(T *)(&u)` but can be faster
#define BIT_CAST(dest, source) do { \
	STATIC_ASSERT(size_of(*(dest)) <= size_of(source)); \
	memcpy((dest), &(source), size_of(*dest)); \
} while (0)

// Cast - Easy to grep
#define cast(Type) (Type)



I have loads more in gb.h.