如何减少二维 _Generic 中第二个参数的扩展次数?

Kam*_*Cuk 5 c generics c11

我有以下代码:

int add_ii(int a, int b) { return a + b; }
unsigned add_iu(int a, unsigned b) { return a + b; }
unsigned add_ui(unsigned a, int b) { return a + b; }
unsigned add_uu(unsigned a, unsigned b) { return a + b; }

#define add(LEFT, RIGHT) \
    _Generic(LEFT \
        ,int: _Generic(RIGHT \
           ,int:      add_ii \
           ,unsigned: add_iu \
        ) \
        ,unsigned: _Generic(RIGHT \
           ,int:      add_ui \
           ,unsigned: add_uu \
        ) \
    )(LEFT, RIGHT)

int main() {
     return add(1, add(2, add(3, 4)));
}
Run Code Online (Sandbox Code Playgroud)

问题是RIGHT每个_Generic案例都会扩展。上面RIGHT使用了3次,所以add(2, add(3, 4))完全展开了3次,所以add(3, 4)展开了9次。随着每个嵌套使用以及内部处理的每个附加类型,这个数字呈指数级增长_Generic。这本身不是问题 - 但它会导致预处理器生成非常大的输出,从而显着拖延编译过程并导致非常高的编译内存使用量。

有没有什么方法可以写成二维_Generic,这样就RIGHT不会每次都对每种类型进行扩展?

切换LEFTwithRIGHT当然不是一个选项,也不会起多大作用 -LEFT然后会扩展很多次。

我知道我可以使用 GNU 扩展,但我的想法是我不想使用它们:

#define add(LEFT, RIGHT0)  ({
    __auto_type LEFT = LEFT0;
    __auto_type RIGHT = RIGHT0;
    _Generic(LEFT, .....)(LEFT, RIGHT)
})
Run Code Online (Sandbox Code Playgroud)

我可以编写一种映射,但这会删除返回类型信息,并要求将所有函数类型转换为通用类型,或者使用一些不安全的类型va_arg

unsigned add_ii_adaptor(unsigned a, unsigned b) { return add_ii(a, b); }
unsigned add_iu_adaptor(unsigned a, unsigned b) { return add_iu(a, b); }
unsigned add_ui_adaptor(unsigned a, unsigned b) { return add_ui(a, b); }
static unsigned (*const funcarr[])(unsigned a, unsigned b) = {
    add_ii_adaptor,
    add_iu_adaptor,
    add_ui_adaptor,
    add_uu,
};
#define typeidx(x)  _Generic((x), int: 0, unsigned 1)
#define add(LEFT, RIGHT)  \
     funcarr[typeidx(LEFT) << 1 | typeidx(RIGHT)](LEFT, RIGHT)
Run Code Online (Sandbox Code Playgroud)

有没有我忽略的方法?

背景:However, what is the ultimate point here?在这里实现没有 GNU 扩展的整数安全 n2792。我想要支持 26 种类型,使其成为 17576 种类型组合。(尽管如此,这可以减少)。在上面的示例中,返回类型是操作数的提升类型,因此,但例如它可能是。ckd_add(x, y, z)unsigned + int = unsignedlong + char = long *or unsigned long!*

tst*_*isl 5

可以将_Generic元组映射到整数。您需要一个由整数参数化的类型,而 C 提供了这样一个系列...数组。但是,数组不能用作分派参数,因为数组会衰减为指针。然而,指向数组的指针不会衰减。

只需使用 -like 宏计算数组的大小typeidx,并使用 is 作为新数组类型的大小。添加是1因为 C 禁止零大小数组。

接下来使用复合文字形成指向它的指针。等式(int(*)[3]) { 0 }。最后,使用此文字的类型来分派正确的函数指针。

#define TYPE_TO_NUM(X) _Generic((X), int: 0, unsigned: 1)

#define add(LEFT, RIGHT) \
    _Generic(        \
        (int(*)[1 + 2 * TYPE_TO_NUM(LEFT) + TYPE_TO_NUM(RIGHT)]) { 0 } \
        ,int(*)[1]: add_ii \
        ,int(*)[2]: add_iu \
        ,int(*)[3]: add_ui \
        ,int(*)[4]: add_uu \
        )(LEFT, RIGHT)
Run Code Online (Sandbox Code Playgroud)

由于扩展LEFTRIGHT两倍,该解决方案仍然具有指数复杂度,但比原始解决方案快得多。