The C language itself does not provide this ability, but most executable formats do provide a way to enable this capability, since it's required by debuggers.
You can often get function names from the callstack by using a library like libunwind or libbacktrace, but they're not always portable and they aren't always trivial to perform (execution cost), and they require that you build your program with debug symbols available.
In either case, this is only reliable when you build without optimization. As soon as the optimizer is involved, all bets are off.
For example,
if (pointer && pointer->sub_->something_) {
pointer->sub_->action(); //1
return nullptr;
}
/// ...
if (pointer) {
pointer->sub_->action(); //2
return nullptr;
}
/// ...
I've actually seen this in a production crash bug: the compiler told us we were accessing a null pointer at //1, which is clearly impossible. We couldn't repro the crash in testing, and the function was particularly long and complex.
What happened was that the compiler collapsed all the pointer->sub_->action(); return nullptrs to one stub function which came from //1, and it was actually the unchecked call at //2 that was the source of the crash.
Between optimizations like this, inlining of functions, whole program optimization, etc, it can be incredibly difficult to accurately tell what is what from the machine state of a running program relative to the source code.
A further complication for stack traces is that in optimized code they often contain a forwarding address. Consider:
int f() {
g();
h();
}
If you were to check the callstack in g there is a good chance it would look like it had been called from h: the compiler can manipulate the stack so that when g returns it goes straight to h rather than wastefully returning to f just to get another jump.
Variables are even harder - the optimizer works hard to eliminate them entirely, to usefully shuffle them around in registers, and so forth.
But you could in-theory build your own simple reflection system, wrapping variables in containers. This often gets clumsy, though.
For tracking the call stack:
#include <iostream>
#include <vector>
struct Callsite {
const char* file_;
size_t line_;
static thread_local std::vector<Callsite*> callStack;
Callsite(const char* file, size_t line) : file_(file), line_(line) {
callStack.push_back(this);
}
~Callsite() noexcept { callStack.pop_back(); }
};
thread_local std::vector<Callsite*> Callsite::callStack;
#define ENTER Callsite __callsite_entry(__FILE__, __LINE__);
void f() {
ENTER;
for (auto&& stack: Callsite::callStack) {
std::cout << stack->file_ << ":" << stack->line_ << "\n";
}
}
int main() {
ENTER;
f();
}
Live demo: http://ideone.com/ZAUVib
z. Not sure what your problem is. And there is no requirement to use a stack by the standard anyway. And on recent platforms like x86/x64/ARM, it very likely does not. And the type is apparentlyintC does not support dynamic typing (this includes compiler extensions).intin his code is a compiler extension (false for C89)?intis apparently an error on my side (no idea why I made it), as the type ofzis clearlycharOccam's razor: The type is explicitly specified and conversions are only applied in expressions, not declarations. Any compiler doing them would violate the standard. (FYI: an implementation can (e.g. ARM does) use wider internal operations, it just must have the same observable behaviour as the abstract machine of the C standard requires.)