簡體   English   中英

如何跟蹤通話統計信息? C ++

[英]How to keep track of call statistics? C++

我正在研究一個向用戶提供統計信息的項目。 我創建了一個名為Dog的類,它具有多個功能。 說話,上低音,跑步,抓取等

我想要一個可以吐出每個函數被調用次數的函數。 我也對構造函數調用和析構函數調用感興趣。

我有一個定義所有功能的頭文件,然后是一個實現它們的單獨的.cc文件。 我的問題是,是否有辦法跟蹤每個函數被調用了多少次?

我有一個名為print的函數,該函數將獲取“統計信息”,然后將其輸出到標准輸出。 我當時正在考慮將靜態整數用作類本身的一部分,並聲明幾個整數來跟蹤這些情況。 我知道編譯器將創建整數的副本並將其初始化為最小值,然后在.cc函數中遞增整數。

我還考慮過將靜態整數作為.cc中的全局變量。 哪種方法更容易? 還是有更好的方法來做到這一點?

任何幫助是極大的贊賞!

使用靜態成員變量是方法。 但是,編譯器不會“創建整數的副本並將其初始化為最小值”。 您必須為.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中,可以使用諸如string(__ func__)+“:” + STRINGIZE(__ LINE__)之類的東西-這有點難看,但是至少每個函數都有一個唯一的名稱。 在C ++ 03中,沒有等效的方法。 如果您只需要“足夠便攜”,請查閱您使用的每個編譯器的文檔,或者依靠諸如autoconf之類的東西。

有什么原因不能使用將為您計算這些調用的標准配置工具? gprof一樣?

否則,將使用靜態整數。

假設您希望始終在程序中跟蹤這些統計信息,則可以使用函數名稱的unordered_map

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

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

有目的地使用編譯器特定的函數名稱說明符,以獲取修飾的函數名稱。 這樣一來,重載的函數名稱就被算作單獨的函數。

這項技術使您可以輕松添加新功能,而無需考慮其他任何事情,但是如果存在哈希沖突(可以通過將stats映射圖的大小設置得更大些,可以在某種程度上進行補救),則需要花費少量的額外費用。 字符串上沒有計算哈希,因為鍵是指針類型,它僅將指針值本身用作哈希。

如果這只是用於概要分析的一次性代碼,那么您應該首先嘗試使用平台上可用的代碼概要分析工具。

您可以將static局部變量放入方法本身,這看起來更干凈,因為這些變量在邏輯上未連接到類,因此沒有理由使其成為成員。

另外,您可以使用宏來簡化工作。 我通常不建議使用宏,但這似乎是一種適當的用法:

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


void foo()
{
   DEFINE_COUNTER
}

使用實現觀察者模式或方法調用攔截的庫。 您可以從此列表中選擇一種,或使用諸如維生素之類的東西。

暫無
暫無

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

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