C ++ Обтекание макроса-оболочки переменными аргументами?

Вопрос

У меня есть метод, чтобы обернуть функции, заменив их макросом, чтобы я мог записать вызов и код возврата. Вот пример, который работает:

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 () для вставки отладочной печати, чтобы записать вызов и результат или любой другой код отладки, который вы хотите вставить.

3

Решение

Этот код выглядит так, как будто он соответствует вашим требованиям:

#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.

2

Другие решения

Других решений пока нет …