获取在C / C ++中调用的最后一个函数

时间:2013-05-16 00:35:12

标签: c++ c api function

我正在使用API​​,我发现自己编写了许多一般形式的错误处理代码:

if (errorCode = functionName(params)) 
    printError(errorCode, "functionName", __LINE__, __FILE__);

printError的正文可能如下:

fprintf(stderr, "Error in function %s on line %d of file %s", 
    functionName, lineNumber, fileNumber);

然而,每次硬编码函数名都很痛苦。有没有办法在运行时或通过宏获取导致错误的函数名称,即最后一个名为的函数?当然,我无法以任何方式修改API函数。有一些方法可以获得current function以及calling function,但这些方法都不能在函数外部工作。

4 个答案:

答案 0 :(得分:9)

你可以拥有这个

#define SAFE_CALL(func) \
do {\
    if (errorCode = (func)) \
        printError(errorCode, #func, __LINE__, __FILE__);\
} while(0)

SAFE_CALL(functionName(params));

答案 1 :(得分:5)

这里的解决方案是创建一个调用函数的宏,如果失败,则调用带有特殊参数的print函数。如果您正在使用C99,那么您可以(ab)使用可变参数宏,如下所示:

void printError(const char *fmt, const char *func, const int line, const char *file, ...) {
    // the first three arguments are our function, line and file, so first, print out that first
    va_list list;
    va_start(list, file);

    // only print until the first '('
    int nchars = (int) (strchr(func, '(') - func);

    fprintf(stderr, "Error in function %.*s on line %d of file %s: ", nchars, func, line, file);
    vfprintf(stderr, fmt, list);

    va_end(list);
}

#define checkError(invoke, fmt, ...)\
do {\
    int err;\
    if ((err = (invoke))) {\
        printError(fmt, #invoke, __LINE__, __FILE__, ## __VA_ARGS__);\
    }\
} while(0)

请注意,上面使用了GCC extension(在clang中也支持),如果没有提供可变参数,则允许正确传递变量参数。

然后可以这样使用:

checkError(fail(), "Couldn't validate results: %s", "Context-Sensitive Debug Info");

这为您的调试乐趣输出了这个可爱的文字:

Error in function fail on line 703 of file /Users/rross/Documents/TestProj/TestProj/main.mm: Couldn't validate results: Context-Sensitive Debug Info

您可能想要使用其他宏,例如GCC / Clang的__PRETTY_FUNCTION__,但这应该可以帮助您入门!

答案 2 :(得分:2)

注意:此解决方案仅在可以修改或包装API以修改错误代码的返回类型时才有效。

我会使用返回代码的函数的名称来扩充errorCode返回值。然后,printError()函数可以从errorCode本身提取信息。

typedef struct error_code_type {
    unsigned code;
    const char *function;
} error_code_type;

error_code_type error_code (unsigned code. const char *function) {
    error_code_type x = { code, function };
    return x;
}

#define ERROR_CODE(code) error_code(code, __func__)

因此,返回错误时的函数将使用:return ERROR_CODE(xyz);。错误代码的接收者接收返回错误的函数的函数名称。如果复制错误代码而不是重新构造错误代码,则可以正确地反向传播触发错误的最内层函数。

例如:

void foo () {
    error_code_type errorCode;
    if ((errorCode = the_api_function()).code) {
        printError(errorCode.code, errorCode.function, __LINE__, __FILE__);
    }
}

如果使用C ++实现,则可以使用运算符重载使error_code_type行为更像是一个整数类型,如果这很重要的话。

答案 3 :(得分:1)

更新的解决方案

如果您没有定义errorCode(或者并不总是想要设置),请改用它:

#define CALL_AND_LOG_ERROR(callee) \
    CheckCallAndPrintError(callee, #callee, __func__, __LINE__, __FILE__)

int CheckCallAndPrintError( int result, const char* callee,
                            const char* caller, int line, const char* file)
{
    if(result)
    {
        fprintf(stderr, "Error %d returned by function %s called from function %s on line \
                         %d of file %s\n", result, callee, caller, line, file);
    }

    return result;
}

然后您可以使用CALL_AND_LOG_ERROR,如果您想要保留值,则明确必须分配给变量:

int errorCode = CALL_AND_LOG_ERROR(SomeFunc(a, 0, NULL));

虽然这解决了对外部变量的依赖,但我不知道优化器会多长时间内联调用。我怀疑性能影响无论如何都会显着。请注意,两种解决方案都假设API返回值为int s。如果它们是其他标量类型(或不同函数的不同标量类型),则可能必须更改它们的传递/输出方式。


原始解决方案

调用一个调用你的函数的宏绝对是这里的方法,但我建议使用三元运算符而不是控制结构。这允许您使用宏:

  • 作为独立声明(如果您愿意,稍后检查errorCode
  • 作为控制结构的控制声明
  • 在表达式
  • 作为函数的操作数

示例代码(您可以在http://ideone.com/NG8U16处使用它):

#include <stdio.h>

// these macros rely on the fact that errorCode is defined in the the caller

#define FUNC_RETURN_ISNT(func, unexpected) \
    ((errorCode = func) == unexpected) ? printf("error: call %s shouldn't have returned %d but did (from function %s at line %d of file %s)\n", #func, unexpected, __func__, __LINE__, __FILE__), 0 : 1

#define FUNC_RETURN_IS(func, expected) \
    ((errorCode = func) != expected) ? printf("error: call %s returned %d instead of %d (from function %s at line %d of file %s)\n", #func, errorCode, expected, __func__, __LINE__, __FILE__), 0 : 1

#define ERROR_CHECK(func) \
    (errorCode = func) ? printf("error: call %s returned %d (from function %s at line %d of file %s)\n", #func, errorCode, __func__, __LINE__, __FILE__), errorCode : 0

int func(int a, int b, int c)
{
    return a^b^c;
}

int func2(void)
{
    return -1;
}

int func3(void)
{
   static int i = 3;

   return i--;
}

int main(void)
{
    int a = 0, b = 0, c = 0;
    int errorCode;

    int (*funcPoint)(void) = func2;

    FUNC_RETURN_ISNT(func(1,1,1), 1);

    FUNC_RETURN_IS(func(a,b,c), 1);

    ERROR_CHECK(func(a,b,1));

    if(ERROR_CHECK(func2()))
    {
       printf("func2 failed error check\n");
    }
    else
    {
       printf("func2 passed error check\n");
    }

    if(ERROR_CHECK(funcPoint()))
    {
       printf("funcPoint failed error check\n");
    }
    else
    {
       printf("funcPoint passed error check\n");
    }

    if(ERROR_CHECK(func(0,0,0)))
    {
       printf("func failed error check\n");
    }
    else
    {
       printf("func passed error check\n");
    }

    while(ERROR_CHECK(func3()))
    {
        printf("retry...\n");
    }

    switch(ERROR_CHECK(func(1,2,4)))
    {
        case 0:
            printf("okay\n");
            break;
        case 1:
            printf("non-fatal error 1\n");
            break;
        case 7:
            printf("fatal error 7\n");
            return 1;
    }

    return 0;
}

示例输出:

error: call func(1,1,1) shouldn't have returned 1 but did (from function main at line 38 of file prog.c)
error: call func(a,b,c) returned 0 instead of 1 (from function main at line 40 of file prog.c)
error: call func(a,b,1) returned 1 (from function main at line 42 of file prog.c)
error: call func2() returned -1 (from function main at line 44 of file prog.c)
func2 failed error check
error: call funcPoint() returned -1 (from function main at line 53 of file prog.c)
funcPoint failed error check
func passed error check
error: call func3() returned 3 (from function main at line 71 of file prog.c)
retry...
error: call func3() returned 2 (from function main at line 71 of file prog.c)
retry...
error: call func3() returned 1 (from function main at line 71 of file prog.c)
retry...
error: call func(1,2,4) returned 7 (from function main at line 76 of file prog.c)
fatal error 7

但仍存在一些缺点:

  • 这不是很漂亮。语法对我来说很尴尬,它可能会违反一些编码标准。
  • 它可能很脆弱。我没有多想它,但我敢打赌你可以通过传递一些相对合理的东西来打破它。
  • 它需要errorCode已经定义(由于printf,它必须是一个整数类型)。然而,公平地说,原始代码片段也是如此。可以修改宏以接受变量作为参数(它不必总是被命名为errorCode,但它仍然必须在宏之外定义。)
  • 它打印用于调用该函数的确切代码段。在某些(甚至大多数)情况下,这实际上可能是一件好事,但我认为它在函数指针场景中并不理想(在调用API时可能不常见)。此外,更难打印传递参数的实际值。这两种方法都可以在一定程度上得到解决,但这只会使其变得更加笨拙和/或更不便携。