残念ながら、標準の C++ でこれを行う組み込みの方法はありません。スタック トレーサ ユーティリティを構築するのに役立つクラスのシステムを構築できますが、トレースする各メソッドに特別なマクロを配置する必要があります。
以下に概説する戦略を使用して、それが行われた(およびその一部を実装した)のを見てきました。
- スタック フレームに関する情報を格納する独自のクラスを定義します。少なくとも、各ノードには呼び出される関数の名前が含まれている必要があります。ファイル名/行番号情報は 2 番目に近いものです。
- スタック フレーム ノードはリンク リストに格納され、存在する場合は再利用され、存在しない場合は作成されます。
- 特別なオブジェクトをインスタンス化することにより、スタック フレームが作成され、リストに追加されます。オブジェクトのコンストラクターはフレーム ノードをリストに追加します。オブジェクトのデストラクタは、リストからノードを削除します。
- 同じコンストラクター/デストラクターのペアが、スレッド ローカル ストレージにフレームのリストを作成し、作成したリストを削除します。
- 特別なオブジェクトの構築は、マクロによって処理されます。マクロは、特殊なプリプロセッサ トークンを使用して、関数の識別情報と位置情報をフレーム クリエータ オブジェクトに渡します。
以下は、このアプローチの骨の折れる概念実証の実装です。
#include <iostream>
#include <list>
using namespace std;
struct stack_frame {
const char *funName;
const char *fileName;
int line;
stack_frame(const char* func, const char* file, int ln)
: funName(func), fileName(file), line(ln) {}
};
thread_local list<stack_frame> *frames = 0;
struct entry_exit {
bool delFrames;
entry_exit(const char* func, const char* file, int ln) {
if (!frames) {
frames = new list<stack_frame>();
delFrames = true;
} else {
delFrames = false;
}
frames->push_back(stack_frame(func, file, ln));
}
~entry_exit() {
frames ->pop_back();
if (delFrames) {
delete frames;
frames = 0;
}
}
};
void show_stack() {
for (list<stack_frame>::const_iterator i = frames->begin() ; i != frames->end() ; ++i) {
cerr << i->funName << " - " << i->fileName << " (" << i->line << ")" << endl;
}
}
#define FUNCTION_ENTRY entry_exit _entry_exit_(__func__, __FILE__, __LINE__);
void foo() {
FUNCTION_ENTRY;
show_stack();
}
void bar() {
FUNCTION_ENTRY;
foo();
}
void baz() {
FUNCTION_ENTRY;
bar();
}
int main() {
baz();
return 0;
}
上記のコードは C++11 でコンパイルされ、次のように出力されます。
baz - prog.cpp (52)
bar - prog.cpp (48)
foo - prog.cpp (44)
そのマクロを持たない関数は、スタック上では見えません。パフォーマンスが重要な関数には、そのようなマクロを含めるべきではありません。
これはideoneのデモです。