C - X 宏自迭代/扩展

C - X Macro Self Iteration / Expansion

提问人:morrantho 提问时间:1/31/2023 更新时间:2/3/2023 访问量:136

问:

我很好奇你们中是否有人能想出一种宏扩展方法来重复宏本身。这是一个整体更大问题的令人难以置信的小规模版本:

#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;
}

输出: ABCD

期望输出:AABCD BABCD CABCD DABCD

所需的输出显然类似于任何输入数据上的嵌套 (N^2) 循环。

字符串化无关紧要,它仅用于编译。

对于所需的输出,有一些明显和一些不那么明显的解决方案。

一种是制作宏的完整副本,然后在每个 X 元素之间,您只需引用副本即可。这将是浪费,我不想这样做。显然,由于递归,您不能引用宏本身。我已经做了很多尝试来找到一个体面的解决方案,并且不会列出所有这些解决方案,因为这会占用太多时间。我对使用其他宏来重复或扩展原始宏的解决方案,或使用卡顿形式的递归的解决方案持开放态度。

#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;
}

同样,这构建得很好并且有效,但需要原始数据的完整副本,在每个 X 元素之间插入自己。

C X 宏

评论

0赞 Scott Hunter 1/31/2023
为什么这必须是一个宏?这在普通代码中足够简单。
0赞 morrantho 1/31/2023
它必须是一个宏。
2赞 Eric Postpischil 1/31/2023
您不必使用 C 预处理进行预处理。您可以用编译或脚本语言编写任何程序来预处理源代码。只需编写一个脚本并完成它;不要试图用 C 预处理器进行 kudge。
1赞 jxh 1/31/2023
gustedt.gitlabpages.inria.fr/p99/p99-html
1赞 Kaz 1/31/2023
@morrantho 但是你的宏都没有参数。假设可以调用两个扩展级别,并且允许递归。您需要一个参数来区分仅产生“ABCD”的顶层和底层。LETTERS

答:

0赞 morrantho 1/31/2023 #1

对于任何想知道的人,我能做的最好的事情就是制作第二个需要 2 个参数的 X 宏,而外部列表变成一个需要单个参数的函数宏。这样一来,您就可以将数据传递到列表和第二个 x 宏,同时仍然能够根据需要解压缩任一 X 宏。

#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

1赞 jxh 2/2/2023 #2

如果使用迭代宏,就像在 P99 宏实用程序集合中定义宏一样,那么求解起来就会容易得多。

由于我们打算定义一个迭代宏,因此我们不再需要字母上的 X 宏。

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

下面是一个简化的迭代宏,最多支持 5 个参数。如果您需要更多,希望您能看到如何扩展实现。

#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__)

因此,如果您调用 ,它将扩展到您之前拥有的 X-macro 版本。XX(X, LETTERS)LETTERS

宏的魔力在于宏的元性质,它选择正确的数字宏来使用。当 调用时,数字宏将以相反的顺序传递给宏。这样,如果包含较少的参数,则选择较低的数值宏。XX()XX_X()XX_X()XX()XX_X()__VA_ARGS__

现在,我们创建一个宏来将参数转换为字符串:

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

这使您可以轻松地创建包含所有字母的字符串。 打印迭代输出只需要另一个宏。

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

我们看到该解决方案适用两次。一次创建所有字母的字符串。一次创建输出,该输出将每个字母添加到组合字母之前。XX()

在线试用!