C - X Macro Self Iteration / Expansion
Asked Answered
U

2

2

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.

Underbelly answered 30/1, 2023 at 17:20 Comment(8)
Why does this have to be a macro? It would be simple enough in normal code.Edgeworth
It has to be a macro.Underbelly
You do not have to do preprocessing with the C preprocessing. You can write any program, in a compiled or scripted language, to preprocess source code. Just write a script and be done with it; do not try to make a kludge with the C preprocessor.Geomorphic
Yes, that is a sensible thing to do. Write a preprocessor and parse things, I get it. Figured I'd ask if anyone had come to an elegant solution for something like this before.Underbelly
gustedt.gitlabpages.inria.fr/p99/p99-htmlEmpurple
How would you make the preprocessor break the iteration? The macro LETTERS_COPY is not a duplicate of LETTERS.Oligopoly
Yes, that's precisely the point. Self referring to LETTERS within LETTERS will not build, which is why I used a separate macro that contains the original letters in order to achieve this less than desirable solution.Underbelly
@Underbelly But none of your macros take arguments. Suppose both levels of expansion could be called LETTERS, and the recursion were allowed.You'd need an argument to distinguish the top level from the bottom one that just produces "ABCD".Awning
E
1

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!

Empurple answered 2/2, 2023 at 5:26 Comment(0)
U
0

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

Underbelly answered 31/1, 2023 at 1:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.