2017-10-12 116 views
0

我想計算函數被調用的次數,以便如果多次調用該函數,則會向開發人員通知一些錯誤(通過日誌記錄或斷言等等)。我希望這段代碼能夠很好地定義和隔離,以便它可以很容易地跨多種函數和成員函數移植。東西一樣容易:計算函數被調用的次數

void function() 
{ 
    if(is_called_more_than_once()) 
    { 
     // do something to handle the error 
    } 
} 

void AClass::method() 
{ 
    if(is_called_more_than_once()) 
    { 
     // do something to handle the error 
    } 
} 

在C++中,它可能的,因爲它沒有反射,以達到類似的東西不知何故?

+0

這是旨在用於調試的目的? – VTT

+0

@VTT不嚴格。 – nyarlathotep108

+0

如果這是爲了在生產中使用,那麼您可能需要爲監控軟件編寫某種通知,例如'zabbix'或'collectd'計數器。請注意,此方法將確切的錯誤處理委託給負責產品支持的人員。如果這是應該在應用程序中處理的錯誤,那麼您可能想要拋出異常並編寫錯誤處理程序代碼(可能仍會通知外部監視軟件)。 – VTT

回答

3

對於獨立的功能,或靜態類的方法,可以使用靜態本地變量:

void function() 
{ 
    static int num_called = 0; 
    if(++num_called > 1) 
    { 
     // do something to handle the error 
    } 
    ... 
} 

對於非靜態類的方法,使用一個類數據成員爲每個方法,允許類的各個實例做自己的跟蹤:

class AClass 
{ 
private: 
    int num_method1_called; 
    int num_method2_called; 

public: 
    AClass(); 

    void method1(); 
    void method2(); 

    ... 
}; 

AClass::AClass() : 
    num_method1_called(0), 
    num_method2_called(0) 
{ 
} 

void AClass::method1() 
{ 
    if(++num_method1_called > 1) 
    { 
     // do something to handle the error 
    } 
    ... 
} 

void AClass::method2() 
{ 
    if(++num_method2_called > 1) 
    { 
     // do something to handle the error 
    } 
    ... 
} 

如果錯誤處理始終是相同的,認爲它解壓到一個可重複使用的幫手:

struct callTracker 
{ 
    int counter; 
    callTracker() : counter(0) {} 
    void called() 
    { 
     if(++counter > 1) 
     { 
      // do something to handle the error 
     } 
    } 
}; 

void function() 
{ 
    static callTracker tracker; 
    tracker.called(); 
    ... 
} 

​​

或者:

struct singleCallTracker 
{ 
    int counter; 
    singleCallTracker() : counter(0) {} 
    void called() 
    { 
     if(++counter > 1) 
     { 
      // do something to handle the error 
     } 
    } 
}; 

struct multiCallTracker 
{ 
    std::map<std::string, singleCallTracker> trackers; 
    void called(const std::string &name) 
    { 
     trackers[name].called(); 
    } 
}; 

void function() 
{ 
    static singleCallTracker tracker; 
    tracker.called(); 
    ... 
} 

class AClass 
{ 
private: 
    multiCallTracker method_tracker; 

public: 
    void method1(); 
    void method2(); 

    ... 
}; 

void AClass::method1() 
{ 
    method_tracker.called(__FUNC__); 
    ... 
} 

void AClass::method2() 
{ 
    method_tracker.called(__FUNC__); 
    ... 
} 
+3

這不符合OP的原始意圖'完全定義和儘可能孤立'可能是一個最小類與計數器。 –

+1

也在方法情況下,這不區分同一類的不同對象。 – nyarlathotep108

+0

@ nyarlathotep108使用數據成員......? –

1

反射不是必須的,因爲調用者被稱爲編譯時。 C++ 11有一個內置的__func__,它的計算結果是一個普通的C字符串,它是函數的無名稱。還有typeid(*this).name()可以在*this後面找到班級的名稱。

因此,定義一個維護一組字符串並具有單一方法的類announce_call。將該類的實例放入您定義的每個類中,可能通過繼承具有protected實例的人。

調用announce_call提供函數名和類名。如果函數名稱已經在集合中,請記錄一個錯誤和類名稱。否則將其添加到集合中。

如果需要,爲不屬於類的函數提供一個全局實例。

因此,每類語法成本淨額是:添加一個額外的基類,在每個計數的函數開始時添加一個額外的行。

傳承提供了主要的忠告:因爲this總是指向功能屬於,如果從ACB繼承擁有一個實例B但稱從A的方法兩次,記錄會顯示一個雙的東西實例致電A而不是B

+1

在方法的情況下,這不區分同一類的不同對象。 – nyarlathotep108

+0

@ nyarlathotep108這是真的,一旦你考慮了語義,修正就會很尷尬。這個問題將會提供'this'作爲調用者的額外指示器,但是在拆解過程中你必須發佈一個'全部清除'請求,這個請求開始大大增加語法開銷,然後支持你認爲的任何東西在移動過程中應該做的事情(我會假設帶有可移動對象),以及複製考慮因素(因爲它可能是每個對象的上下文,無論副本是乾淨的石板還是僅僅是同一事物的另一種表示)。 – Tommy

+0

因此,對於記錄,@ nyarlathotep108的評論引用了這個答案的一個版本,建議使用__FILE__和__LINE__作爲每個函數的唯一標識符,映射到一個調用計數表。因此它沒有考慮到多個實例。被替換的答案會隨時出現,以消除他的擔憂,因爲原來的評論是有效的。 – Tommy

0

我認爲這是接近你可以用一個宏來得到:

#define if_called_more_than_once() \ 
    static int s_nTimesCalled = 0; if (++s_nTimesCalled > 1) 

void function() 
{ 
    if_called_more_than_once() 
    { 
     // do something to handle the error 
    } 
}