I am writing a library that I would like to be portable. Thus, it should not depend on glibc or Microsoft extensions or anything else that is not in the standard. I have a
Look up Nested Diagnostic Context once. Here is a little hint:
class NDC {
public:
static NDC* getContextForCurrentThread();
int addEntry(char const* file, unsigned lineNo);
void removeEntry(int key);
void dump(std::ostream& os);
void clear();
};
class Scope {
public:
Scope(char const *file, unsigned lineNo) {
NDC *ctx = NDC::getContextForCurrentThread();
myKey = ctx->addEntry(file,lineNo);
}
~Scope() {
if (!std::uncaught_exception()) {
NDC *ctx = NDC::getContextForCurrentThread();
ctx->removeEntry(myKey);
}
}
private:
int myKey;
};
#define DECLARE_NDC() Scope s__(__FILE__,__LINE__)
void f() {
DECLARE_NDC(); // always declare the scope
// only use try/catch when you want to handle an exception
// and dump the stack
try {
// do stuff in here
} catch (...) {
NDC* ctx = NDC::getContextForCurrentThread();
ctx->dump(std::cerr);
ctx->clear();
}
}
The overhead is in the implementation of the NDC. I was playing with a lazily evaluated version as well as one that only kept a fixed number of entries as well. The key point is that if you use constructors and destructors to handle the stack so that you don't need all of those nasty try/catch blocks and explicit manipulation everywhere.
The only platform specific headache is the getContextForCurrentThread() method. You can use a platform specific implementation using thread local storage to handle the job in most if not all cases.
If you are more performance oriented and live in the world of log files, then change the scope to hold a pointer to the file name and line number and omit the NDC thing altogether:
class Scope {
public:
Scope(char const* f, unsigned l): fileName(f), lineNo(l) {}
~Scope() {
if (std::uncaught_exception()) {
log_error("%s(%u): stack unwind due to exception\n",
fileName, lineNo);
}
}
private:
char const* fileName;
unsigned lineNo;
};
This will give you a nice stack trace in your log file when an exception is thrown. No need for any real stack walking, just a little log message when an exception is being thrown ;)