handmade.network » Forums » Post your C/C++ macro tricks
Barret5Ocal
Barret Gaylor
21 posts

I am a game designer who recently became interested in coding in a handmade way. I am not that experienced, but I want to learn.

#7434 Post your C/C++ macro tricks
2 years, 10 months ago

This might not be the best place to ask this but since we are all talk about it, what would be a good resource for learning about how macros work along with the other pre compiler stuff?

Just some designer guy.
Evine
Daniel Rasmussen
7 posts
#7435 Post your C/C++ macro tricks
2 years, 10 months ago Edited by Daniel Rasmussen on June 29, 2016, 8:35 a.m.

A big pro-tip would be to set the -E compiler flag. That will send the preprocessed file to stdout, so you can inspect that the macros expands as you expect. Just be aware that everything becomes a massive file and C library includes of stdio.h will put a massive amount of code into that file.

So with that you can try the samples from this thread. Fundamentally macros is just text replacement. Which is easy to understand, but also easy to mess up.
iffy
Andre
15 posts
#7437 Post your C/C++ macro tricks
2 years, 10 months ago

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.
mrmixer
Simon Anciaux
597 posts
#7700 Post your C/C++ macro tricks
2 years, 10 months ago

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
Cranky
TM
13 posts
#7704 Post your C/C++ macro tricks
2 years, 10 months ago

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.
mrmixer
Simon Anciaux
597 posts
#7709 Post your C/C++ macro tricks
2 years, 10 months ago Edited by Simon Anciaux on July 23, 2016, 8:04 p.m. 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( ) + [ & ]( )
Pseudonym
Andrew Bromage
184 posts / 1 project

Research engineer, resident maths nerd (Erdős number 3).

#7942 Post your C/C++ macro tricks
2 years, 9 months ago Edited by Andrew Bromage on Aug. 8, 2016, 2:44 a.m.

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.

sub f{($f)[email protected]_;print"$f(q{$f});";}f(q{sub f{($f)[email protected]_;print"$f(q{$f});";}f});