使用宏迭代生成函数声明

发布于 2024-10-23 19:03:45 字数 2109 浏览 1 评论 0原文

我正在尝试使用宏生成函数声明

/* 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

帅的被狗咬 2024-10-30 19:03:45

为了解决“FOO”问题,您可以根据变量参数包的数量选择不同的宏。这是第一个镜头:

// These need to be updated to handle more than three arguments:
#define PP_HAS_ARGS_IMPL2(_1, _2, _3, N, ...) N
#define PP_HAS_ARGS_SOURCE() MULTI, MULTI, ONE, ERROR

#define PP_HAS_ARGS_IMPL(...) PP_HAS_ARGS_IMPL2(__VA_ARGS__)
#define PP_HAS_ARGS(...)      PP_HAS_ARGS_IMPL(__VA_ARGS__, PP_HAS_ARGS_SOURCE())

#define FOO_ONE(x)     ONE_ARG:    x
#define FOO_MULTI(...) MULTI_ARG:  __VA_ARGS__

#define FOO_DISAMBIGUATE2(has_args, ...) FOO_ ## has_args (__VA_ARGS__)
#define FOO_DISAMBIGUATE(has_args, ...) FOO_DISAMBIGUATE2(has_args, __VA_ARGS__)
#define FOO(...) FOO_DISAMBIGUATE(PP_HAS_ARGS(__VA_ARGS__), __VA_ARGS__)

使用示例:(

FOO(1)     // replaced by ONE_ARG:   1
FOO(1, 2)  // replaced by MULTI_ARG: 1, 2

我将尝试重新审视它以清理它;我认为其中肯定有一些不必要的宏。我没有机会研究您描述的更广泛的问题,所以我不确定这是否也能解决这个问题。可能还有一种更简单的方法来解决这个问题......我对可变参数宏不是特别熟悉。)

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:

// These need to be updated to handle more than three arguments:
#define PP_HAS_ARGS_IMPL2(_1, _2, _3, N, ...) N
#define PP_HAS_ARGS_SOURCE() MULTI, MULTI, ONE, ERROR

#define PP_HAS_ARGS_IMPL(...) PP_HAS_ARGS_IMPL2(__VA_ARGS__)
#define PP_HAS_ARGS(...)      PP_HAS_ARGS_IMPL(__VA_ARGS__, PP_HAS_ARGS_SOURCE())

#define FOO_ONE(x)     ONE_ARG:    x
#define FOO_MULTI(...) MULTI_ARG:  __VA_ARGS__

#define FOO_DISAMBIGUATE2(has_args, ...) FOO_ ## has_args (__VA_ARGS__)
#define FOO_DISAMBIGUATE(has_args, ...) FOO_DISAMBIGUATE2(has_args, __VA_ARGS__)
#define FOO(...) FOO_DISAMBIGUATE(PP_HAS_ARGS(__VA_ARGS__), __VA_ARGS__)

Usage example:

FOO(1)     // replaced by ONE_ARG:   1
FOO(1, 2)  // replaced by MULTI_ARG: 1, 2

(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.)

欲拥i 2024-10-30 19:03:45

P99 有一个宏可以完全满足您的要求,我认为,即 P99_PROTOTYPE。它有一个“签名”,其中

P99_PROTOTYPE(RT, NAME [, AT]*)

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" of

P99_PROTOTYPE(RT, NAME [, AT]*)

where RT is the return type (may be void) and AT are the argument types. The list of argument types may be empty, in which case it is substituted by void.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文