У меня есть метод, чтобы обернуть функции, заменив их макросом, чтобы я мог записать вызов и код возврата. Вот пример, который работает:
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:%08x\n", 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)
Я понимаю основы stringify и double stringify, но даже не могу заставить макрос WRAPPER выполнять настоящий вызов функции. В идеале я хотел бы свести это к одному выражению WRAP (foo). Причина в том, что у меня есть около 100 или более функций, которые я бы хотел обернуть, и он хотел бы сделать это просто из одного простого файла включения. Я прихожу к выводу, что это невозможно, но я решил спросить здесь, прежде чем отказаться от идеи. Я использую clang и vc ++, если это имеет какое-то значение, но было бы неплохо иметь это на любом компиляторе, так как я отлаживаю множество различных систем.
Поскольку я новичок здесь, я не был уверен, должен ли это быть отдельный ответ или редактировать обновление. По сути, это ответ Джонатана Леффлера. Это в функциональном примере. Хотя две функции, которые он вызывает, не имеют смысла, цель состояла в том, чтобы создать макрос, который может обернуть любую функцию любым списком аргументов. Мое основное использование заключается в регистрации потока использования в большой библиотеке кода, которая имеет проблему. Кроме того, у моего оригинального образца есть одна явная слабость. Хранение кода возврата в глобальном формате не является поточно-ориентированным без громоздкой подготовки в TLS. Глобал теперь удален, и макрос больше не использует оператор последовательности для возврата значения, он сохраняется и возвращается функцией регистрации. Кроме того, как указал Авгурар и показал на примере Джонатана. Если макрос объявлен в том же файле, что и объявление функции, он требует скобок.
#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:%08x\n", file, from, ln, fn, ret);
return ret;
}
#define foo(...) WRAPPER(foo, __VA_ARGS__)
#define bar(...) WRAPPER(bar, __VA_ARGS__)
#endifint 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 = %d\n", a, b, a + b);
return a + b;
}
int (bar)(int a, char *b, int *c)
{
printf("%d %s = %d\n", *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:%08x\n", 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 = %d\n", __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 = %d\n", __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:%08x\n", 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 = %d\n", __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 = %d\n", __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;
}
Протестировано с GCC 4.8.2 на Mac OS X 10.9.2.
Других решений пока нет …