There is my code:
#include <string>
#include <tr1/functional>
using namespace std;
using namespace std::tr1;
using namespace std::tr1::placeholders;
class Event
{
public:
typedef std::tr1::function<void()> Handler;
void set(Handler h)
{
m_handler = h;
}
template<typename T, typename F>
void set(T * obj, F memfn)
{
set(std::tr1::bind(memfn, obj));
}
void operator()()
{
m_handler();
}
static void fire(Event * event) throw ()
{
(*event)();
}
Handler m_handler;
};
class BuggyHandler
{
public:
BuggyHandler()
{
}
BuggyHandler(Event * b) :
bar(b)
{
bar->set(this, &BuggyHandler::HandleEvent);
}
void HandleEvent()
{
// throw std::length_error
std::string().append(std::numeric_limits<size_t>::max(), '0');
}
private:
Event * bar;
};
void get_correct_stacktrace()
{
Event bar;
BuggyHandler handler(&bar);
bar();
}
void get_incorrect_stacktrace()
{
Event bar;
BuggyHandler handler(&bar);
Event::fire(&bar);
}
int main(int argc, char **argv)
{
int opt = atoi(argv[1]);
if (opt)
get_correct_stacktrace();
else
get_incorrect_stacktrace();
}
When I call ./test 1, I can get correct stack trace from core:
#0 0xffffe410 in __kernel_vsyscall ()
#1 0xf7d028d0 in raise () from /lib/libc.so.6
#2 0xf7d03ff3 in abort () from /lib/libc.so.6
#3 0xf7ede880 in __gnu_cxx::__verbose_terminate_handler () from /usr/lib/libstdc++.so.6
#4 0xf7edc2a5 in std::exception::what () from /usr/lib/libstdc++.so.6
#5 0xf7edc2e2 in std::terminate () from /usr/lib/libstdc++.so.6
#6 0xf7edc41a in __cxa_throw () from /usr/lib/libstdc++.so.6
#7 0xf7e73c6f in std::__throw_length_error () from /usr/lib/libstdc++.so.6
#8 0xf7eb9a17 in std::string::append () from /usr/lib/libstdc++.so.6
#9 0x08049b96 in BuggyHandler::HandleEvent (this=0xffc26c9c) at /home/liangxu/release/server_2.0/test/src/test.cc:54
#10 0x08049857 in get_correct_stacktrace () at /home/liangxu/release/server_2.0/test/src/test.cc:67
#11 0x080498e0 in main (argc=Cannot access memory at address 0x5ac6) at /home/liangxu/release/server_2.0/test/src/test.cc:81
The location of throw exception is test.cc:54
When I call ./test 0, I can get incorrect stack trace from core:
#0 0xffffe410 in __kernel_vsyscall ()
#1 0xf7d508d0 in raise () from /lib/libc.so.6
#2 0xf7d51ff3 in abort () from /lib/libc.so.6
#3 0xf7f2c880 in __gnu_cxx::__verbose_terminate_handler () from /usr/lib/libstdc++.so.6
#4 0xf7f2a2a5 in std::exception::what () from /usr/lib/libstdc++.so.6
#5 0xf7f2a2e2 in std::terminate () from /usr/lib/libstdc++.so.6
#6 0xf7f2a305 in std::exception::what () from /usr/lib/libstdc++.so.6
#7 0xf7f29d98 in __cxa_call_unexpected () from /usr/lib/libstdc++.so.6
#8 0x080497eb in get_incorrect_stacktrace () at /home/liangxu/release/server_2.0/test/src/test.cc:30
#9 0x080498f5 in main (argc=Cannot access memory at address 0x5adf) at /home/liangxu/release/server_2.0/test/src/test.cc:83
There aren't the location of throw exception.
My compiler is "gcc (GCC) 4.1.2 20070115 (prerelease) (SUSE Linux)"
If compiled with "-fno-exceptions", both methods generated correct stack trace.
What is the reason?
Both stack traces are correct.
When you call Event::fire
, the exception is thrown in HandleEvent
and stack unwinding happens until it encounters the fire
which has that exception specification.
If you don't know the real behaviour of exception specifications, you can read about it here : http://www.gotw.ca/publications/mill22.htm
Basically, the specification throw ()
does guarantee that this method does not throw by failing miserably if one of the containing calls throws. When stack unwinding tries to go out of this method, it checks the exception specification, sees that it doesn't match, and calls std::unexpected
from the current location of unwiding, thus a __cxa_call_unexpected ()
in your stack trace just after get_incorrect_stacktrace ()
.
In most cases, using exception specifications is useless in C++ because the guarantee is provided at the cost of a general failure of the program is something throws and shouldn't.