morrantho
morrantho

Reputation: 21

C - X Macro Self Iteration / Expansion

I am curious if any of you can think of a way upon macro expansion to repeat the macro itself. Here is an incredibly small scale version of an overall bigger problem:

#include<stdio.h>

#define LETTERS\
    X(A)\
    X(B)\
    X(C)\
    X(D)

#define X(L) #L

int main(int nargs,char** args)
{
    printf("%s\n",LETTERS);
    return 0;
}

Output: ABCD

Desired output: AABCD BABCD CABCD DABCD

The desired output is clearly similar to a nested (N^2) loop over whatever input data.

The stringification doesn't matter, it's only there for compilation.

There are some obvious and some not so obvious solutions to the desired output.

One is to make a complete copy of the macro, then between each X element, you simply refer to the copy. This would be wasteful and I don't want to do it. Obviously you can't refer to the macro itself due to recursion. I have made many attempts to find a decent solution, and won't list all of them as it would take up way too much time. I am open to solutions that use other macros to repeat or expand the original, or solutions that use janky forms of recursion.

#include<stdio.h>

#define LETTERS_COPY\
    X(A)\
    X(B)\
    X(C)\
    X(D)

#define LETTERS\
    X(A)\
    LETTERS_COPY\
    X(B)\
    LETTERS_COPY\
    X(C)\
    LETTERS_COPY\
    X(D)\
    LETTERS_COPY

#define X(L) #L

int main(int nargs,char** args)
{
    printf("%s\n",LETTERS);
    return 0;
}

Again, this builds just fine and works, but requires a complete duplicate of the original data, interjecting itself between each X element.

Upvotes: 2

Views: 186

Answers (2)

jxh
jxh

Reputation: 70392

If you use an iterating macro, like how one is defined in the P99 macro utility collection, then it becomes much easier to solve.

Since we intend to define an iterating macro, we don't need X-macros on the letters anymore.

#define LETTERS \
         A \
        ,B \
        ,C \
        ,D

Below is a simplified iterating macro that supports up to 5 arguments. Hopefully you see how to extend the implementation if you need more.

#define XX(X, ...) \
        XX_X(__VA_ARGS__, XX_5, XX_4, XX_3, XX_2, XX_1) \
        (X, __VA_ARGS__)
#define XX_X(_1,_2,_3,_4,_5,X,...) X
#define XX_1(X, _)      X(_)
#define XX_2(X, _, ...) X(_) XX_1(X, __VA_ARGS__)
#define XX_3(X, _, ...) X(_) XX_2(X, __VA_ARGS__)
#define XX_4(X, _, ...) X(_) XX_3(X, __VA_ARGS__)
#define XX_5(X, _, ...) X(_) XX_4(X, __VA_ARGS__)

So, if you invoke XX(X, LETTERS), it will expand into the X-macro version of LETTERS you had before.

The magic of the XX() macro is the meta nature of the XX_X() macro, which selects the right numeric macro to use. The numeric macro is passed in reverse order to the XX_X() macro when it is invoked by XX(). This makes it so that XX_X() selects a lower numeric macro if __VA_ARGS__ contains fewer arguments.

Now, we create a macro to turn the argument into a string:

#define STR(X) STR_(X)
#define STR_(X) #X

This allows you to easily create the string with all your letters. And printing your iterative output just needs another macro.

int main () {
    const char *letters = XX(STR, LETTERS);
    #define LETTERS_PRINT(X) printf("%s%s\n", #X, letters);
    XX(LETTERS_PRINT, LETTERS)
}

We see that the solution applies XX() twice. Once to create the string of all your letters. Once to create the output, which is prepending each letter to the combined letters.

Try it online!

Upvotes: 1

morrantho
morrantho

Reputation: 21

For anyone wondering, the best I could do was make a second X macro that takes 2 args, and the outer list becomes a function macro that takes a single arg. This lets you pass data to the list, and to the second x macro, while still being able to unpack either X macro however you like.

#include<stdio.h>

#define _CAT(A,B) A##B
#define CAT(A,B) _CAT(A,B)

#define _STR(S) #S
#define STR(S) _STR(S)

#define LETTERS(L)\
    XX(L,X(A))\
    XX(L,X(B))\
    XX(L,X(C))\
    XX(L,X(D))

int main(int nargs,char** args)
{
    #define X(L) L
    #define XX(L1,L2) STR(CAT(L1,L2))
    printf("%s\n",LETTERS(A));
    printf("%s\n",LETTERS(B));
    printf("%s\n",LETTERS(C));
    printf("%s\n",LETTERS(D));
    #undef XX
    #undef X
    return 0;
}

enter image description here

Upvotes: 0

Related Questions