简体   繁体   中英

How to keep track of call statistics? C++

I'm working on a project that delivers statistics to the user. I created a class called Dog, And it has several functions. Speak, woof, run, fetch, etc.

I want to have a function that spits out how many times each function has been called. I'm also interested in the constructor calls and destructor calls as well.

I have a header file which defines all the functions, then a separate .cc file that implements them. My question is, is there a way to keep track of how many times each function is called?

I have a function called print that will fetch the "statistics" and then output them to standard output. I was considering using static integers as part of the class itself, declaring several integers to keep track of those things. I know the compiler will create a copy of the integer and initialize it to a minimum value, and then I'll increment the integers in the .cc functions.

I also thought about having static integers as a global variable in the .cc. Which way is easier? Or is there a better way to do this?

Any help is greatly appreciated!

Using static member variables is the way to go. However, the compiler will not "create a copy of the integer and initialize it to a minimum value"; you'll have to provide a definition for each one in the .cc file and initialize it to 0 there. (Things are a bit different if you're using C++11, but the basic idea is the same.)

There's no reason to use static global variables instead of static members.

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";
}

But if you've got a lot of functions, the repetition involved is a bit annoying—it's very easy to accidentally do ++countBar_ when you meant ++countBaz_ (especially if you copy and paste the boilerplate), so you may want something a bit fancier, such as a static map and a macro that increments counts[__FUNC__], so you can just use the exact same line in each function. Like this:

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";
  }
}

In the example code above, __FUNC__ is a placeholder. Unfortunately, there is no standard-compliant value you can use in its place. Most compilers have some subset of __func__, __FUNC__, __FUNCTION__, __FUNCSIG__, and __PRETTY_FUNCTION__. However, none of those are standard in C++03. C++11 does standardize __func__, but only as an "implementation-defined string", which isn't guaranteed to be useful, or even unique. On top of that, the values will be different on different compilers. Also, some of them may be macros rather than identifiers, to make things more fun.

If you want truly portable code, in C++11, you can use something like string(__func__) + ":" + STRINGIZE(__LINE__)—this will be somewhat ugly, but at least each function will have a unique name. And in C++03, there is no equivalent. If you just need "portable enough", consult the documentation for every compiler you use, or rely on something like autoconf.

Is there any reason you can't use standard profiling tools that will count these calls for you? Something like gprof ?

Otherwise static integers would be the way to go.

Assuming you want these statistics tracked all the time in your program, you could use an unordered_map of your function names:

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

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

The use of compiler specific function name specifiers is purposefully there to get the decorated function names. This is so that overloaded function names get counted as separate functions.

This technique allows you to add new functions easily without thinking about anything else, but there is a small additional cost if there are hash collisions (which can be remedied somewhat by sizing the stats map to be larger). There is no hash computed on the string, since the key is a pointer type, it just uses the pointer value itself as the hash.

If this is just one-off code for profiling, then you should first try to use the code profiling tools available on your platform.

You can put static locals inside the methods themselves, that seems cleaner since these variables aren't logically connected to the class so there's no reason to make them members.

Additionaly, you could have a macro to simplify the work. I normally don't recommend using macros, but this seems like an appropriate use:

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


void foo()
{
   DEFINE_COUNTER
}

Use a library that implements the Observer Pattern or Method Call Interception. You can choose one from this list , or use something like Vitamin .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM