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:
Maybe(decltype(nullptr)) noexcept: ptr(nullptr) {}
~Maybe() noexcept {}
inline Maybe& operator=(Maybe&& other) { ptr = kj::mv(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; }
~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:
internal::NullableValue<T> ptr;
......@@ -458,12 +479,21 @@ public:
Maybe(const Maybe<U&>& other): ptr(other.ptr) {}
Maybe(decltype(nullptr)) noexcept: ptr(nullptr) {}
~Maybe() noexcept {}
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; }
~Maybe() noexcept {}
template <typename Func>
auto map(Func&& f) -> Maybe<decltype(f(instance<T&>()))> {
if (ptr == nullptr) {
return nullptr;
} else {
return f(*ptr);
}
}
private:
T* ptr;
......
......@@ -29,6 +29,7 @@
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <exception>
namespace kj {
namespace internal {
......@@ -44,26 +45,26 @@ public:
void onRecoverableException(Exception&& exception) override {
text += "recoverable exception: ";
auto what = str(exception);
// Discard the last line of "what" because it is a stack trace.
const char* what = exception.what();
const char* end = strrchr(what, '\n');
const char* end = strrchr(what.cStr(), '\n');
if (end == nullptr) {
text += exception.what();
text += what.cStr();
} else {
text.append(what, end);
text.append(what.cStr(), end);
}
text += '\n';
}
void onFatalException(Exception&& exception) override {
text += "fatal exception: ";
auto what = str(exception);
// Discard the last line of "what" because it is a stack trace.
const char* what = exception.what();
const char* end = strrchr(what, '\n');
const char* end = strrchr(what.cStr(), '\n');
if (end == nullptr) {
text += exception.what();
text += what.cStr();
} else {
text.append(what, end);
text.append(what.cStr(), end);
}
text += '\n';
throw MockException();
......@@ -155,6 +156,30 @@ TEST(Logging, Log) {
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) {
MockExceptionCallback mockCallback;
MockExceptionCallback::ScopedRegistration reg(mockCallback);
......
......@@ -27,6 +27,7 @@
#include <unistd.h>
#include <execinfo.h>
#include <stdlib.h>
#include <exception>
namespace kj {
......@@ -53,6 +54,42 @@ ArrayPtr<const char> KJ_STRINGIFY(Exception::Durability durability) {
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,
String description) noexcept
: file(file), line(line), nature(nature), durability(durability),
......@@ -83,41 +120,21 @@ void Exception::wrapContext(const char* file, int line, String&& description) {
context = heap<Context>(file, line, mv(description), mv(context));
}
const char* Exception::what() const noexcept {
uint contextDepth = 0;
const Maybe<Own<Context>>* contextPtr = &context;
for (;;) {
KJ_IF_MAYBE(c, *contextPtr) {
++contextDepth;
contextPtr = &(*c)->next;
} else {
break;
}
class ExceptionImpl: public Exception, public std::exception {
public:
inline ExceptionImpl(Exception&& other): Exception(mv(other)) {}
ExceptionImpl(const ExceptionImpl& other): Exception(other) {
// No need to copy whatBuffer since it's just to hold the return value of what().
}
Array<String> contextText = heapArray<String>(contextDepth);
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;
}
}
const char* what() const noexcept override;
// Must be careful to NUL-terminate this.
whatBuffer = str(strArray(contextText, ""),
file, ":", line, ": ", nature,
durability == Durability::TEMPORARY ? " (temporary)" : "",
this->description == nullptr ? "" : ": ", this->description,
"\nstack: ", strArray(arrayPtr(trace, traceCount), " "));
private:
mutable String whatBuffer;
};
const char* ExceptionImpl::what() const noexcept {
whatBuffer = str(*this);
return whatBuffer.begin();
}
......@@ -146,9 +163,9 @@ void ExceptionCallback::onRecoverableException(Exception&& exception) {
logMessage(str(exception.what(), '\n'));
#else
if (std::uncaught_exception()) {
logMessage(str("unwind: ", exception.what(), '\n'));
logMessage(str("unwind: ", ExceptionImpl(mv(exception)).what(), '\n'));
} else {
throw kj::mv(exception);
throw ExceptionImpl(mv(exception));
}
#endif
}
......@@ -157,7 +174,7 @@ void ExceptionCallback::onFatalException(Exception&& exception) {
#if KJ_NO_EXCEPTIONS
logMessage(str(exception.what(), '\n'));
#else
throw kj::mv(exception);
throw ExceptionImpl(mv(exception));
#endif
}
......
......@@ -24,15 +24,19 @@
#ifndef KJ_EXCEPTION_H_
#define KJ_EXCEPTION_H_
#include <exception>
#include "memory.h"
#include "array.h"
#include "string.h"
namespace kj {
class Exception: public std::exception {
class ExceptionImpl;
class Exception {
// 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__
// For some reason Eclipse gets confused by the definition of Nature if it's the first thing
......@@ -74,6 +78,7 @@ public:
Nature getNature() const { return nature; }
Durability getDurability() const { return durability; }
ArrayPtr<const char> getDescription() const { return description; }
ArrayPtr<void* const> getStackTrace() const { return arrayPtr(trace, traceCount); }
struct Context {
// Describes a bit about what was going on when the exception was thrown.
......@@ -101,8 +106,6 @@ public:
// is expected that contexts will be added in reverse order as the exception passes up the
// callback stack.
const char* what() const noexcept override;
private:
const char* file;
int line;
......@@ -112,11 +115,13 @@ private:
Maybe<Own<Context>> context;
void* trace[16];
uint traceCount;
mutable String whatBuffer;
friend class ExceptionImpl;
};
ArrayPtr<const char> KJ_STRINGIFY(Exception::Nature nature);
ArrayPtr<const char> KJ_STRINGIFY(Exception::Durability durability);
String KJ_STRINGIFY(const Exception& e);
class ExceptionCallback {
// 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