2009-07-10 84 views
5

考虑下面的代码:C++函数呼叫标识符

void Foo() { 
    ...... 
    LOG_ERROR("I'm error 1") // call 1 
    ..... 
    LOG_ERROR("I'm error 2") // call 2 
    ..... 

} 

LOG_ERROR()是一个宏。 LOG_ERROR()应该在代码中打印标识它的字符串 ,而假设代码可以更改,但A::Foo() 将保持不变。在代码 更改时,标识符应保留。

这可以通过将错误代码作为参数来解决,以LOG_ERROR(), 但是我们想从程序员卸下包袱管理错误 代码。

使用__LINE__不是答案,因为Foo()可以从构建移动到 构建。

因此我认为有关识别LOG_ERROR()相对于开始的 Foo()

  • 一个。通过文件名识别(__FILE__)+函数名称(__FUNCTION__)+ 行号LOG_ERROR()相对于Foo()开始。
  • b。按照文件名称(__FILE__)+功能名称(__FUNCTION__)+ LOG_ERROR()呼叫号码Foo()

解决方案应该至少与VC++ 2008和g ++ 4.1.1一起工作。

所提出的一种解决方案(link text)为:

#define ENABLE_LOG_ERROR static const int LOG_ERROR_start_line = __LINE__ 
#define LOG_ERROR(s) cerr << "error #" << (__LINE__ - LOG_ERROR_start_line) \ 
    << " in " << __func__ << ": " << s << endl 

void Foo() { 
    ENABLE_LOG_ERROR; 
    //... 
    LOG_ERROR("error 1"); 
    int i; 
    LOG_ERROR("error 2"); 
} 

这将迫使用户在含有 LOG_ERROR()并且有许多是这样的功能的各功能的启动写ENABLE_LOG_ERROR

是否有其他方式来完成任务?

+0

@idimba,如果没有太多的麻烦,可以使用脚本在每个函数名后面自动添加`ENABLE_LOG_ERROR;`。当我*探索*第三方代码时,我使用该技巧。 – 2009-07-10 09:05:34

+0

不错的主意,但它会影响我们在日志记录子系统中广泛使用的__LINE__。 – dimba 2009-07-10 14:25:22

回答

0

通过修改堆栈的想法,使用std::stringstd::map映射到计数,并查找函数名称。

std::map<std::string, int> LOG_ERROR_count_map; 
#define LOG_ERROR(s) {\ 
    int count = ++LOG_ERROR_count_map[ __func__ ];\ 
    std::cout << count << " in " __func__ ": " s << std::endl;\ 
} 

这意味着你不需要ENABLE_LOG_ERROR,但在地图上查找每个日志的成本。 (这是易于使用和时间之间的权衡。)

+0

我的解决方案和GMan都不在函数中给LOG_ERROR的第n个实例,而是在函数中给出LOG_ERROR的第n个调用。因此,当分支发生时,它们不能用于简单地倒数用途(这可能是预期的用途)。任何形式的动态计数方法都不起作用。 – 2009-07-10 10:10:04

1

该解决方案是非标准,但都MSVC和GCC支持__COUNTER__,这是每递增调用时。

#define LOG_ERROR(s) cerr << "error #" << (__COUNTER__) << " in " \ 
<< __func__ << ": " << s << endl 

注意__COUNTER__将在每个编译单元复位,只有在每个编译单元。因此,如果Foo()具有7个LOG_ERROR()宏,则在后面的函数Bar()中,对于LOG_ERROR()的第一次使用,__COUNTER__的值将为7。

1

虽然问题是关于如何在函数中为记录目的生成唯一的行标识符,但我会退后一步,看看要解决的实际问题:如何生成可轻松识别的日志输出源代码行不会给代码编写者带来负担。

假设您在程序的每个版本中都嵌入了一个独特的构建版本(通常这是一个好主意)。我们还假设您使用的源代码控制机制可以保持源代码的历史记录(这也是一个非常好的主意,无论如何都是这样),并且可以为您提供源代码,因为它可以用于任何请求的源代码版本程序。

如果这些假设成立,则解决方案是让您的程序将其当前版本写入日志文件。然后每个单独的日志条目可以通过__LINE__简单地记录行号。因此,当有人需要使用日志时:他们可以查看日志中的版本号,从源代码管理存储库中获取相应的源代码,并使用日志中的行号转到正确的源代码线。这给使用日志输出的人带来了更多的负担。但是,如果日志代码依赖于或受其他代码的影响,那么其他代码的版本可能会发生变化,那么源代码的历史状态可能是必需的。

此外,以这种方式工作的好处是,它不需要假定任何给定的函数将保持不变,正如原来问题的一部分。所以这种方法有更广泛的应用。


至于实施的话,你既可以登录程序版本程序启动时,或者您可以使日志宏包括在每个条目。

如果程序版本通常存储在普通源代码中不易访问的地方,那么您可以创建一个预生成步骤,提取版本并将其作为#define或const写入简单version.h文件串。然后日志代码或宏可以自动使用它来始终输出当前版本的程序。