计算函数被调用的次数
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 不严格。 如果打算在生产中使用,那么您可能需要为监控软件编写某种通知,例如zabbix
或collectd
计数器。请注意,这种方法将准确的错误处理委托给负责产品支持的人员。如果这是应该在应用程序中处理的错误,那么您可能需要抛出异常并编写错误处理程序代码(可能仍会通知外部监控软件)。
如果 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 的评论指的是这个答案的一个版本,它建议使用 FILE 和 LINE 作为每个函数的唯一标识符,映射到呼叫计数表。因此,它无法允许多个实例。替换后的答案会暂时出现差异,以消除他的担忧,因为原始评论是有效的。【参考方案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
【讨论】:
以上是关于计算函数被调用的次数的主要内容,如果未能解决你的问题,请参考以下文章