簡體   English   中英

C ++包裝帶有可變參數的包裝器宏?

[英]C++ Wrapping a wrapper macro with variable arguments?

我有一個方法來通過用宏替換它們來包裝函數,以便我可以記錄調用和返回代碼。 這是一個有效的例子:

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和雙字符串化的基礎知識,但我甚至無法讓WRAPPER宏進行真正的函數調用。 理想情況下,我想將其歸結為單個WRAP(foo)語句。 原因是,我有大約100個或更多我想要包裝的函數,它希望從一個簡單的強制包含文件中做到這一點。 我得出的結論是,這是不可能的,但我想在放棄這個想法之前我會問這里。 我正在使用clang和vc ++,如果這有任何區別,但是當我調試很多不同的系統時,在任何編譯器上都有這個很好。


適應Jonathan Leffler的回答

由於我是新來的,我不確定這應該是一個單獨的答案還是編輯更新。 這基本上是Jonathan Leffler的回答。 這是一個功能性的例子。 雖然它調用的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:%08x\n", 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 = %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;
}

在Mac OS X 10.9.2上使用GCC 4.8.2進行測試。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM