问题
我有一个方法来包装函数,用宏替换它们,这样我就可以记录调用和返回代码。下面是一个有效的例子:
int rc;
int foo(int a, int b);
int bar(int a, char *b, int *c);
void LogRet(char *fn, char *file, char *from, int ln, int ret)
{
printf("%s.%s.%d: %s() ret:%08xn", file, from, ln, fn, ret);
}
#define foo(args, ...) (rc = (foo)(args, ##__VA_ARGS__), LogRet("foo", __FILE__, __FUNCTION__, __LINE__, rc), rc)
#define bar(args, ...) (rc = (bar)(args, ##__VA_ARGS__), LogRet("bar", __FILE__, __FUNCTION__, __LINE__, rc), rc)
它所替代的函数的宏调用该函数,并记录函数名称、调用位置以及返回代码。封装任何函数都使用相同的语法,只需要在宏中替换函数名3次。我想做的是创建包装器宏,其中foo的宏重新定义类似于:
#define foo(args, ...) WRAPPPER(foo)
我了解字符串和双字符串的基本知识,但我甚至无法使用WRAPPER宏进行真正的函数调用。理想情况下,我想把它归结为一个WRAP(foo(语句。原因是,我有大约100个或更多的函数想要包装,它希望从一个简单的强制包含文件中简单地完成。我得出的结论是,这是不可能的,但我想在放弃这个想法之前,我应该先问一下。如果有什么不同的话,我会使用clang和vc++,但在任何编译器上都有它会很好,因为我调试了很多不同的系统。
乔纳森·勒夫勒答案的改编
由于我是新来的,我不确定这是否应该是一个单独的答案或编辑更新。这基本上是乔纳森·勒夫勒的答案。这是一个函数示例。虽然它调用的2个函数毫无意义,但目标是拥有一个可以用任何arg列表包装任何函数的宏。我的主要用途是在一个有问题的大型代码库中记录使用流。此外,我的原始样本有一个明显的弱点。通过将返回代码存储在全局中,如果不在TLS中进行繁琐的准备,它就不是线程安全的。全局现在被删除,宏不再使用序列运算符来返回值,它被保留并由日志记录函数返回。此外,正如Augurar在Jonathan的例子中指出和展示的那样。如果宏是在与函数声明相同的文件中声明的,则它需要括号。
#include <stdio.h>
#include <string.h>
int foo(int a, int b);
int bar(int a, char *b, int *c);
#if defined (_DEBUG) || defined (DEBUG)
// Short version of __FILE__ without path requires runtime parsing
#define __SFILE__ (strrchr(__FILE__, '\') ? strrchr(__FILE__, '\') + 1 : __FILE__)
#ifdef WIN32
#define WRAPPER(func, ...) LogRet(#func, __SFILE__, __FUNCTION__, __LINE__, (func)(__VA_ARGS__))
#else
#define WRAPPER(func, ...) LogRet(#func, __SFILE__, __func__, __LINE__, (func)(__VA_ARGS__))
#endif
inline int LogRet(const char *fn, const char *file, const char *from, int ln, int ret)
{
printf("%s.%s.%d: %s() ret:%08xn", file, from, ln, fn, ret);
return ret;
}
#define foo(...) WRAPPER(foo, __VA_ARGS__)
#define bar(...) WRAPPER(bar, __VA_ARGS__)
#endif
int main(void)
{
int x = foo(1, 2);
bar(2, "doubled", &x);
return 0;
}
#ifdef foo
#undef foo
#undef bar
#endif
// If and only if the function definition is in the same file with the macros, you must either undefine the macros or
// parenthesize the function e.g. int (foo)(int a, int b) { ... }
int foo(int a, int b)
{
printf("%d + %d = %dn", a, b, a + b);
return a + b;
}
int (bar)(int a, char *b, int *c)
{
printf("%d %s = %dn", *c, b, a * *c);
return *c * a;
}
发布构建输出:
1 + 2 = 3
3 doubled = 6
调试生成输出:
1 + 2 = 3
test.cpp.main.35: foo() ret:00000003
3 doubled = 6
test.cpp.main.36: bar() ret:00000006
主要的好处是不必在代码中找到foo((或bar((的每一个出现,就可以插入一个调试打印来记录调用和结果或您想要插入的任何调试代码。
此代码看起来似乎满足您的要求:
#include <stdio.h>
int rc;
int foo(int a, int b);
int bar(int a, char *b, int *c);
extern void LogRet(const char *fn, const char *file, const char *from, int ln, int ret);
void LogRet(const char *fn, const char *file, const char *from, int ln, int ret)
{
printf("%s.%s.%d: %s() ret:%08xn", file, from, ln, fn, ret);
}
#define foo(args, ...) (rc = (foo)(args, ##__VA_ARGS__), LogRet("foo", __FILE__, __FUNCTION__, __LINE__, rc), rc)
#define bar(args, ...) (rc = (bar)(args, ##__VA_ARGS__), LogRet("bar", __FILE__, __FUNCTION__, __LINE__, rc), rc)
extern void caller1(void);
void caller1(void)
{
int d;
int e = foo(1, 2);
int f = bar(3, "abc", &d);
printf("%s(): %d + %d + %d = %dn", __func__, d, e, f, d + e + f);
}
#undef foo
#undef bar
#define WRAPPER(func, ...) ((rc = (func)(__VA_ARGS__)), LogRet(#func, __FILE__, __func__, __LINE__, rc), rc)
#define foo(...) WRAPPER(foo, __VA_ARGS__)
#define bar(...) WRAPPER(bar, __VA_ARGS__)
extern void caller2(void);
void caller2(void)
{
int d;
int e = foo(2, 3);
int f = bar(3, "abc", &d);
printf("%s(): %d + %d + %d = %dn", __func__, d, e, f, d + e + f);
}
int (foo)(int a, int b)
{
return (a + b) % 3;
}
int (bar)(int a, char *b, int *c)
{
int d = b[a];
*c = d;
return a + d;
}
int main(void)
{
caller1();
caller2();
return 0;
}
示例输出:
wrapper.c.caller1.23: foo() ret:00000000
wrapper.c.caller1.24: bar() ret:00000003
caller1(): 0 + 0 + 3 = 3
wrapper.c.caller2.41: foo() ret:00000002
wrapper.c.caller2.42: bar() ret:00000003
caller2(): 0 + 2 + 3 = 5
预处理源代码(不包括#include <stdio.h>
的输出(:
# 2 "wrapper.c" 2
int rc;
int foo(int a, int b);
int bar(int a, char *b, int *c);
extern void LogRet(const char *fn, const char *file, const char *from, int ln, int ret);
void LogRet(const char *fn, const char *file, const char *from, int ln, int ret)
{
printf("%s.%s.%d: %s() ret:%08xn", file, from, ln, fn, ret);
}
extern void caller1(void);
void caller1(void)
{
int d;
int e = (rc = (foo)(1, 2), LogRet("foo", "wrapper.c", __FUNCTION__, 23, rc), rc);
int f = (rc = (bar)(3, "abc", &d), LogRet("bar", "wrapper.c", __FUNCTION__, 24, rc), rc);
printf("%s(): %d + %d + %d = %dn", __func__, d, e, f, d + e + f);
}
# 36 "wrapper.c"
extern void caller2(void);
void caller2(void)
{
int d;
int e = ((rc = (foo)(2, 3)), LogRet("foo", "wrapper.c", __func__, 41, rc), rc);
int f = ((rc = (bar)(3, "abc", &d)), LogRet("bar", "wrapper.c", __func__, 42, rc), rc);
printf("%s(): %d + %d + %d = %dn", __func__, d, e, f, d + e + f);
}
int (foo)(int a, int b)
{
return (a + b) % 3;
}
int (bar)(int a, char *b, int *c)
{
int d = b[a];
*c = d;
return a + d;
}
int main(void)
{
caller1();
caller2();
return 0;
}
在Mac OS X 10.9.2上使用GCC 4.8.2进行了测试。