计算函数被调用的次数

Posted

技术标签:

【中文标题】计算函数被调用的次数【英文标题】:Counting how many times a function is called 【发布时间】:2017-10-12 15:47:06 【问题描述】:

我想计算一个函数被调用了多少次,这样如果它被多次调用,就会向开发人员通知一些错误(通过日志记录或断言等)。我希望这段代码能够很好地定义和隔离,以便它可以轻松地跨许多函数和成员函数移植。像这样简单的事情:

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++ 中,因为它没有反射,以某种方式实现类似的东西?

【问题讨论】:

这是用于调试目的吗? @VTT 不严格。 如果打算在生产中使用,那么您可能需要为监控软件编写某种通知,例如zabbixcollectd 计数器。请注意,这种方法将准确的错误处理委托给负责产品支持的人员。如果这是应该在应用程序中处理的错误,那么您可能需要抛出异常并编写错误处理程序代码(可能仍会通知外部监控软件)。 如果 Windows 和 Visual-Studio 和(更多限制),那么也许你可以使用 penter 和 /Gh 钩子? msdn.microsoft.com/en-us/library/… 【参考方案1】:

对于独立函数或静态类方法,您可以使用静态局部变量:

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();
    ...

class AClass

private:
    callTracker method1_tracker;
    callTracker method2_tracker;

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

    ... 
;

void AClass::method1()

    method1_tracker.called();
    ... 


void AClass::method2()

    method2_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__);
    ...

【讨论】:

这不满足 OP 的原始意图 well defined and as isolated as possible 可能是带有计数器的最小类。 另外,在方法的情况下,这并不区分同一类的不同对象。 @nyarlathotep108 使用数据成员...? @RemyLebeau 它需要为每个需要跟踪的方法添加一个成员。也许可以做得更好? @nyarlathotep108:您可以更新跟踪器以使用std::map 来跟踪单个容器中的多个计数器,然后每个类只需要一个跟踪器。我更新了我的答案以表明这一点。【参考方案2】:

反射不是必需的,因为调用者被称为编译时。 C++11 有一个内置的__func__,它的计算结果是一个纯 C 字符串,它是函数的朴素名称。还有typeid(*this).name() 来获取*this 后面的类的错位名称。

所以定义一个类,它维护一组字符串并有一个方法,announce_call。在您定义的每个类中都放置一个该类的实例,可能是通过从具有protected 实例的人那里继承。

调用announce_call,提供函数名和类名。如果函数名已经在集合中,请记录错误和类名。否则将其添加到集合中。

如果需要,为不属于类的函数提供一个全局实例。

所以每个类的净语法成本是:添加一个额外的基类,在每个计数函数的开头添加一个额外的行。

继承提供了主要警告:因为this 总是指向函数所属事物的实例,如果B 继承自A 并且C 拥有一个实例B,但调用的方法来自A 两次,日志将显示两次调用A 而不是B

【讨论】:

在方法的情况下,这个不区分同一类的不同对象。 @nyarlathotep108 这是真的,一旦你考虑到语义,修复会很尴尬。下意识的做法是提供this 作为调用者的额外指示符,但随后您必须在解构期间发布“全部清除”请求,这开始显着增加语法开销,然后支持您认为的任何内容适合在移动(我假设将总数超过)移动对象时进行,加上复制考虑(因为它可能是每个对象的上下文,无论副本是干净的石板还是只是同一事物的另一种表示)。跨度> 因此,作为记录,@nyarlathotep108 的评论指的是这个答案的一个版本,它建议使用 FILELINE 作为每个函数的唯一标识符,映射到呼叫计数表。因此,它无法允许多个实例。替换后的答案会暂时出现差异,以消除他的担忧,因为原始评论是有效的。【参考方案3】:

我认为这是使用单个宏所能获得的最接近的结果:

#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
     

【讨论】:

以上是关于计算函数被调用的次数的主要内容,如果未能解决你的问题,请参考以下文章

计算通过 JUnit 调用函数的次数

构造函数被调用的次数

SQL Server如何定位自定义标量函数被那个SQL调用次数最多浅析

Java如何控制方法的调用次数?

按名称或签名计算函数调用。 GCC、C++

1113: 递归调用的次数统计(函数专题)