具有0..n参数的跨平台可变参数宏

问题描述 投票:0回答:2

对于参数包,我需要一个宏,它可以采用任意数量的参数(实际类型),它可以跨平台工作。此代码与GCC,LLVM和MSVC很好地协作(在预处理器重新编写为support the ## sequence之后(参见行为4 [可变参数宏中的逗号]):

class A {};
class B: A {};
class C: A {};
class D: A {};

template<typename... Interfaces>
class Aggregator: public Interfaces... {
};

#define INNER(...) typedef Aggregator<__VA_ARGS__> AGG;
#define ENVIRONMENT(...) INNER(B, C, ## __VA_ARGS__)

ENVIRONMENT(D)

这里的问题是空参数情况(ENVIRONMENT())。由于我还不能使用C ++ 20(它带有__VA_OPT__()令牌序列,我必须找到一个最多需要C ++ 17的解决方案.GCC + LLVM对空参数列表没有问题,但是MSVC坚持在至少有一个参数可供逗号使用。

使这个结构与MSVC完全兼容需要什么?

更新:

It turns out the empty-parameter case doesn't work for GCC either: https://godbolt.org/z/1zKZO- .
c++ visual-c++ c-preprocessor template-meta-programming
2个回答
1
投票

这是一种实际完成你所要求的方法......我已经预定了这个与MSVC,gcc和clang一起工作(只使用gcc和clang,或者只是MSVC,会更简单)。

这实现了OPTIONAL,它期望一个元组(带括号的标记)作为第一个参数。当用一个空的第二个参数调用OPTIONAL时,它会扩展为空;否则,它将扩展到第一个参数的展开版本。最终的结果是类似于(但肯定不等同于)C ++ 20的__VA_OPT__

以下是OPTIONAL实现和支持宏:

#define GLUE(A,B) GLUE_C(GLUE_I,(A,B))
#define GLUE_C(A,B) A B
#define GLUE_I(A,B) A##B
#define FIRST(...) FIRST_C(FIRST_I,(__VA_ARGS__,))
#define FIRST_C(A,B) A B
#define FIRST_I(X,...) X
#define THIRD(...) THIRD_C(THIRD_CC,(THIRD_I,(__VA_ARGS__,,,)))
#define THIRD_C(A,B) A B
#define THIRD_CC(A,B) A B
#define THIRD_I(A,B,C,...) C
#define COUNT(...) COUNT_C(COUNT_I,(__VA_ARGS__,9,8,7,6,5,4,3,2,1,))
#define COUNT_C(A,B) A B
#define COUNT_I(_,_9,_8,_7,_6,_5,_4,_3,_2,X,...) X
#define DISCARD_ARGUMENTS(...)
#define OPTIONAL(APPLY_,...) \
   THIRD(GLUE(OPTIONAL_SHIFT_IF_1_IS_,COUNT(__VA_ARGS__)),\
         OPTIONAL_SINGLE_CASE,\
         APPLY_OPTION) \
   (APPLY_,__VA_ARGS__)
#define OPTIONAL_SHIFT_IF_1_IS_1 ,
#define OPTIONAL_SINGLE_CASE(APPLY_,...) \
   THIRD(OPTIONAL_SHIFT_TEST __VA_ARGS__ (0_UNLOCK), \
         DISCARD_ARGUMENTS, \
         APPLY_OPTION)(APPLY_,)
#define OPTIONAL_SHIFT_TEST(...) GLUE(OPTIONAL_APPLY_SHIFT_TEST_,FIRST(__VA_ARGS__))
#define OPTIONAL_APPLY_SHIFT_TEST_0_UNLOCK ,

#define APPLY_OPTION(A,...) APPLY_OPTION_C(APPLY_OPTION_I,A)
#define APPLY_OPTION_C(A,B) A B
#define APPLY_OPTION_I(...) __VA_ARGS__

核心机制是“间接第三宏观”;这里的想法是生成一个第一个参数,它应用一些“测试”,如果感兴趣的东西出现,它会生成一个逗号,它会在选择之前将第二个参数移动到第三个位置。

OPTIONAL使用了两次;如果有一个参数,则会进行下一阶段测试,以查看该参数是否没有令牌。这个测试在OPTIONAL_SHIFT_TEST(0_UNLOCK)之间注入参数的标记;如果没有令牌,则进行调用,此宏将生成一个创建移位逗号的对象宏。这种间接是有意的,允许括号在第一个参数中而没有错误检测(参见演示)。

What is required to make this construct also work fully with MSVC?

...内置于所有宏的间接层中的是“调用宏”;在这里,他们都有名字中的_C,拿两个参数AB,然后简单地扩展到A B;它们的用途总是将宏名称与宏参数集分开。那些地址是MSVC。如果我真的试图以MSVC为目标(无论出于何种原因),只需要一个这样的呼叫者;通过为每个宏集创建一个调用者,我们也可以为MSVC和gcc / clang做这个工作。 (ETA:THIRD需要两个调用者间接;一个用于第三个本身的不同参数,另一个用于正确解释扩展的第一个参数的逗号,因为这是THIRD宏的整个点)。

请注意,这不依赖于任何编译器特定的逗号省略技巧。

最后......在OPTIONAL到位的情况下,您需要做的就是:

#define INNER(...) typedef Aggregator<__VA_ARGS__> AGG;
#define ENVIRONMENT(...) INNER(B, C OPTIONAL((,),__VA_ARGS__) __VA_ARGS__)

Godbolt演示


0
投票

这样的事情怎么样?

template <class... X>
using INNER = Aggregator<B, C, X...>;

#define ENVIRONMENT(...) typedef INNER<__VA_ARGS__> AGG

ENVIRONMENT(); // Or ENVIRONMENT(D)

希望这可以帮助...

© www.soinside.com 2019 - 2024. All rights reserved.