Commit cd02679f authored by Kenton Varda's avatar Kenton Varda

Don't include <exception> in exception.h, but still throw object implementing std::exception.

parent 7a830031
...@@ -426,13 +426,34 @@ public: ...@@ -426,13 +426,34 @@ public:
Maybe(decltype(nullptr)) noexcept: ptr(nullptr) {} Maybe(decltype(nullptr)) noexcept: ptr(nullptr) {}
~Maybe() noexcept {}
inline Maybe& operator=(Maybe&& other) { ptr = kj::mv(other.ptr); return *this; } inline Maybe& operator=(Maybe&& other) { ptr = kj::mv(other.ptr); return *this; }
inline Maybe& operator=(const Maybe& other) { ptr = other.ptr; return *this; } inline Maybe& operator=(const Maybe& other) { ptr = other.ptr; return *this; }
inline bool operator==(decltype(nullptr)) const { return ptr == nullptr; } inline bool operator==(decltype(nullptr)) const { return ptr == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return ptr != nullptr; } inline bool operator!=(decltype(nullptr)) const { return ptr != nullptr; }
~Maybe() noexcept {} template <typename Func>
auto map(Func&& f) -> Maybe<decltype(f(instance<T&>()))> {
if (ptr == nullptr) {
return nullptr;
} else {
return f(*ptr);
}
}
template <typename Func>
auto map(Func&& f) const -> Maybe<decltype(f(instance<const T&>()))> {
if (ptr == nullptr) {
return nullptr;
} else {
return f(*ptr);
}
}
// TODO(someday): Once it's safe to require GCC 4.8, use ref qualifiers to provide a version of
// map() that uses move semantics if *this is an rvalue.
private: private:
internal::NullableValue<T> ptr; internal::NullableValue<T> ptr;
...@@ -458,12 +479,21 @@ public: ...@@ -458,12 +479,21 @@ public:
Maybe(const Maybe<U&>& other): ptr(other.ptr) {} Maybe(const Maybe<U&>& other): ptr(other.ptr) {}
Maybe(decltype(nullptr)) noexcept: ptr(nullptr) {} Maybe(decltype(nullptr)) noexcept: ptr(nullptr) {}
~Maybe() noexcept {}
inline Maybe& operator=(const Maybe& other) { ptr = other.ptr; return *this; } inline Maybe& operator=(const Maybe& other) { ptr = other.ptr; return *this; }
inline bool operator==(decltype(nullptr)) const { return ptr == nullptr; } inline bool operator==(decltype(nullptr)) const { return ptr == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return ptr != nullptr; } inline bool operator!=(decltype(nullptr)) const { return ptr != nullptr; }
~Maybe() noexcept {} template <typename Func>
auto map(Func&& f) -> Maybe<decltype(f(instance<T&>()))> {
if (ptr == nullptr) {
return nullptr;
} else {
return f(*ptr);
}
}
private: private:
T* ptr; T* ptr;
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <exception>
namespace kj { namespace kj {
namespace internal { namespace internal {
...@@ -44,26 +45,26 @@ public: ...@@ -44,26 +45,26 @@ public:
void onRecoverableException(Exception&& exception) override { void onRecoverableException(Exception&& exception) override {
text += "recoverable exception: "; text += "recoverable exception: ";
auto what = str(exception);
// Discard the last line of "what" because it is a stack trace. // Discard the last line of "what" because it is a stack trace.
const char* what = exception.what(); const char* end = strrchr(what.cStr(), '\n');
const char* end = strrchr(what, '\n');
if (end == nullptr) { if (end == nullptr) {
text += exception.what(); text += what.cStr();
} else { } else {
text.append(what, end); text.append(what.cStr(), end);
} }
text += '\n'; text += '\n';
} }
void onFatalException(Exception&& exception) override { void onFatalException(Exception&& exception) override {
text += "fatal exception: "; text += "fatal exception: ";
auto what = str(exception);
// Discard the last line of "what" because it is a stack trace. // Discard the last line of "what" because it is a stack trace.
const char* what = exception.what(); const char* end = strrchr(what.cStr(), '\n');
const char* end = strrchr(what, '\n');
if (end == nullptr) { if (end == nullptr) {
text += exception.what(); text += what.cStr();
} else { } else {
text.append(what, end); text.append(what.cStr(), end);
} }
text += '\n'; text += '\n';
throw MockException(); throw MockException();
...@@ -155,6 +156,30 @@ TEST(Logging, Log) { ...@@ -155,6 +156,30 @@ TEST(Logging, Log) {
mockCallback.text.clear(); mockCallback.text.clear();
} }
TEST(Logging, Catch) {
int line;
// Catch as kj::Exception.
try {
line = __LINE__; KJ_FAIL_ASSERT("foo");
ADD_FAILURE() << "Expected exception.";
} catch (const Exception& e) {
String what = str(e);
std::string text(what.cStr(), strchr(what.cStr(), '\n') - what.cStr());
EXPECT_EQ(fileLine(__FILE__, line) + ": bug in code: foo", text);
}
// Catch as std::exception.
try {
line = __LINE__; KJ_FAIL_ASSERT("foo");
ADD_FAILURE() << "Expected exception.";
} catch (const std::exception& e) {
const char* what = e.what();
std::string text(what, strchr(what, '\n') - what);
EXPECT_EQ(fileLine(__FILE__, line) + ": bug in code: foo", text);
}
}
TEST(Logging, Syscall) { TEST(Logging, Syscall) {
MockExceptionCallback mockCallback; MockExceptionCallback mockCallback;
MockExceptionCallback::ScopedRegistration reg(mockCallback); MockExceptionCallback::ScopedRegistration reg(mockCallback);
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <unistd.h> #include <unistd.h>
#include <execinfo.h> #include <execinfo.h>
#include <stdlib.h> #include <stdlib.h>
#include <exception>
namespace kj { namespace kj {
...@@ -53,6 +54,42 @@ ArrayPtr<const char> KJ_STRINGIFY(Exception::Durability durability) { ...@@ -53,6 +54,42 @@ ArrayPtr<const char> KJ_STRINGIFY(Exception::Durability durability) {
return arrayPtr(s, strlen(s)); return arrayPtr(s, strlen(s));
} }
String KJ_STRINGIFY(const Exception& e) {
uint contextDepth = 0;
Maybe<const Exception::Context&> contextPtr = e.getContext();
for (;;) {
KJ_IF_MAYBE(c, contextPtr) {
++contextDepth;
contextPtr = c->next.map(
[](const Own<Exception::Context>& c) -> const Exception::Context& { return *c; });
} else {
break;
}
}
Array<String> contextText = heapArray<String>(contextDepth);
contextDepth = 0;
contextPtr = e.getContext();
for (;;) {
KJ_IF_MAYBE(c, contextPtr) {
contextText[contextDepth++] =
str(c->file, ":", c->line, ": context: ", c->description, "\n");
contextPtr = c->next.map(
[](const Own<Exception::Context>& c) -> const Exception::Context& { return *c; });
} else {
break;
}
}
return str(strArray(contextText, ""),
e.getFile(), ":", e.getLine(), ": ", e.getNature(),
e.getDurability() == Exception::Durability::TEMPORARY ? " (temporary)" : "",
e.getDescription() == nullptr ? "" : ": ", e.getDescription(),
"\nstack: ", strArray(e.getStackTrace(), " "));
}
Exception::Exception(Nature nature, Durability durability, const char* file, int line, Exception::Exception(Nature nature, Durability durability, const char* file, int line,
String description) noexcept String description) noexcept
: file(file), line(line), nature(nature), durability(durability), : file(file), line(line), nature(nature), durability(durability),
...@@ -83,41 +120,21 @@ void Exception::wrapContext(const char* file, int line, String&& description) { ...@@ -83,41 +120,21 @@ void Exception::wrapContext(const char* file, int line, String&& description) {
context = heap<Context>(file, line, mv(description), mv(context)); context = heap<Context>(file, line, mv(description), mv(context));
} }
const char* Exception::what() const noexcept { class ExceptionImpl: public Exception, public std::exception {
uint contextDepth = 0; public:
inline ExceptionImpl(Exception&& other): Exception(mv(other)) {}
const Maybe<Own<Context>>* contextPtr = &context; ExceptionImpl(const ExceptionImpl& other): Exception(other) {
for (;;) { // No need to copy whatBuffer since it's just to hold the return value of what().
KJ_IF_MAYBE(c, *contextPtr) {
++contextDepth;
contextPtr = &(*c)->next;
} else {
break;
}
} }
Array<String> contextText = heapArray<String>(contextDepth); const char* what() const noexcept override;
contextDepth = 0;
contextPtr = &context;
for (;;) {
KJ_IF_MAYBE(c, *contextPtr) {
const Context& node = **c;
contextText[contextDepth++] =
str(node.file, ":", node.line, ": context: ", node.description, "\n");
contextPtr = &node.next;
} else {
break;
}
}
// Must be careful to NUL-terminate this. private:
whatBuffer = str(strArray(contextText, ""), mutable String whatBuffer;
file, ":", line, ": ", nature, };
durability == Durability::TEMPORARY ? " (temporary)" : "",
this->description == nullptr ? "" : ": ", this->description,
"\nstack: ", strArray(arrayPtr(trace, traceCount), " "));
const char* ExceptionImpl::what() const noexcept {
whatBuffer = str(*this);
return whatBuffer.begin(); return whatBuffer.begin();
} }
...@@ -146,9 +163,9 @@ void ExceptionCallback::onRecoverableException(Exception&& exception) { ...@@ -146,9 +163,9 @@ void ExceptionCallback::onRecoverableException(Exception&& exception) {
logMessage(str(exception.what(), '\n')); logMessage(str(exception.what(), '\n'));
#else #else
if (std::uncaught_exception()) { if (std::uncaught_exception()) {
logMessage(str("unwind: ", exception.what(), '\n')); logMessage(str("unwind: ", ExceptionImpl(mv(exception)).what(), '\n'));
} else { } else {
throw kj::mv(exception); throw ExceptionImpl(mv(exception));
} }
#endif #endif
} }
...@@ -157,7 +174,7 @@ void ExceptionCallback::onFatalException(Exception&& exception) { ...@@ -157,7 +174,7 @@ void ExceptionCallback::onFatalException(Exception&& exception) {
#if KJ_NO_EXCEPTIONS #if KJ_NO_EXCEPTIONS
logMessage(str(exception.what(), '\n')); logMessage(str(exception.what(), '\n'));
#else #else
throw kj::mv(exception); throw ExceptionImpl(mv(exception));
#endif #endif
} }
......
...@@ -24,15 +24,19 @@ ...@@ -24,15 +24,19 @@
#ifndef KJ_EXCEPTION_H_ #ifndef KJ_EXCEPTION_H_
#define KJ_EXCEPTION_H_ #define KJ_EXCEPTION_H_
#include <exception>
#include "memory.h" #include "memory.h"
#include "array.h" #include "array.h"
#include "string.h" #include "string.h"
namespace kj { namespace kj {
class Exception: public std::exception { class ExceptionImpl;
class Exception {
// Exception thrown in case of fatal errors. // Exception thrown in case of fatal errors.
//
// Actually, a subclass of this which also implements std::exception will be thrown, but we hide
// that fact from the interface to avoid #including <exception>.
#ifdef __CDT_PARSER__ #ifdef __CDT_PARSER__
// For some reason Eclipse gets confused by the definition of Nature if it's the first thing // For some reason Eclipse gets confused by the definition of Nature if it's the first thing
...@@ -74,6 +78,7 @@ public: ...@@ -74,6 +78,7 @@ public:
Nature getNature() const { return nature; } Nature getNature() const { return nature; }
Durability getDurability() const { return durability; } Durability getDurability() const { return durability; }
ArrayPtr<const char> getDescription() const { return description; } ArrayPtr<const char> getDescription() const { return description; }
ArrayPtr<void* const> getStackTrace() const { return arrayPtr(trace, traceCount); }
struct Context { struct Context {
// Describes a bit about what was going on when the exception was thrown. // Describes a bit about what was going on when the exception was thrown.
...@@ -101,8 +106,6 @@ public: ...@@ -101,8 +106,6 @@ public:
// is expected that contexts will be added in reverse order as the exception passes up the // is expected that contexts will be added in reverse order as the exception passes up the
// callback stack. // callback stack.
const char* what() const noexcept override;
private: private:
const char* file; const char* file;
int line; int line;
...@@ -112,11 +115,13 @@ private: ...@@ -112,11 +115,13 @@ private:
Maybe<Own<Context>> context; Maybe<Own<Context>> context;
void* trace[16]; void* trace[16];
uint traceCount; uint traceCount;
mutable String whatBuffer;
friend class ExceptionImpl;
}; };
ArrayPtr<const char> KJ_STRINGIFY(Exception::Nature nature); ArrayPtr<const char> KJ_STRINGIFY(Exception::Nature nature);
ArrayPtr<const char> KJ_STRINGIFY(Exception::Durability durability); ArrayPtr<const char> KJ_STRINGIFY(Exception::Durability durability);
String KJ_STRINGIFY(const Exception& e);
class ExceptionCallback { class ExceptionCallback {
// If you don't like C++ exceptions, you may implement and register an ExceptionCallback in order // If you don't like C++ exceptions, you may implement and register an ExceptionCallback in order
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment