使用宏迭代生成函数声明
我正在尝试使用宏生成函数声明
/* goal: generate int f(int a, float b) */
template<typename P>
struct ptype;
template<typename P>
struct ptype<void(P)> { typedef P type; };
#define NAMEe
#define COMMAe
#define COMMA ,
#define NAME(N) N PARAMS
#define PARAMS(P, ...) COMMA ## __VA_ARGS__ P NAME ## __VA_ARGS__
#define PARAM_ITER(P) P NAME
#define PROTO(R, N, P) \
ptype<void R>::type N (PARAM_ITER P (,e))
PROTO((int), f, (int)(a)(float)(b));
它将通过 NAME
或 < 迭代处理下一个 (name)
或 (type)
分别为 code>PARAMS,其中 ...
具有空宏参数。但是 GCC 抱怨
prototype.hpp:20:35: warning: ISO C99 requires rest arguments to be used
And clang 抱怨
ptype<void (int)>::type f (int aprototype.hpp:20:1: warning: varargs argument missing, but tolerated as an extension [-pedantic]
我认为发生这种情况是因为以下原因
#define FOO(X, ...)
FOO(A);
因为我没有传递 ...
或每个 (name)
的参数或(类型)
。有什么简单的解决办法我可以申请吗?
我现在使用了与 @James 使用的技术类似的技术来查找参数列表的长度。如果作为第二个参数传递 ONT
,而不是 O
,我将打印逗号和 NAME
。最终解决方案如下:
/* goal: generate void f(int a, float b) */
template<typename P>
struct ptype;
template<typename P>
struct ptype<void(P)> { typedef P type; };
#define TYPE_DO(X) X
#define TYPE_DONT(X)
#define TYPE_MAYBE(X, A, ...) TYPE_D ## A (X)
#define COMMA_DO ,
#define COMMA_DONT
#define COMMA_MAYBE(A, B, ...) COMMA_D ## B
#define NAME_DO NAME
#define NAME_DONT
#define NAME_MAYBE(A, B, ...) NAME_D ## B
#define NAME(N) N PARAMS
#define PARAMS(...) COMMA_MAYBE(__VA_ARGS__,O,O) TYPE_MAYBE(__VA_ARGS__,O,O) \
NAME_MAYBE(__VA_ARGS__,O,O)
#define PARAM_ITER(P) P NAME
#define PROTO(R, N, P) \
ptype<void R>::type N (PARAM_ITER P (D,ONT))
测试:
#define STR1(X) #X
#define STR(X) STR1(X)
int main() {
// prints correctly
std::cout << STR(PROTO((int), f, (int)(a)(float)(b)));
}
I'm trying to generate a function declaration using a macro
/* goal: generate int f(int a, float b) */
template<typename P>
struct ptype;
template<typename P>
struct ptype<void(P)> { typedef P type; };
#define NAMEe
#define COMMAe
#define COMMA ,
#define NAME(N) N PARAMS
#define PARAMS(P, ...) COMMA ## __VA_ARGS__ P NAME ## __VA_ARGS__
#define PARAM_ITER(P) P NAME
#define PROTO(R, N, P) \
ptype<void R>::type N (PARAM_ITER P (,e))
PROTO((int), f, (int)(a)(float)(b));
It will iteratively process the next (name)
or (type)
by NAME
or PARAMS
respectively, with the ...
having an empty macro argument. But GCC complains with
prototype.hpp:20:35: warning: ISO C99 requires rest arguments to be used
And clang complains with
ptype<void (int)>::type f (int aprototype.hpp:20:1: warning: varargs argument missing, but tolerated as an extension [-pedantic]
I think this happens because of the following
#define FOO(X, ...)
FOO(A);
Because I'm not passing an argument for the ...
or each of those (name)
or (type)
. Is there any simple work around I can apply?
I've now used a technique similar to the technique used by @James to find the length of a parameter list. If as second argument, instead of O
, ONT
is passed, I will print the comma and NAME
. The following is the final solution:
/* goal: generate void f(int a, float b) */
template<typename P>
struct ptype;
template<typename P>
struct ptype<void(P)> { typedef P type; };
#define TYPE_DO(X) X
#define TYPE_DONT(X)
#define TYPE_MAYBE(X, A, ...) TYPE_D ## A (X)
#define COMMA_DO ,
#define COMMA_DONT
#define COMMA_MAYBE(A, B, ...) COMMA_D ## B
#define NAME_DO NAME
#define NAME_DONT
#define NAME_MAYBE(A, B, ...) NAME_D ## B
#define NAME(N) N PARAMS
#define PARAMS(...) COMMA_MAYBE(__VA_ARGS__,O,O) TYPE_MAYBE(__VA_ARGS__,O,O) \
NAME_MAYBE(__VA_ARGS__,O,O)
#define PARAM_ITER(P) P NAME
#define PROTO(R, N, P) \
ptype<void R>::type N (PARAM_ITER P (D,ONT))
Test:
#define STR1(X) #X
#define STR(X) STR1(X)
int main() {
// prints correctly
std::cout << STR(PROTO((int), f, (int)(a)(float)(b)));
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
为了解决“
FOO
”问题,您可以根据变量参数包的数量选择不同的宏。这是第一个镜头:使用示例:(
我将尝试重新审视它以清理它;我认为其中肯定有一些不必要的宏。我没有机会研究您描述的更广泛的问题,所以我不确定这是否也能解决这个问题。可能还有一种更简单的方法来解决这个问题......我对可变参数宏不是特别熟悉。)
To solve the "
FOO
" problem, you can select different macros depending on the arity of the variable arguments pack. Here's a first shot at that:Usage example:
(I'll try to revisit this to clean it up; I think there are definitely some unneccessary macros in there. I haven't had a chance to look into the broader problem that you describe, so I'm not sure if this solves that too. There may be a simpler way to solve that problem, too... I'm not particularly familiar with variadic macros. This preprocesses cleanly on mcpp.)
P99 有一个宏可以完全满足您的要求,我认为,即
P99_PROTOTYPE
。它有一个“签名”,其中RT
是返回类型(可能是void
),AT
是参数类型。参数类型列表可能为空,在这种情况下,它会被void
替换。请注意,P99 是为 C99 而不是为 C++ 制作的。如果您的参数包含逗号,您将遇到特别的困难。 C++ 的语法滥用标记
<
和>
作为模板的括号表达式,这对预处理器来说特别糟糕。 C 预处理器和 C++ 在语法层面上基本上是不兼容的语言。P99 通过检测宏在调用时接收的参数数量并对边界情况做出不同的反应来摆脱您所面临的困难。
P99 has a macro that does exactly what you want, I think, namely
P99_PROTOTYPE
. It has a "signature" ofwhere
RT
is the return type (may bevoid
) andAT
are the argument types. The list of argument types may be empty, in which case it is substituted byvoid
.Beware that P99 is made for C99 and not for C++. You'll encounter particular difficulties if your arguments contain commas. C++'s syntax abuse of tokens
<
and>
as bracketing expressions for templates is particularly bad for the preprocessor. C-preprocessor and C++ are basically incompatible languages on the syntax level.P99 gets away from the difficulties that you are facing by detecting the number of arguments that the macro receives on a call and reacts differently on the border cases.