如何跟踪通话统计? C ++

时间:2012-07-02 21:14:20

标签: c++ class static statistics

我正在开发一个向用户提供统计信息的项目。我创建了一个名为Dog的类, 它有几个功能。说话,说话,跑步,取票等。

我希望有一个函数可以调出每个函数的调用次数。我也对构造函数调用和析构函数调用感兴趣。

我有一个定义所有函数的头文件,然后是一个实现它们的单独的.cc文件。我的问题是,有没有办法跟踪每个函数的调用次数?

我有一个名为print的函数,它将获取“统计信息”,然后将它们输出到标准输出。我正在考虑使用静态整数作为类本身的一部分,声明几个整数来跟踪这些事情。我知道编译器会创建一个整数的副本并将其初始化为最小值,然后我将增加.cc函数中的整数。

我还考虑过将静态整数作为.cc中的全局变量。哪种方式更容易?或者有更好的方法吗?

非常感谢任何帮助!

5 个答案:

答案 0 :(得分:4)

使用静态成员变量是可行的方法。但是,编译器不会“创建整数的副本并将其初始化为最小值”;你必须为.cc文件中的每一个提供一个定义,并在那里将其初始化为0。 (如果您使用的是C ++ 11,情况会有所不同,但基本思路是一样的。)

没有理由使用静态全局变量而不是静态成员。

foo.h中:

class Foo {
  static int countCtor_;
  static int countDtor_;
  static int countprint_:
  Foo();
  ~Foo();
  static void print();
};

foo.cc:

#include <iostream>
#include "foo.h"

int Foo::countCtor_ = 0;
int Foo::countDtor_ = 0;
int Foo::countprint_ = 0;

Foo::Foo() {
  ++countCtor_;
  // Something here
}
Foo::~Foo() {
  ++countDtor_;
  // Something here
}
void Foo::print() {
  ++countprint_;
  std::cout << "Ctor:  " << countCtor_ << "\n"
            << "Dtor:  " << countDtor_ << "\n"
            << "print: " << countprint_ << "\n";
}

但是如果你有很多函数,那么重复有点烦人 - 当你的意思是++ countBaz_(特别是如果你复制并粘贴样板文件)时,很容易意外地做++ countBar_,所以你可能想要一些更有趣的东西,比如静态地图和增加计数[__ FUNC__]的宏,所以你可以在每个函数中使用完全相同的行。像这样:

foo.h中:

#include <map>
class Foo {
  static std::map<const char*, int> counts_;
  Foo();
  ~Foo();
  void print();
};

foo.cc:

#include <iostream>
#include "foo.h"

std::map<const char *, int> Foo::counts_;

#define INC_COUNT_() do { ++counts_[__FUNC__]; } while (0)

Foo::Foo() {
  INC_COUNT_();
  // Something here
}
Foo::~Foo() {
  INC_COUNT_();
  // Something here
}
void Foo::print() {
  INC_COUNT_();
  for (std::map<const char *, int>::const_iterator it = counts_.begin(); 
       it != counts_.end(); ++it) {
    std::cout << it->first << ": " << it->second << "\n";
  }
}

在上面的示例代码中,__ FUNC__是占位符。遗憾的是,您无法使用符合标准的值。大多数编译器都有__func __,__ FUNC __,__ FUNCTION __,__ FUNCSIG__和__PRETTY_FUNCTION__的一些子集。但是,这些都不是C ++ 03中的标准。 C ++ 11标准化__func__,但仅作为“实现定义的字符串”,不保证是有用的,甚至是唯一的。最重要的是,不同编译器的值会有所不同。此外,它们中的一些可能是宏而不是标识符,以使事情变得更有趣。

如果你想要真正可移植的代码,在C ++ 11中,你可以使用像字符串(__ func__)+“:”+ STRINGIZE(__ LINE __)这样的东西 - 这有点难看,但至少每个函数都有一个独特的名称。在C ++ 03中,没有相应的东西。如果你只需要“足够便携”,请查阅你使用的每个编译器的文档,或者依赖autoconf这样的东西。

答案 1 :(得分:3)

您是否有任何理由不能使用标准的分析工具来统计这些来电?像gprof这样的东西?

否则静态整数将成为可能。

答案 2 :(得分:2)

假设您希望在程序中始终跟踪这些统计信息,您可以使用unordered_map函数名称:

std::unordered_map<const char *, unsigned> stats;

void foo () {
    // use __FUNCDNAME__ for MSVC
    ++stats[__PRETTY_FUNCTION__];
    //...
}

有目的地使用编译器特定的函数名称说明符来获取修饰的函数名称。这样可以将重载的函数名称计为单独的函数。

这种技术允许您轻松添加新功能而无需考虑其他任何事情,但如果存在哈希冲突(通过将stats地图的大小调整为更大可以稍微补救一下,则会有一些额外的成本)。在字符串上没有计算哈希值,因为键是指针类型,它只使用指针值本身作为哈希值。

如果这只是用于分析的一次性代码,那么您应该首先尝试使用平台上提供的代码分析工具。

答案 3 :(得分:1)

你可以将static本地人放在方法本身内,这似乎更清晰,因为这些变量在逻辑上与班级无关,所以没有理由让他们成员。

另外,您可以使用宏来简化工作。我通常不建议使用宏,但这似乎是一个合适的用途:

#define DEFINE_COUNTER \
   static int noCalls = 0; \
   noCalls++;


void foo()
{
   DEFINE_COUNTER
}

答案 4 :(得分:1)

使用实现Observer Pattern或Method Call Interception的库。您可以从this list中选择一个,或使用Vitamin之类的内容。