C中的范围保护

时间:2010-01-19 18:49:58

标签: c scope guard

我想在C中使用范围保护来进行分析。

我想知道我在一个功能中花了多少时间。这是我的工作:

int function() {

  tic();

  ... do stuff ...
  if (something)
  {
    toc();
    return 0;
   }

  toc();
  return 1;
}

每次退出函数时我都需要放置一个toc语句。我想这样做,而无需到处复制粘贴toc。有没有通用的方法来做到这一点,使用宏或什么? 此外,我不想改变调用函数的方式,因为我需要分析许多函数。

由于

8 个答案:

答案 0 :(得分:5)

这不会改变调用函数的方式。但是,如果您希望能够分析每个功能,可能没什么用。

static inline int real_function() {
    // previous contents of function(), with no tic or toc
}

int function() {
    tic();
    int r = real_function();
    toc();
    return r;
}

正如其他人所说:使用分析器,从长远来看,它将为您节省大量的精力。因为他们没有说:如果你的平台有一个。

如果没有,那么最简单的可能就是说(作为编码规则)函数必须只有一个出口点,并且该出口点必须通过你的宏。然后,您可以在进入和退出时使用代码手动检测所有功能。具有多个返回的旧版函数可以如上所述进行包装。

另外,请记住,当你做这样的事情时,你的编译器会搞砸你。你可以这样写:

tic();
do_something();
int i = something_else();
toc();
return i;

如果编译器确定something_else没有副作用,那么即使something_else花费大量时间,它也可能将代码转换为:

tic();
do_something();
toc();
return something_else();

您的个人资料数据会低估您在函数中花费的时间。有一个真实的探查器是如此之好的另一个原因 - 它可以与编译器合作。

答案 1 :(得分:4)

您可以定义一个宏,如:

#define TOC_RETURN(x) \
    do { \
    toc(); \
    return x; \
    } while(0)

哪个应该可以在任何地方使用它。然后,您可以使用return *;自动替换TOC_RETURN(*)

答案 2 :(得分:4)

为什么不使用实际的分析工具,例如gprof

答案 3 :(得分:2)

您可以通过宏“重新定义”返回:(请参阅免责声明)

#include <stdio.h>

void tic() { printf("tic\n"); }
void toc() { printf("toc\n"; }

#define return toc(); return
int foo() {
    tic();

    return 0;
}
#undef return

int main() {
    foo();
    return 0;
}

免责声明:这可以被视为丑陋和黑客,因为:

  • 除非您使用 return; -statements,否则它不适用于void函数。
  • 它可能不是便携式/标准的,即使它适用于MSVC8。
  • 不应该定义关键字。

答案 4 :(得分:1)

我不会为此推荐一个宏。您只需偶尔对代码进行一次分析,并将“return”替换为一些特殊的宏仅仅是为了这个目的,这会使代码的可读性降低。

这样做不是更好吗?

tic();
call_function();
toc();

这会自动处理函数中的“所有退出点”。

P.S。你为什么不使用探查器?

答案 5 :(得分:1)

真正的分析器不需要您修改代码,只需要在启用分析的情况下编译它。

答案 6 :(得分:0)

嗯,也许将函数调用包装在一个宏(宏的系列,真的)中?这是一个不带参数并返回Retval:

// define the wrapper for name
#define DEFTIMECALL0(Retval,name) \
    Retval timed##name() \
    { \
        Retval ret;
        tic(); \
        ret = name(); \
        toc(); \
        return ret; \
    }

你需要为每个函数调用提供宏,并使用Retval和void返回版本。

编辑也许在定义包装器函数时甚至没有意义,更好的是只有一个宏系列(同样,对于每个arity和返回类型/ void版本)包装一个函数直接在调用的tic / toc中调用

不要害怕检测分析器,这基本上是为你做的。

答案 7 :(得分:0)

我很晚才参加聚会,但是还有另一种使用GCC扩展cleanup属性在C中进行范围保护的方法。 cleanup属性将函数附加到变量声明,该变量声明在变量超出范围时运行。最初旨在为动态分配的类型执行内存释放,但它也可以被用作作用域保护。

void cleanup_toc(int *ignored __attribute__((__unused__))) { toc(); }

int function(void) {
    tic();
    int atexit __attribute__((__cleanup__(cleanup_toc))) = 0;

    //... do stuff ...
    if (something) {
        return 0;
    }

    return 1;
}

此解决方案不使用宏,但是您当然可以将其包装到宏中。例如:

#define CONCATENATE_IMPL(x, y) x ## y
#define CONCATENATE(x, y) CONCATENATE_IMPL(x, y)
#define ATEXIT(f) int CONCATENATE(atexit, __LINE__) __attribute__((__cleanup__(f))) = 0

int function(void) {
    ATEXIT(cleanup1); // These are executed in reverse order, i.e.
    ATEXIT(cleanup2); // cleanup2 will run before cleanup1.
}