0

私はユーザーに統計を提供するプロジェクトに取り組んでいます。Dogというクラスを作成しましたが、いくつかの機能があります。話す、横糸、走る、フェッチするなど。

各関数が何回呼び出されたかを吐き出す関数が欲しいです。コンストラクター呼び出しとデストラクタ呼び出しにも興味があります。

すべての関数を定義するヘッダーファイルと、それらを実装する別の.ccファイルがあります。私の質問は、各関数が何回呼び出されたかを追跡する方法はありますか?

「統計」を取得して標準出力に出力するprintという関数があります。クラス自体の一部として静的整数を使用することを検討し、それらを追跡するためにいくつかの整数を宣言しました。コンパイラーが整数のコピーを作成し、それを最小値に初期化してから、.cc関数で整数をインクリメントすることを知っています。

また、.ccのグローバル変数として静的整数を使用することも考えました。どちらが簡単ですか?それとも、これを行うためのより良い方法はありますか?

どんな助けでも大歓迎です!

4

5 に答える 5

4

静的メンバー変数を使用するのが方法です。ただし、コンパイラは「整数のコピーを作成して最小値に初期化」しません。.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などに依存してください。

于 2012-07-02T21:21:46.943 に答える
3

これらの呼び出しをカウントする標準のプロファイリングツールを使用できない理由はありますか?gprofのようなもの?

それ以外の場合は、静的整数が最適です。

于 2012-07-02T21:18:14.713 に答える
2

これらの統計をプログラムで常に追跡したい場合は、次unordered_mapの関数名を使用できます。

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

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

コンパイラ固有の関数名指定子の使用は、装飾された関数名を取得するために意図的に行われます。これは、オーバーロードされた関数名が個別の関数としてカウントされるようにするためです。

statsこの手法を使用すると、他に何も考えずに新しい関数を簡単に追加できますが、ハッシュの衝突がある場合はわずかな追加コストがかかります(マップのサイズを大きくすることで多少改善できます)。キーはポインタ型であるため、文字列に対してハッシュは計算されません。これは、ポインタ値自体をハッシュとして使用するだけです。

これがプロファイリング用の1回限りのコードである場合は、最初に、プラットフォームで利用可能なコードプロファイリングツールを使用してみてください。

于 2012-07-02T21:30:07.020 に答える
1

staticローカルをメソッド自体の中に入れることができます。これらの変数はクラスに論理的に接続されていないため、メンバーにする理由がないため、よりクリーンに見えます。

さらに、作業を簡素化するマクロを使用することもできます。通常、マクロの使用はお勧めしませんが、これは適切な使用法のようです。

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


void foo()
{
   DEFINE_COUNTER
}
于 2012-07-02T21:16:43.663 に答える
1

オブザーバーパターンまたはメソッド呼び出しインターセプトを実装するライブラリを使用します。このリストから1つを選択するか、ビタミンなどを使用できます。

于 2012-07-02T21:21:41.157 に答える