[英]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.