Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
473 views
in Technique[技术] by (71.8m points)

recursion - How do I write a recursive for-loop "repeat" macro to generate C code with the CPP preprocessor?

I want to force the preprocessor to do some automatic code generation for me. I don't need much: just a simple for-loop that contains another for-loop.[1]

I've read all that I can about macro expansion, and no longer giggle when the blue paint comes up. On a good day I can even explain why one needs multiple layers of macros to generate a function name with token pasting. I've actually got the for-loop working. But when it comes to putting a loop within a loop, I'm reduced to sprinkling randomly with DEFER, EVAL and OBSTRUCT and hoping for the best.

I will not be deterred by calls to reason. I really do want to do this with the standard C preprocessor. I promise that regardless of outcome, neither I, my employer, nor my heirs will sue you for technological malpractice. I promise I won't allow anyone else to maintain the code, or even view it, without appropriate safety glasses. Pretend if you'd like that I'm just asking out of theoretical interest. Or that my only other option is using M4: for if recursive macros in CPP are kinky, certainly M4 is the whole chicken.

The best reference material I've found is a 9-year-old Usenet thread: http://comp.std.c.narkive.com/5WbJfCof/double-cpp-expansion

It starts off-topic, is somewhat petty and combative in tone, and is way over my head. But I think the answer I seek is in there somewhere.

The next best is documentation for a CPP abusing header called Cloak: https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms

It takes a somewhat different approach to iteration, and perhaps would serve my needs instead. But it's also a good overview.

Here's some cut-down code to show where I'm stuck.

repeat.h:

#define REPEAT(macro, times, start_n, next_func, next_arg, macro_args...) 
    _REPEAT_ ## times(macro, start_n, next_func, next_arg, ## macro_args)

#define REPEAT_ADD_ONE(macro, times, start_n, macro_args... )                    
    REPEAT(macro, times, start_n, _REPEAT_ADD_ONE, 0, ## macro_args)

#define _REPEAT_ADD_ONE(n, ignore...) _REPEAT_ADD_ONE_ ## n

#define _REPEAT_0(args...)  /* empty */
#define _REPEAT_1(macro, n, func, i, args...) macro(n, ## args) 
#define _REPEAT_2(m, n, f, i, a...) m(n, ## a); _REPEAT_1(m, f(n, i), f, i, ## a)
#define _REPEAT_3(m, n, f, i, a...) m(n, ## a); _REPEAT_2(m, f(n, i), f, i, ## a)
#define _REPEAT_4(m, n, f, i, a...) m(n, ## a); _REPEAT_3(m, f(n, i), f, i, ## a)
#define _REPEAT_5(m, n, f, i, a...) m(n, ## a); _REPEAT_4(m, f(n, i), f, i, ## a)
#define _REPEAT_6(m, n, f, i, a...) m(n, ## a); _REPEAT_5(m, f(n, i), f, i, ## a)
#define _REPEAT_7(m, n, f, i, a...) m(n, ## a); _REPEAT_6(m, f(n, i), f, i, ## a)
#define _REPEAT_8(m, n, f, i, a...) m(n, ## a); _REPEAT_7(m, f(n, i), f, i, ## a)
#define _REPEAT_9(m, n, f, i, a...) m(n, ## a); _REPEAT_8(m, f(n, i), f, i, ## a)
#define _REPEAT_10(m, n, f, i, a...) m(n, ## a); _REPEAT_9(m, f(n, i), f, i, ## a)

#define _REPEAT_ADD_ONE_0 1
#define _REPEAT_ADD_ONE_1 2
#define _REPEAT_ADD_ONE_2 3
#define _REPEAT_ADD_ONE_3 4
#define _REPEAT_ADD_ONE_4 5
#define _REPEAT_ADD_ONE_5 6
#define _REPEAT_ADD_ONE_6 7
#define _REPEAT_ADD_ONE_7 8
#define _REPEAT_ADD_ONE_8 9
#define _REPEAT_ADD_ONE_9 10
#define _REPEAT_ADD_ONE_10 11

#define _REPEAT_ADD_0(x) x
#define _REPEAT_ADD_1(x) _REPEAT_ADD_ONE(x)
#define _REPEAT_ADD_2(x) _REPEAT_ADD_1(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_3(x) _REPEAT_ADD_2(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_4(x) _REPEAT_ADD_3(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_5(x) _REPEAT_ADD_4(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_6(x) _REPEAT_ADD_5(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_7(x) _REPEAT_ADD_6(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_8(x) _REPEAT_ADD_7(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_9(x) _REPEAT_ADD_8(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_10(x) _REPEAT_ADD_9(_REPEAT_ADD_ONE(x))

sample.c:

#include "repeat.h"

#define INNER_MACRO(inner, outer) if (inner == outer) printf("Match
")
#define INNER_BLOCK  { if (inner == outer) printf("Match
"); }

#define OUTER_MACRO_INNER_MACRO(outer) REPEAT_ADD_ONE(INNER_MACRO, 3, 0, outer)
#define OUTER_BLOCK_INNER_MACRO { REPEAT_ADD_ONE(INNER_MACRO, 3, 0, outer); }
#define OUTER_MACRO_INNER_BLOCK(outer) REPEAT_ADD_ONE(INNER_BLOCK, 3, 0, outer)
#define OUTER_BLOCK_INNER_BLOCK { REPEAT_ADD_ONE(INNER_BLOCK, 3, 0, outer); }

void outer_macro_inner_macro() {
    REPEAT_ADD_ONE(OUTER_MACRO_INNER_MACRO, 2, 1);
}

void outer_macro_inner_block() {
    REPEAT_ADD_ONE(OUTER_MACRO_INNER_BLOCK, 2, 1);
}

void outer_block_inner_macro() {
    REPEAT_ADD_ONE(OUTER_BLOCK_INNER_MACRO, 2, 1);
}

void outer_block_inner_block() {
    REPEAT_ADD_ONE(OUTER_BLOCK_INNER_BLOCK, 2, 1);
}

In sample.c I've shown four variations that come close to what I want. But none are quite there. Here's what I get as output with "cpp sample.c > out.c; astyle out.c;"

void outer_macro_inner_macro() {
    REPEAT_ADD_ONE(INNER_MACRO, 3, 0, 1);
    REPEAT_ADD_ONE(INNER_MACRO, 3, 0, 2);
}

void outer_macro_inner_block() {
    REPEAT_ADD_ONE({ if (inner == outer) printf("Match
"); }, 3, 0, 1);
    REPEAT_ADD_ONE({ if (inner == outer) printf("Match
"); }, 3, 0, 2);
}

void outer_block_inner_macro() {
    {
        if (0 == outer) printf("Match
");
        if (1 == outer) printf("Match
");
        if (2 == outer) printf("Match
");
    }(1);
    {
        if (0 == outer) printf("Match
");
        if (1 == outer) printf("Match
");
        if (2 == outer) printf("Match
");
    }(2);
}

void outer_block_inner_block() {
    { {
            if (inner == outer) printf("Match
");
        }(0, outer);
        {
            if (inner == outer) printf("Match
");
        }(1, outer);
        {
            if (inner == outer) printf("Match
");
        }(2, outer);
    }(1);
    { {
            if (inner == outer) printf("Match
");
        }(0, outer);
        {
            if (inner == outer) printf("Match
");
        }(1, outer);
        {
            if (inner == outer) printf("Match
");
        }(2, outer);
    }(2);
}

And here's the output I want to get instead:

void desired_results() {
   {
       if (0 == 1) printf("Match
");
       if (1 == 1) printf("Match
");
       if (2 == 1) printf("Match
");
   };
   {
       if (0 == 2) printf("Match
");
       if (1 == 2) printf("Match
");
       if (2 == 2) printf("Match
");
   };
}

Essentially, I can get things to work if I use a block as the outer loop body, but not if I use a function-like macro. But I need to use a macro with arguments so that the loop bodies can use the loop counter as a constant instead of as a variable.

The problem with the "macro"-"macro" manner is that the inner recursive call to REPEAT_ADD_ONE() is not expanded. The answer would seem to be deferring the expansion of the inner loop until after the outer loop is created, and then forcing another pass that expands the inner loop. But for some reason my "random monkey" approach to that hasn't yet produced a solution...

[1] Understatement intended.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Vesa Karvonen's "Order" library/language can definitely do this for you. It implements unrestricted recursion and looping in the C preprocessor, and as a really cool bonus dresses it up with the nice concise syntax of a "proper" programming language (to clarify: this is not an alternative preprocessor, it just does a lot of token-pasting to keep its keywords short. It is still pure CPP).

It uses a rather different technique, converting your metaprograms to CPS and then passing them to a single loop construct that has potentially trillions of steps, and executes the metaprogram in a strictly linear fashion. Loops and recursive functions can therefore be nested as deeply as you like because they don't have separate drivers that need to interact and paint each other blue.

Yes really, someone implemented a full virtual machine and interpreter using CPP macros. It's intimidating.

(EDIT: try the archived version if Rosetta Code has stopped working for you too.)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...