[英]Use C/C++ Macros to Generate Function Signature
I am attempting to use macros in C/C++ to generate some boiler-plate function declarations and definitions.我试图在 C/C++ 中使用宏来生成一些样板 function 声明和定义。
I would like a macro similar to:我想要一个类似于以下的宏:
DECLARE_FUNCTION(myFunction, int, A, int, B, char, C)
to generate the following code (please ignore the fact that this code seems pointless, it is just a simplified example)生成以下代码(请忽略此代码似乎毫无意义的事实,这只是一个简化的示例)
void myFunction(int A, int B, char C) {
myFunction_PROXY((Variant[4]){Variant(A), Variant(B), Variant(C), Variant()});
}
void myFunction_PROXY(const Variant (&args)[4]) {
myFunction_HANDLER(args[0], args[1], args[2]);
}
void myFunction_HANDLER(int A, int B, char C) {
}
Notice how I need both the function signature (with types and names) as well as just the names to be used in the Variant() constructors.请注意,我需要 function 签名(带有类型和名称)以及要在 Variant() 构造函数中使用的名称。 Thus, I would I assume I need to selectively "loop" through the VA_ARGS value of the macro to get different combinations of arguments and apply them in different ways.因此,我假设我需要有选择地“循环”通过宏的VA_ARGS值来获得 arguments 的不同组合并以不同的方式应用它们。
Where I already have a class named Variant defined.我已经定义了一个名为 Variant 的 class 。
From my research, I believe this involves some combination of "recursive macros" however, I can't seem to understand how to get my desired output.根据我的研究,我相信这涉及“递归宏”的某种组合,但是,我似乎无法理解如何获得我想要的 output。
Would anyone be able to help or at least point me towards a good explanation on how recursive macros in C work?任何人都可以帮助或至少指出我对 C 中的递归宏如何工作的一个很好的解释吗?
Thanks谢谢
Disclaimer 1:免责声明一:
Honestly, I don't advise you do such machinery behind macros.老实说,我不建议你在宏后面做这样的机器。 They will become a massive maintenance burden, and the return is minimal.它们将成为巨大的维护负担,回报微乎其微。 If you can do this with other more maintainable abstractions, it would be better for you in the long run.如果您可以使用其他更易于维护的抽象来做到这一点,那么从长远来看对您来说会更好。
Disclaimer 2:免责声明2:
I am writing this without a compiler at the moment.我现在在没有编译器的情况下写这个。 I may have a syntax error, but these are the generic building blocks to make this solution.我可能有语法错误,但这些是制作此解决方案的通用构建块。
That disclaimer said, this can be done -- but not nicely.该免责声明说,这是可以做到的——但不是很好。
You have several problems here that need to be tackled with a lot of macro trickery:这里有几个问题需要用很多宏技巧来解决:
__VA_ARGS__ / 2
(at expansion time)您希望扩展包含__VA_ARGS__ / 2
的大小(在扩展时)__VA_ARGS__
to produce Variant(<arg 1>), Variant(<arg 3>), Variant(<arg 5>), Variant()
您希望__VA_ARGS__
的扩展产生Variant(<arg 1>), Variant(<arg 3>), Variant(<arg 5>), Variant()
__VA_ARGS__
to produce Variant[size]{args[0], args[1], args[2], ...}
您希望__VA_ARGS__
的扩展产生Variant[size]{args[0], args[1], args[2], ...}
To start with, I'm going to create a helper called JOIN
:首先,我将创建一个名为JOIN
的助手:
#define JOIN(a, b) JOIN_H(a, b)
#define JOIN_H(a, b) a ## b
This may seem silly, but what it actually does is ensures that macros being joined together will be evaluated before they get joined -- so that macro functions being called will properly instantiate a join with their result rather than the full name.这可能看起来很傻,但它实际上做的是确保被连接在一起的宏将在它们被连接之前被评估——这样被调用的宏函数将正确地用它们的结果而不是全名来实例化一个连接。
__VA_ARGS__ / 2
获取__VA_ARGS__ / 2
的大小Getting the size of __VA_ARGS__
usually requires two macros:获取__VA_ARGS__
的大小通常需要两个宏:
__VA_ARGS__, N, N-1, N-2, ...
into a helper macro, and将__VA_ARGS__, N, N-1, N-2, ...
传递给辅助宏的方法,以及N
at the end.另一个在最后提取N
Something like:就像是:
#define COUNT_VA_ARGS(...) \
COUNT_VA_ARGS_H(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define COUNT_VA_ARGS_H(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N,...) N
This works because the first one passes all the arguments of __VA_ARGS__
and counts backwards from the Nth number, and we then extract N
.这是有效的,因为第一个通过了 __VA_ARGS__ 的所有__VA_ARGS__
并从第 N 个数字向后计数,然后我们提取N
。
In your case, you want the __VA_ARGS__ / 2
, so you will need to double those arguments在您的情况下,您需要__VA_ARGS__ / 2
,因此您需要将 arguments 加倍
#define COUNT_VA_ARGS(...) \
COUNT_VA_ARGS_H(__VA_ARGS__, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0)
#define COUNT_VA_ARGS_H(_1, _1, _2, _2, _3, _3, _4, _4, _5, _5, _6, _6, _7, _7, _8, _8, _9, _9, _10, _10, N,...) N
__VA_ARGS__
produce Wrap(<arg 1>), Wrap(<arg 3>), ...
使__VA_ARGS__
产生Wrap(<arg 1>), Wrap(<arg 3>), ...
Unlike C++ variadic templates, macros are not able to have expansion expressions where you can wrap each argument.与 C++ 可变参数模板不同,宏不能具有可以包装每个参数的扩展表达式。 To simulate this in Macros, you pretty much have to have N expansions explicitly defined, and then to call this, you will need to combine the result of one macro to call it.要在宏中模拟这一点,您几乎必须明确定义 N 个扩展,然后调用它,您需要组合一个宏的结果来调用它。
#define WRAP_VA_ARGS_0(wrap)
#define WRAP_VA_ARGS_1(wrap,x0) wrap(x0)
...
#define WRAP_VA_ARGS_10(wrap,x0,x1, ..., x10) wrap(x0), wrap(x1), ..., wrap(x10)
// Call into one of the concrete ones above
#define WRAP_VA_ARGS(wrap, __VA_ARGS__) JOIN(WRAP_VA_ARGS_, COUNT_VA_ARGS(__VA_ARGS__))(__VA_ARGS__)
Since the expression actually wants every other argument in it, you will again need to double the arguments:由于表达式实际上需要其中的所有其他参数,因此您将再次需要将 arguments 加倍:
#define WRAP_VA_ARGS_0(wrap)
#define WRAP_VA_ARGS_1(wrap,x0type,x0) wrap(x0)
#define WRAP_VA_ARGS_2(wrap,x0type,x0,x1type,x1) wrap(x0), wrap(x1)
...
Calling WRAP_VA_ARGS(Variant, int, A, float, B)
will now create Variant(A), Variant(B)
调用WRAP_VA_ARGS(Variant, int, A, float, B)
现在将创建Variant(A), Variant(B)
Similar to the above wrapping, you will need to find a way to generate a list of numbers, and wrap it.与上面的包装类似,您需要找到一种方法来生成数字列表并将其包装。 Again this must delegate to the counting wrapper同样,这必须委托给计数包装器
#define WRAP_COUNT_VA_ARGS_0(wrap)
#define WRAP_COUNT_VA_ARGS_1(wrap) wrap[0]
#define WRAP_COUNT_VA_ARGS_2(wrap) wrap[0], wrap[1]
...
#define WRAP_COUNT_VA_COUNT_ARGS(wrap, ...) JOIN(WRAP_COUNT_VA_ARGS_, COUNT_VA_ARGS(__VA_ARGS))(wrap)
Calling WRAP_COUNT_VA_COUNT_ARGS(args, int, A, float, B)
should generate args[0], args[1]
调用WRAP_COUNT_VA_COUNT_ARGS(args, int, A, float, B)
应该生成args[0], args[1]
Trigger warning: this is going to be ugly触发警告:这将是丑陋的
#define DECLARE_FUNCTION(name, ...) \
void name(__VA_ARGS__) { \
JOIN(name, _PROXY)((Variant[COUNT_VA_ARGS(__VA_ARGS__)+1]) {WRAP_VA_ARGS(Variant,__VA_ARGS__), Variant()}); \
} \
void JOIN(name, _PROXY)(const Variant (&args)[COUNT_VA_ARGS(__VA_ARGS__) + 1]) { \
JOIN(name, _HANDLER)(WRAP_COUNT_VA_COUNT_ARGS(args, __VA_ARGS__)); \
} \
void JOIN(name, _HANDLER)(__VA_ARGS__) { \
\
}
With any luck, an example of DECLARE_FUNCTION(myFunction, int, A, int, B, char, C)
should produce:运气好的话, DECLARE_FUNCTION(myFunction, int, A, int, B, char, C)
的示例应该产生:
void myFunction(int A, int B, char C) {
myFunction_PROXY((Variant[3+1]{Variant(A), Variant(B), Variant(C), Variant()});
}
void myFunction_PROXY(const Variant (&args)[3+1]) {
myFunction_HANDLER(args[0], args[1], args[2]);
}
void myFunction_HANDLER(int A, int B, char C) {
}
Note: the array is created by the constant expression 3 + 1
, since we need do this arithmetic to account for the Variant()
at the end of myFunction_PROXY
's call注意:数组是由常量表达式3 + 1
创建的,因为我们需要做这个算术来解释myFunction_PROXY
调用结束时的Variant()
Don't do macros.不要做宏。 Macros are bad, mmmm'kay?宏很糟糕,嗯?
Iterating over comma-separated lists with preprocessor requires writing boilerplate macros.使用预处理器迭代逗号分隔的列表需要编写样板宏。
Normally you must write or generate at least O(n)
macros to process list up to n
elements long.通常,您必须编写或生成至少O(n)
宏来处理长达n
元素的列表。 @Human-Compiler's answer does it with O(n 2 )
. @Human-Compiler 的答案是O(n 2 )
。
You can get similar macros from Boost.Preprocessor, or use it as an inspiration.您可以从 Boost.Preprocessor 获得类似的宏,或者将其用作灵感。
Or you can use a different syntax for your list:或者,您可以为列表使用不同的语法:
DECLARE_FUNCTION(myFunction, (int,A)(int,B)(char,C))
Then you can process lists of any size, with a fixed amount of macros:然后,您可以使用固定数量的宏处理任何大小的列表:
#define DECLARE_FUNCTION(func_, seq_) \
void myFunction(END(PARAMS_LOOP_0 seq_)) { \
myFunction_PROXY(
(Variant[1 END(COUNT_LOOP_A seq_)]){END(VAR_LOOP_A seq_) Variant()}); \
} \
void myFunction_PROXY(const Variant (&args)[1 END(COUNT_LOOP_A seq_)]) { \
const int x = __COUNTER__+1; \
myFunction_HANDLER(END(ARR_LOOP_0 seq_)); \
} \
void myFunction_HANDLER(END(PARAMS_LOOP_0 seq_)) {}
#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_END
#define PARAMS_LOOP_0(type_, name_) PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
#define PARAMS_LOOP_A(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_B
#define PARAMS_LOOP_B(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
#define PARAMS_LOOP_0_END
#define PARAMS_LOOP_A_END
#define PARAMS_LOOP_B_END
#define PARAMS_LOOP_BODY(type_, name_) type_ name_
#define COUNT_LOOP_A(...) COUNT_LOOP_BODY COUNT_LOOP_B
#define COUNT_LOOP_B(...) COUNT_LOOP_BODY COUNT_LOOP_A
#define COUNT_LOOP_A_END
#define COUNT_LOOP_B_END
#define COUNT_LOOP_BODY +1
#define VAR_LOOP_A(type_, name_) VAR_LOOP_BODY(name_) VAR_LOOP_B
#define VAR_LOOP_B(type_, name_) VAR_LOOP_BODY(name_) VAR_LOOP_A
#define VAR_LOOP_A_END
#define VAR_LOOP_B_END
#define VAR_LOOP_BODY(name_) Variant(name_),
#define ARR_LOOP_0(...) ARR_LOOP_BODY ARR_LOOP_A
#define ARR_LOOP_A(...) , ARR_LOOP_BODY ARR_LOOP_B
#define ARR_LOOP_B(...) , ARR_LOOP_BODY ARR_LOOP_A
#define ARR_LOOP_A_END
#define ARR_LOOP_B_END
#define ARR_LOOP_BODY args[__COUNTER__-x]
With those macros, DECLARE_FUNCTION(myFunction, (int,A)(int,B)(char,C))
expands to:使用这些宏, DECLARE_FUNCTION(myFunction, (int,A)(int,B)(char,C))
扩展为:
void myFunction(int A, int B, char C)
{
myFunction_PROXY((Variant[1+1+1+1]){Variant(A), Variant(B), Variant(C), Variant()});
}
void myFunction_PROXY(const Variant (&args)[1+1+1+1])
{
const int x = 0+1;
myFunction_HANDLER(args[1-x], args[2-x], args[3-x]);
}
void myFunction_HANDLER(int A, int B, char C) {}
Note the use __COUNTER__
.注意使用__COUNTER__
。 It's not a part of the standard C++, but the major compilers support it as an extension.它不是标准 C++ 的一部分,但主要编译器支持它作为扩展。 You don't have any other options for getting consecutive array indices, other than writing boilerplate macros.除了编写样板宏之外,您没有任何其他选择来获取连续的数组索引。
I've found extremely useful all your reply;我发现您的所有回复都非常有用; in my case I had the need to protect a set of routines implemented as part of an old application.就我而言,我需要保护一组作为旧应用程序的一部分实现的例程。
Constraints: mutex, minimize changes in the code.约束:互斥,尽量减少代码的变化。
The "MTX_DB_PROTECTED_FUNCTION" macro works great. “MTX_DB_PROTECTED_FUNCTION”宏效果很好。
#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_END
#define PARAMS_LOOP_0(type_, name_) PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
#define PARAMS_LOOP_A(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_B
#define PARAMS_LOOP_B(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
#define PARAMS_LOOP_0_END
#define PARAMS_LOOP_A_END
#define PARAMS_LOOP_B_END
#define PARAMS_LOOP_BODY(type_, name_) type_ name_
#define VAR_LOOP_0(type_, name_) VAR_LOOP_BODY(type_, name_) VAR_LOOP_A
#define VAR_LOOP_A(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_B
#define VAR_LOOP_B(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_A
#define VAR_LOOP_0_END
#define VAR_LOOP_A_END
#define VAR_LOOP_B_END
#define VAR_LOOP_BODY(type_, name_) name_
//https://stackoverflow.com/questions/62903631/use-c-c-macros-to-generate-function-signature
#define MTX_DB_PROTECTED_FUNCTION(type_, func_, seq_) \
\
static type_ _s_mtx_##func_##_protected(END(PARAMS_LOOP_0 seq_));\
\
type_ func_(END(PARAMS_LOOP_0 seq_))\
{\
UTL_AcquireMutex(__FUNCTION__, &g_h_dataFileMutex, OSL_TIMEOUT_INFINITE);\
type_ ret = _s_mtx_##func_##_protected(END(VAR_LOOP_0 seq_));\
UTL_ReleaseMutex(__FUNCTION__, &g_h_dataFileMutex);\
return ret;\
}\
\
\
static type_ _s_mtx_##func_##_protected(END(PARAMS_LOOP_0 seq_))
Sample样本
Original func原始功能
int dummyfunc(char TabId, char checksum)
{
return 0;
}
Macro insertion宏插入
MTX_DB_PROTECTED_FUNCTION(int, dummyfunc, (char,TabId)(char,checksum))
{
return 0;
}
Macro expansion宏扩展
static int _s_mtx_dummyfunc_protected(char TabId , char checksum );
int dummyfunc(char TabId , char checksum )
{
UTL_AcquireMutex(__FUNCTION__, &g_h_dataFileMutex, (unsigned long)(-1));
int ret = _s_mtx_dummyfunc_protected(TabId , checksum );
UTL_ReleaseMutex(__FUNCTION__, &g_h_dataFileMutex);
return ret;
}
static int _s_mtx_dummyfunc_protected(char TabId , char checksum )
{
return 0;
}
For no-params function对于无参数 function
MTX_DB_PROTECTED_FUNCTION(int, dummyWoParams,(,))
{
Based upon many of the other answers and comments, here is what I cam up with that solves my particular problem:基于许多其他答案和评论,这是我想出的解决我的特定问题的方法:
#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_END
#define PARAMS_LOOP_0(type_, name_) PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
#define PARAMS_LOOP_A(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_B
#define PARAMS_LOOP_B(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
#define PARAMS_LOOP_0_END
#define PARAMS_LOOP_A_END
#define PARAMS_LOOP_B_END
#define PARAMS_LOOP_BODY(type_, name_) type_ name_
#define VAR_LOOP_0(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_A
#define VAR_LOOP_A(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_B
#define VAR_LOOP_B(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_A
#define VAR_LOOP_0_END
#define VAR_LOOP_A_END
#define VAR_LOOP_B_END
#define VAR_LOOP_BODY(type_, name_) name_
//------------------------------------------------------------------------------
#define DEFINE_SLOT(func_, ...) \
void func_(END(PARAMS_LOOP_0 __VA_ARGS__)) { invoke(this, &func_##_SLOT END(VAR_LOOP_0 __VA_ARGS__)); }\
void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__))
#define DECLARE_SLOT(func_, ...) \
void func_(END(PARAMS_LOOP_0 __VA_ARGS__));\
void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__))
#define DECLARE_VIRTUAL_SLOT(func_, ...) \
virtual void func_(END(PARAMS_LOOP_0 __VA_ARGS__));\
virtual void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__))
#define DECLARE_SLOT_INTERFACE(func_, ...) \
virtual void func_(END(PARAMS_LOOP_0 __VA_ARGS__)) = 0;\
virtual void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__)) = 0
#define DECLARE_SLOT_OVERRIDE(func_, ...) \
void func_(END(PARAMS_LOOP_0 __VA_ARGS__)) override;\
void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__)) override
I have adopted some of Qt's naming conventions by calling these "slots."我通过调用这些“插槽”来采用 Qt 的一些命名约定。
These macros would be used as follows:这些宏将按如下方式使用:
//In the .h file
class MyBase {
public:
DECLARE_SLOT_INTERFACE(foo, (int, a) (int, b));
};
class MyClass : public MyBase {
public:
DECLARE_SLOT_OVERRIDE(foo, (int, a) (int, b));
DECLARE_SLOT(bar, (bool, a) (const MyClass& obj));
};
//In the .cpp file
DEFINE_SLOT(MyClass::foo, (int, a) (int, b)) {
}
DEFINE_SLOT(MyClass::bar, (bool, a) (const MyClass& obj)) {
}
Not very clear what it variable into your case, you can do what you are looking for with __VA_ARGS__
不太清楚它会影响你的情况,你可以用__VA_ARGS__
做你正在寻找的东西
#define DECLARE_FUNCTION(myFunction, ...) \
static void myFunction##_HANDLER(__VA_ARGS__) { \
\
} \
\
static void myFunction##_PROXY(const Variant (&args)[4]) { \
myFunction##_HANDLER(args[0], args[1], args[2]); \
} \
\
static void myFunction(__VA_ARGS__) { \
myFunction##_PROXY((Variant[4]){Variant(A), Variant(B), Variant(C), Variant()}); \
}
DECLARE_FUNCTION(myFunction, int A, int B, char C)
Will generate会产生
static void myFunction_HANDLER(int A, int B, char C) { }
static void myFunction_PROXY(const Variant (&args)[4]) {
myFunction_HANDLER(args[0], args[1], args[2]);
}
static void myFunction(int A, int B, char C) {
myFunction_PROXY((Variant[4]){Variant(A), Variant(B), Variant(C), Variant()});
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.