jeudi 30 avril 2015

Understanding DEFER and OBSTRUCT macros

I created a small macro metaprogramming library that implements basic useful constructs such as REPEAT(times, x), IF(value, true, false), tuples, and more.

Most of my implementations work by overloading macros based upon their variadic argument count or through a counter:

// Example:
#define REPEAT_0(x) 
#define REPEAT_1(x) x REPEAT_0(x) 
#define REPEAT_2(x) x REPEAT_1(x)
#define REPEAT_3(x) x REPEAT_2(x)
// ...
// (these defines are generated using an external script)
// ...

#define REPEAT(count, x) CAT(REPEAT_, count)(x)

This works fine, but I've recently come across an extremely interesting implementation of macro recursion by Paul Fultz.

Up until the deferred expression section I had no trouble understanding his article.

I am, however, having a lot of trouble understanding the use of DEFER and OBSTRUCT properly.

Paul implements a very elegant version of REPEAT that does not require script-generated defines like this:

#define EAT(...)
#define EXPAND(...) __VA_ARGS__
#define WHEN(c) IF(c)(EXPAND, EAT)

#define REPEAT(count, macro, ...) \
    WHEN(count) \
    ( \
        OBSTRUCT(REPEAT_INDIRECT) () \
        ( \
            DEC(count), macro, __VA_ARGS__ \
        ) \
        OBSTRUCT(macro) \
        ( \
            DEC(count), __VA_ARGS__ \
        ) \
    )
#define REPEAT_INDIRECT() REPEAT

//An example of using this macro
#define M(i, _) i
EVAL(REPEAT(8, M, ~)) // 0 1 2 3 4 5 6 7

DEFER, OBSTRUCT and other utilities are implemented as such:

#define EMPTY()
#define DEFER(id) id EMPTY()
#define OBSTRUCT(...) __VA_ARGS__ DEFER(EMPTY)()
#define EXPAND(...) __VA_ARGS__

#define A() 123
A() // Expands to 123
DEFER(A)() // Expands to A () because it requires one more scan to fully expand
EXPAND(DEFER(A)()) // Expands to 123, because the EXPAND macro forces another scan


  • When the preprocessor expands a macro, the result is "painted" until the next scan - it will not expand recursively unless an additional scan occurs. Is this correct?

  • Does the EXPAND(...) macro force an additional scan? If so, does this scan allow macros to expand recursively? What's the difference btween EXPAND(...) and DEFER(id)?

    • Does DEFER force two additional scans?
  • What about the OBSTRUCT(...) macro? Does it force two additional scans?

  • Now - why is OBSTRUCT required in the recursive implementation of REPEAT? Why wouldn't DEFER or EXPAND work here?

Aucun commentaire:

Enregistrer un commentaire