Commit 93e030e3 authored by Kenton Varda's avatar Kenton Varda

Implement better exception strategy and neat-o new logging/assert macros.

parent d6567127
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "exception.h"
#include "util.h"
#include "logging.h"
#include <unistd.h>
namespace capnproto {
ArrayPtr<const char> operator*(const Stringifier&, Exception::Nature nature) {
static const char* NATURE_STRINGS[] = {
"precondition not met",
"bug in code",
"invalid input data",
"network failure",
"error"
};
const char* s = NATURE_STRINGS[static_cast<uint>(nature)];
return arrayPtr(s, strlen(s));
}
ArrayPtr<const char> operator*(const Stringifier&, Exception::Durability durability) {
static const char* DURABILITY_STRINGS[] = {
"temporary",
"permanent"
};
const char* s = DURABILITY_STRINGS[static_cast<uint>(durability)];
return arrayPtr(s, strlen(s));
}
Exception::Exception(Nature nature, Durability durability, const char* file, int line,
Array<char> description) noexcept
: file(file), line(line), nature(nature), durability(durability),
description(move(description)) {
bool hasDescription = this->description != nullptr;
// Must be careful to NUL-terminate this.
whatStr = str(file, ":", line, ": ", nature,
durability == Durability::TEMPORARY ? " (temporary)" : "",
hasDescription ? ": " : "", this->description, '\0');
}
Exception::Exception(const Exception& other) noexcept
: file(other.file), line(other.line), nature(other.nature), durability(other.durability),
description(str(other.description)), whatStr(str(other.whatStr)) {}
Exception::~Exception() noexcept {}
const char* Exception::what() const noexcept {
return whatStr.begin();
}
// =======================================================================================
namespace {
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)
#define thread_local __thread
#endif
thread_local ExceptionCallback::ScopedRegistration* threadLocalCallback = nullptr;
ExceptionCallback* globalCallback = nullptr;
} // namespace
ExceptionCallback::ExceptionCallback() {}
ExceptionCallback::~ExceptionCallback() {
if (globalCallback == this) {
globalCallback = nullptr;
}
}
void ExceptionCallback::onRecoverableException(Exception&& exception) {
#if __GNUC__ && !__EXCEPTIONS
Log::writeRaw(str(exception.what(), '\n'));
#else
throw std::move(exception);
#endif
}
void ExceptionCallback::onFatalException(Exception&& exception) {
#if __GNUC__ && !__EXCEPTIONS
Log::writeRaw(str(exception.what(), '\n'));
#else
throw std::move(exception);
#endif
}
void ExceptionCallback::logMessage(ArrayPtr<const char> text) {
while (text != nullptr) {
ssize_t n = write(STDERR_FILENO, text.begin(), text.size());
if (n <= 0) {
// stderr is broken. Give up.
return;
}
text = text.slice(n, text.size());
}
}
void ExceptionCallback::useProcessWide() {
RECOVERABLE_PRECOND(globalCallback == nullptr,
"Can't register multiple global ExceptionCallbacks at once.") {
return;
}
globalCallback = this;
}
ExceptionCallback::ScopedRegistration::ScopedRegistration(ExceptionCallback* callback)
: callback(callback) {
old = threadLocalCallback;
threadLocalCallback = this;
}
ExceptionCallback::ScopedRegistration::~ScopedRegistration() {
threadLocalCallback = old;
}
ExceptionCallback* getExceptionCallback() {
static ExceptionCallback defaultCallback;
ExceptionCallback::ScopedRegistration* scoped = threadLocalCallback;
return scoped != nullptr ? scoped->getCallback() :
globalCallback != nullptr ? globalCallback : &defaultCallback;
}
} // namespace capnproto
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CAPNPROTO_EXCEPTION_H_
#define CAPNPROTO_EXCEPTION_H_
#include <exception>
#include "type-safety.h"
namespace capnproto {
class Exception: public std::exception {
// Exception thrown in case of fatal errors.
public:
enum class Nature {
// What kind of failure? This is informational, not intended for programmatic use.
// Note that the difference between some of these failure types is not always clear. For
// example, a precondition failure may be due to a "local bug" in the calling code, or it
// may be due to invalid input.
PRECONDITION,
LOCAL_BUG,
INPUT,
NETWORK_FAILURE,
OTHER
// Make sure to update the stringifier if you add a new nature.
};
enum class Durability {
TEMPORARY, // Retrying the exact same operation might succeed.
PERMANENT // Retrying the exact same operation will fail in exactly the same way.
// Make sure to update the stringifier if you add a new durability.
};
Exception(Nature nature, Durability durability, const char* file, int line,
Array<char> description = nullptr) noexcept;
Exception(const Exception& other) noexcept;
~Exception() noexcept;
const char* what() const noexcept override;
private:
const char* file;
int line;
Nature nature;
Durability durability;
Array<char> description;
Array<char> whatStr;
};
class Stringifier;
ArrayPtr<const char> operator*(const Stringifier&, Exception::Nature nature);
ArrayPtr<const char> operator*(const Stringifier&, Exception::Durability durability);
class ExceptionCallback {
// If you don't like C++ exceptions, you may implement and register an ExceptionCallback in order
// to perform your own exception handling.
//
// For example, a reasonable thing to do is to have onRecoverableException() set a flag
// indicating that an error occurred, and then check for that flag further up the stack.
public:
ExceptionCallback();
virtual ~ExceptionCallback();
virtual void onRecoverableException(Exception&& exception);
// Called when an exception has been raised, but the calling code has the ability to continue by
// producing garbage output. This method _should_ throw the exception, but is allowed to simply
// return if garbage output is acceptable. The default implementation throws an exception unless
// Cap'n Proto was compiled with -fno-exceptions, in which case it logs an error and returns.
virtual void onFatalException(Exception&& exception);
// Called when an exception has been raised and the calling code cannot continue. If this method
// returns normally, abort() will be called. The method must throw the exception to avoid
// aborting. The default implementation throws an exception unless Cap'n Proto was compiled with
// -fno-exceptions, in which case it logs an error and returns.
virtual void logMessage(ArrayPtr<const char> text);
// Called when Cap'n Proto wants to log some debug text. The text always ends in a newline if
// it is non-empty. The default implementation writes the text to stderr.
void useProcessWide();
// Use this ExceptionCallback for all exceptions thrown from any thread in the process which
// doesn't otherwise have a thread-local callback. When the ExceptionCallback is destroyed,
// the default behavior will be restored. It is an error to set multiple process-wide callbacks
// at the same time.
//
// Note that to delete (and thus unregister) a global ExceptionCallback, you must ensure that
// no other threads might running code that could throw at that time. It is probably best to
// leave the callback registered permanently.
class ScopedRegistration {
// Allocate a ScopedRegistration on the stack to register you ExceptionCallback just within
// the current thread. When the ScopedRegistration is destroyed, the previous thread-local
// callback will be restored.
public:
ScopedRegistration(ExceptionCallback* callback);
~ScopedRegistration();
inline ExceptionCallback* getCallback() { return callback; }
private:
ExceptionCallback* callback;
ScopedRegistration* old;
};
};
ExceptionCallback* getExceptionCallback();
// Returns the current exception callback.
} // namespace capnproto
#endif // CAPNPROTO_EXCEPTION_H_
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "logging.h"
#include "exception.h"
#include <gtest/gtest.h>
#include <string>
#include <stdio.h>
namespace capnproto {
namespace internal {
namespace {
class MockException {};
class MockExceptionCallback: public ExceptionCallback {
public:
~MockExceptionCallback() {}
std::string text;
void onRecoverableException(Exception&& exception) override {
text += "recoverable exception: ";
text += exception.what();
text += '\n';
}
void onFatalException(Exception&& exception) override {
text += "fatal exception: ";
text += exception.what();
text += '\n';
throw MockException();
}
void logMessage(ArrayPtr<const char> text) override {
this->text += "log message: ";
this->text.append(text.begin(), text.end());
}
};
std::string fileLine(std::string file, int line) {
file += ':';
char buffer[32];
sprintf(buffer, "%d", line);
file += buffer;
return file;
}
TEST(Logging, Log) {
MockExceptionCallback mockCallback;
MockExceptionCallback::ScopedRegistration reg(&mockCallback);
int line;
LOG(WARNING, "Hello world!"); line = __LINE__;
EXPECT_EQ("log message: warning: " + fileLine(__FILE__, line) + ": Hello world!\n",
mockCallback.text);
mockCallback.text.clear();
int i = 123;
const char* str = "foo";
LOG(ERROR, i, str); line = __LINE__;
EXPECT_EQ("log message: error: " + fileLine(__FILE__, line) + ": i = 123; str = foo\n",
mockCallback.text);
mockCallback.text.clear();
CHECK(1 == 1);
EXPECT_THROW(CHECK(1 == 2), MockException); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": bug in code: expected "
"1 == 2\n", mockCallback.text);
mockCallback.text.clear();
RECOVERABLE_CHECK(1 == 1) {
ADD_FAILURE() << "Shouldn't call recovery code when check passes.";
};
bool recovered = false;
RECOVERABLE_CHECK(1 == 2, "1 is not 2") { recovered = true; } line = __LINE__;
EXPECT_EQ("recoverable exception: " + fileLine(__FILE__, line) + ": bug in code: expected "
"1 == 2; 1 is not 2\n", mockCallback.text);
EXPECT_TRUE(recovered);
mockCallback.text.clear();
EXPECT_THROW(CHECK(1 == 2, i, "hi", str), MockException); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": bug in code: expected "
"1 == 2; i = 123; hi; str = foo\n", mockCallback.text);
mockCallback.text.clear();
EXPECT_THROW(PRECOND(1 == 2, i, "hi", str), MockException); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": precondition not met: expected "
"1 == 2; i = 123; hi; str = foo\n", mockCallback.text);
mockCallback.text.clear();
}
} // namespace
} // namespace internal
} // namespace capnproto
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "logging.h"
#include <stdlib.h>
#include <ctype.h>
namespace capnproto {
Log::Severity Log::minSeverity = Log::Severity::WARNING;
ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity) {
static const char* SEVERITY_STRINGS[] = {
"info",
"warning",
"error",
"fatal"
};
const char* s = SEVERITY_STRINGS[static_cast<uint>(severity)];
return arrayPtr(s, strlen(s));
}
static Array<char> makeDescription(const char* condition, const char* macroArgs,
ArrayPtr<Array<char>> argValues) {
ArrayPtr<const char> argNames[argValues.size()];
if (argValues.size() > 0) {
size_t index = 0;
const char* start = macroArgs;
while (isspace(*start)) ++start;
const char* pos = start;
uint depth = 0;
bool quoted = false;
while (char c = *pos++) {
if (quoted) {
if (c == '\\' && *pos != '\0') {
++pos;
} else if (c == '\"') {
quoted = false;
}
} else {
if (c == '(') {
++depth;
} else if (c == ')') {
--depth;
} else if (c == '\"') {
quoted = true;
} else if (c == ',' && depth == 0) {
if (index < argValues.size()) {
argNames[index] = arrayPtr(start, pos - 1);
}
++index;
while (isspace(*pos)) ++pos;
start = pos;
}
}
}
if (index < argValues.size()) {
argNames[index] = arrayPtr(start, pos - 1);
}
++index;
if (index != argValues.size()) {
getExceptionCallback()->logMessage(
str(__FILE__, ":", __LINE__, ": Failed to parse logging macro args into ",
argValues.size(), " names: ", macroArgs, '\n'));
}
}
{
ArrayPtr<const char> expected = arrayPtr("expected ");
ArrayPtr<const char> conditionArray = condition == nullptr ? nullptr : arrayPtr(condition);
ArrayPtr<const char> sep = arrayPtr(" = ");
ArrayPtr<const char> delim = arrayPtr("; ");
size_t totalSize = 0;
if (condition != nullptr) {
totalSize += expected.size() + conditionArray.size();
}
for (size_t i = 0; i < argValues.size(); i++) {
if (i > 0 || condition != nullptr) totalSize += delim.size();
if (argNames[i].size() > 0 && argNames[i][0] != '\"') {
totalSize += argNames[i].size() + sep.size();
}
totalSize += argValues[i].size();
}
ArrayBuilder<char> result(totalSize);
if (condition != nullptr) {
result.addAll(expected);
result.addAll(conditionArray);
}
for (size_t i = 0; i < argValues.size(); i++) {
if (i > 0 || condition != nullptr) result.addAll(delim);
if (argNames[i].size() > 0 && argNames[i][0] != '\"') {
result.addAll(argNames[i]);
result.addAll(sep);
}
result.addAll(argValues[i]);
}
return result.finish();
}
}
void Log::logInternal(const char* file, uint line, Severity severity, const char* macroArgs,
ArrayPtr<Array<char>> argValues) {
getExceptionCallback()->logMessage(
str(severity, ": ", file, ":", line, ": ",
makeDescription(nullptr, macroArgs, argValues), '\n'));
}
void Log::recoverableFaultInternal(
const char* file, uint line, Exception::Nature nature,
const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues) {
getExceptionCallback()->onRecoverableException(
Exception(nature, Exception::Durability::PERMANENT, file, line,
makeDescription(condition, macroArgs, argValues)));
}
void Log::fatalFaultInternal(
const char* file, uint line, Exception::Nature nature,
const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues) {
getExceptionCallback()->onFatalException(
Exception(nature, Exception::Durability::PERMANENT, file, line,
makeDescription(condition, macroArgs, argValues)));
abort();
}
} // namespace capnproto
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CAPNPROTO_LOGGING_H_
#define CAPNPROTO_LOGGING_H_
#ifndef CAPNPROTO_PRIVATE
#error "This header is only meant to be included by Cap'n Proto's own source code."
#endif
#include "util.h"
#include "exception.h"
namespace capnproto {
class Log {
public:
enum class Severity {
INFO, // Information useful for debugging. No problem detected.
WARNING, // A problem was detected but execution can continue with correct output.
ERROR, // Something is wrong, but execution can continue with garbage output.
FATAL // Something went wrong, and execution cannot continue.
// Make sure to update the stringifier if you add a new severity level.
};
static inline bool shouldLog(Severity severity) { return severity >= minSeverity; }
static inline void setLogLevel(Severity severity) { minSeverity = severity; }
template <typename... Params>
static void log(const char* file, uint line, Severity severity, const char* macroArgs,
Params&&... params);
template <typename... Params>
static void recoverableFault(const char* file, uint line, Exception::Nature nature,
const char* condition, const char* macroArgs, Params&&... params);
template <typename... Params>
static void fatalFault(const char* file, uint line, Exception::Nature nature,
const char* condition, const char* macroArgs, Params&&... params)
CAPNPROTO_NORETURN;
private:
static Severity minSeverity;
static void logInternal(const char* file, uint line, Severity severity, const char* macroArgs,
ArrayPtr<Array<char>> argValues);
static void recoverableFaultInternal(
const char* file, uint line, Exception::Nature nature,
const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues);
static void fatalFaultInternal(
const char* file, uint line, Exception::Nature nature,
const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues)
CAPNPROTO_NORETURN;
};
ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity);
#define LOG(severity, ...) \
if (!::capnproto::Log::shouldLog(::capnproto::Log::Severity::severity)) {} else \
::capnproto::Log::log(__FILE__, __LINE__, ::capnproto::Log::Severity::severity, \
#__VA_ARGS__, __VA_ARGS__)
#define FAULT(nature, cond, ...) \
if (CAPNPROTO_EXPECT_TRUE(cond)) {} else \
::capnproto::Log::fatalFault(__FILE__, __LINE__, \
::capnproto::Exception::Nature::nature, #cond, #__VA_ARGS__, ##__VA_ARGS__)
#define RECOVERABLE_FAULT(nature, cond, ...) \
if (CAPNPROTO_EXPECT_TRUE(cond)) {} else \
if (::capnproto::Log::recoverableFault(__FILE__, __LINE__, \
::capnproto::Exception::Nature::nature, #cond, #__VA_ARGS__, ##__VA_ARGS__), false) {} \
else
#define CHECK(...) FAULT(LOCAL_BUG, __VA_ARGS__)
#define RECOVERABLE_CHECK(...) RECOVERABLE_FAULT(LOCAL_BUG, __VA_ARGS__)
#define PRECOND(...) FAULT(PRECONDITION, __VA_ARGS__)
#define RECOVERABLE_PRECOND(...) RECOVERABLE_FAULT(PRECONDITION, __VA_ARGS__)
#define VALIDATE_INPUT(...) RECOVERABLE_FAULT(INPUT, __VA_ARGS__)
#ifdef NDEBUG
#define DLOG(...) do {} while (false)
#define DCHECK(...) do {} while (false)
#define RECOVERABLE_DCHECK(...) do {} while (false)
#define DPRECOND(...) do {} while (false)
#define RECOVERABLE_DPRECOND(...) do {} while (false)
#else
#define DLOG LOG
#define DCHECK CHECK
#define RECOVERABLE_DCHECK RECOVERABLE_CHECK
#define DPRECOND PRECOND
#define RECOVERABLE_DPRECOND RECOVERABLE_PRECOND
#endif
template <typename... Params>
void Log::log(const char* file, uint line, Severity severity, const char* macroArgs,
Params&&... params) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
logInternal(file, line, severity, macroArgs, arrayPtr(argValues, sizeof...(Params)));
}
template <typename... Params>
void Log::recoverableFault(const char* file, uint line, Exception::Nature nature,
const char* condition, const char* macroArgs, Params&&... params) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
recoverableFaultInternal(file, line, nature, condition, macroArgs,
arrayPtr(argValues, sizeof...(Params)));
}
template <typename... Params>
void Log::fatalFault(const char* file, uint line, Exception::Nature nature,
const char* condition, const char* macroArgs, Params&&... params) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
fatalFaultInternal(file, line, nature, condition, macroArgs,
arrayPtr(argValues, sizeof...(Params)));
}
} // namespace capnproto
#endif // CAPNPROTO_LOGGING_H_
...@@ -21,53 +21,20 @@ ...@@ -21,53 +21,20 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "macros.h" #include "macros.h"
#include <exception> #include "exception.h"
#include <string>
#include <unistd.h> #include <unistd.h>
#include <stdio.h> #include <stdio.h>
#include "exception.h"
#include "util.h"
namespace capnproto { namespace capnproto {
namespace internal { namespace internal {
class Exception: public std::exception {
// Exception thrown in case of fatal errors.
public:
Exception(const char* file, int line, const char* expectation, const char* message);
~Exception() noexcept;
const char* what() const noexcept override;
private:
std::string description;
};
Exception::Exception(
const char* file, int line, const char* expectation, const char* message) {
description = "Captain Proto debug assertion failed:\n ";
description += file;
description += ':';
char buf[32];
sprintf(buf, "%d", line);
description += buf;
description += ": ";
description += expectation;
description += "\n ";
description += message;
description += "\n";
write(STDERR_FILENO, description.data(), description.size());
}
Exception::~Exception() noexcept {}
const char* Exception::what() const noexcept {
return description.c_str();
}
void assertionFailure(const char* file, int line, const char* expectation, const char* message) { void assertionFailure(const char* file, int line, const char* expectation, const char* message) {
throw Exception(file, line, expectation, message); throw Exception(Exception::Nature::LOCAL_BUG, Exception::Durability::PERMANENT,
file, line, str(expectation));
} }
} // namespace internal } // namespace internal
......
...@@ -55,8 +55,10 @@ namespace internal { ...@@ -55,8 +55,10 @@ namespace internal {
#define CAPNPROTO_ALWAYS_INLINE(prototype) inline prototype __attribute__((always_inline)) #define CAPNPROTO_ALWAYS_INLINE(prototype) inline prototype __attribute__((always_inline))
// Force a function to always be inlined. Apply only to the prototype, not to the definition. // Force a function to always be inlined. Apply only to the prototype, not to the definition.
#define CAPNPROTO_NORETURN __attribute__((noreturn));
void assertionFailure(const char* file, int line, const char* expectation, const char* message) void assertionFailure(const char* file, int line, const char* expectation, const char* message)
__attribute__((noreturn)); CAPNPROTO_NORETURN;
#define CAPNPROTO_ASSERT(condition, message) \ #define CAPNPROTO_ASSERT(condition, message) \
if (CAPNPROTO_EXPECT_TRUE(condition)); else ::capnproto::internal::assertionFailure(\ if (CAPNPROTO_EXPECT_TRUE(condition)); else ::capnproto::internal::assertionFailure(\
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "macros.h" #include "macros.h"
#include <cstddef> #include <cstddef>
#include <string.h>
namespace capnproto { namespace capnproto {
...@@ -51,8 +52,24 @@ struct NoInfer { ...@@ -51,8 +52,24 @@ struct NoInfer {
typedef T Type; typedef T Type;
}; };
template <typename T> struct RemoveReference { typedef T Type; };
template <typename T> struct RemoveReference<T&> { typedef T Type; };
template<typename> struct IsLvalueReference { static constexpr bool value = false; };
template<typename T> struct IsLvalueReference<T&> { static constexpr bool value = true; };
// #including <utility> just for std::move() and std::forward() is excessive. Instead, we
// re-define them here.
template<typename T> constexpr T&& move(T& t) noexcept { return static_cast<T&&>(t); } template<typename T> constexpr T&& move(T& t) noexcept { return static_cast<T&&>(t); }
// Like std::move. Unfortunately, #including <utility> brings in tons of unnecessary stuff.
template<typename T>
constexpr T&& forward(typename RemoveReference<T>::Type& t) noexcept {
return static_cast<T&&>(t);
}
template<typename T> constexpr T&& forward(typename RemoveReference<T>::Type&& t) noexcept {
static_assert(!IsLvalueReference<T>::value, "Attempting to forward rvalue as lvalue reference.");
return static_cast<T&&>(t);
}
// ======================================================================================= // =======================================================================================
// ArrayPtr // ArrayPtr
...@@ -63,10 +80,10 @@ class ArrayPtr { ...@@ -63,10 +80,10 @@ class ArrayPtr {
// and passing by value only copies the pointer, not the target. // and passing by value only copies the pointer, not the target.
public: public:
inline ArrayPtr(): ptr(nullptr), size_(0) {} inline constexpr ArrayPtr(): ptr(nullptr), size_(0) {}
inline ArrayPtr(std::nullptr_t): ptr(nullptr), size_(0) {} inline constexpr ArrayPtr(std::nullptr_t): ptr(nullptr), size_(0) {}
inline ArrayPtr(T* ptr, std::size_t size): ptr(ptr), size_(size) {} inline constexpr ArrayPtr(T* ptr, std::size_t size): ptr(ptr), size_(size) {}
inline ArrayPtr(T* begin, T* end): ptr(begin), size_(end - begin) {} inline constexpr ArrayPtr(T* begin, T* end): ptr(begin), size_(end - begin) {}
inline operator ArrayPtr<const T>() { inline operator ArrayPtr<const T>() {
return ArrayPtr<const T>(ptr, size_); return ArrayPtr<const T>(ptr, size_);
...@@ -97,17 +114,22 @@ private: ...@@ -97,17 +114,22 @@ private:
}; };
template <typename T> template <typename T>
inline ArrayPtr<T> arrayPtr(T* ptr, size_t size) { inline constexpr ArrayPtr<T> arrayPtr(T* ptr, size_t size) {
// Use this function to construct ArrayPtrs without writing out the type name. // Use this function to construct ArrayPtrs without writing out the type name.
return ArrayPtr<T>(ptr, size); return ArrayPtr<T>(ptr, size);
} }
template <typename T> template <typename T>
inline ArrayPtr<T> arrayPtr(T* begin, T* end) { inline constexpr ArrayPtr<T> arrayPtr(T* begin, T* end) {
// Use this function to construct ArrayPtrs without writing out the type name. // Use this function to construct ArrayPtrs without writing out the type name.
return ArrayPtr<T>(begin, end); return ArrayPtr<T>(begin, end);
} }
inline constexpr ArrayPtr<const char> arrayPtr(const char* s) {
// Use this function to construct an ArrayPtr from a NUL-terminated string, especially a literal.
return arrayPtr(s, strlen(s));
}
template <typename T> template <typename T>
class Array { class Array {
// An owned array which will automatically be deleted in the destructor. Can be moved, but not // An owned array which will automatically be deleted in the destructor. Can be moved, but not
...@@ -127,6 +149,9 @@ public: ...@@ -127,6 +149,9 @@ public:
inline operator ArrayPtr<T>() { inline operator ArrayPtr<T>() {
return ArrayPtr<T>(ptr, size_); return ArrayPtr<T>(ptr, size_);
} }
inline operator ArrayPtr<const T>() const {
return ArrayPtr<T>(ptr, size_);
}
inline ArrayPtr<T> asPtr() { inline ArrayPtr<T> asPtr() {
return ArrayPtr<T>(ptr, size_); return ArrayPtr<T>(ptr, size_);
} }
...@@ -147,8 +172,8 @@ public: ...@@ -147,8 +172,8 @@ public:
return ArrayPtr<T>(ptr + start, end - start); return ArrayPtr<T>(ptr + start, end - start);
} }
inline bool operator==(std::nullptr_t) { return ptr == nullptr; } inline bool operator==(std::nullptr_t) { return size_ == 0; }
inline bool operator!=(std::nullptr_t) { return ptr != nullptr; } inline bool operator!=(std::nullptr_t) { return size_ != 0; }
inline Array& operator=(std::nullptr_t) { inline Array& operator=(std::nullptr_t) {
delete[] ptr; delete[] ptr;
...@@ -168,12 +193,16 @@ public: ...@@ -168,12 +193,16 @@ public:
private: private:
T* ptr; T* ptr;
std::size_t size_; size_t size_;
inline explicit Array(std::size_t size): ptr(new T[size]), size_(size) {} inline explicit Array(std::size_t size): ptr(new T[size]), size_(size) {}
inline Array(T* ptr, size_t size): ptr(ptr), size_(size) {}
template <typename U> template <typename U>
friend Array<U> newArray(size_t size); friend Array<U> newArray(size_t size);
template <typename U>
friend class ArrayBuilder;
}; };
template <typename T> template <typename T>
...@@ -181,6 +210,55 @@ inline Array<T> newArray(size_t size) { ...@@ -181,6 +210,55 @@ inline Array<T> newArray(size_t size) {
return Array<T>(size); return Array<T>(size);
} }
template <typename T>
class ArrayBuilder {
union Slot { T value; char dummy; };
static_assert(sizeof(Slot) == sizeof(T), "union is bigger than content?");
public:
explicit ArrayBuilder(size_t size): ptr(new Slot[size]), pos(ptr), endPtr(ptr + size) {}
~ArrayBuilder() {
for (Slot* p = ptr; p < pos; ++p) {
p->value.~T();
}
delete [] ptr;
}
template <typename... Params>
void add(Params&&... params) {
CAPNPROTO_DEBUG_ASSERT(pos < endPtr, "Added too many elements to ArrayBuilder.");
new(&pos->value) T(forward<Params>(params)...);
++pos;
}
template <typename Container>
void addAll(Container&& container) {
Slot* __restrict__ pos_ = pos;
auto i = container.begin();
auto end = container.end();
while (i != end) {
pos_++->value = *i++;
}
pos = pos_;
}
Array<T> finish() {
// We could allow partial builds if Array<T> used a deleter callback, but that would make
// Array<T> bigger for no benefit most of the time.
CAPNPROTO_DEBUG_ASSERT(pos == endPtr, "ArrayBuilder::finish() called prematurely.");
Array<T> result(reinterpret_cast<T*>(ptr), pos - ptr);
ptr = nullptr;
pos = nullptr;
endPtr = nullptr;
return result;
}
private:
Slot* ptr;
Slot* pos;
Slot* endPtr;
};
// ======================================================================================= // =======================================================================================
// IDs // IDs
......
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "util.h"
#include <gtest/gtest.h>
#include <string>
namespace capnproto {
namespace internal {
namespace {
std::string arrayToStr(Array<char> arr) {
return std::string(arr.begin(), arr.size());
}
TEST(Util, Foo) {
EXPECT_EQ("foobar", arrayToStr(str("foo", "bar")));
EXPECT_EQ("1 2 3 4", arrayToStr(str(1, " ", 2u, " ", 3l, " ", 4ll)));
EXPECT_EQ("1.5 foo 1e15 bar -3", arrayToStr(str(1.5f, " foo ", 1e15, " bar ", -3)));
EXPECT_EQ("foo", arrayToStr(str('f', 'o', 'o')));
}
} // namespace
} // namespace internal
} // namespace capnproto
This diff is collapsed.
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CAPNPROTO_UTIL_H_
#define CAPNPROTO_UTIL_H_
#include <initializer_list>
#include <utility>
#include <type_traits>
#include "type-safety.h"
#include <string.h>
namespace capnproto {
// =======================================================================================
// Arrays
// TODO: Move Array here?
template <typename T, size_t fixedSize>
class FixedArray {
public:
inline size_t size() const { return fixedSize; }
inline T* begin() { return content; }
inline T* end() { return content + fixedSize; }
inline const T* begin() const { return content; }
inline const T* end() const { return content + fixedSize; }
inline operator ArrayPtr<T>() {
return arrayPtr(content, fixedSize);
}
inline operator ArrayPtr<const T>() const {
return arrayPtr(content, fixedSize);
}
inline T& operator[](size_t index) { return content[index]; }
inline const T& operator[](size_t index) const { return content[index]; }
private:
T content[fixedSize];
};
template <typename T, size_t fixedSize>
class CappedArray {
public:
inline constexpr CappedArray(): currentSize(fixedSize) {}
inline explicit constexpr CappedArray(size_t s): currentSize(s) {}
inline size_t size() const { return currentSize; }
inline void setSize(size_t s) { currentSize = s; }
inline T* begin() { return content; }
inline T* end() { return content + currentSize; }
inline const T* begin() const { return content; }
inline const T* end() const { return content + currentSize; }
inline operator ArrayPtr<T>() const {
return arrayPtr(content, fixedSize);
}
private:
size_t currentSize;
T content[fixedSize];
};
template <typename T>
Array<T> iterableToArray(T&& a) {
Array<T> result = newArray<T>(a.size());
auto i = a.iterator();
auto end = a.end();
T* __restrict__ ptr = result.begin();
while (i != end) {
*ptr++ = *i++;
}
return result;
}
template <typename T>
Array<T> iterableToArray(Array<T>&& a) {
return std::move(a);
}
// =======================================================================================
// String stuff
inline size_t sum(std::initializer_list<size_t> nums) {
size_t result = 0;
for (auto num: nums) {
result += num;
}
return result;
}
template <typename Element>
Element* fill(Element* ptr) { return ptr; }
template <typename Element, typename First, typename... Rest>
Element* fill(Element* __restrict__ target, First& first, Rest&&... rest) {
auto i = first.begin();
auto end = first.end();
while (i != end) {
*target++ = *i++;
}
return fill(target, std::forward<Rest>(rest)...);
}
template <typename Element, typename First, typename... Rest>
Element* fill(Element* __restrict__ target, First&& first, Rest&&... rest) {
auto i = first.begin();
auto end = first.end();
while (i != end) {
*target++ = std::move(*i++);
}
return fill(target, std::forward<Rest>(rest)...);
}
template <typename Element, typename... Params>
Array<Element> concat(Params&&... params) {
Array<Element> result = newArray<Element>(sum({params.size()...}));
fill(result.begin(), std::forward<Params>(params)...);
return result;
}
template <typename Element>
Array<Element> concat(Array<Element>&& arr) {
return std::move(arr);
}
struct Stringifier {
// This is a dummy type with only one instance: STR (below). To make an arbitrary type
// stringifiable, define `operator*(Stringifier, T)` to return an iterable container of `char`.
// The container type must have a `size()` method. Be sure to declare the operator in the same
// namespace as `T` **or** in the global scope.
//
// A more usual way to accomplish what we're doing here would be to require that you define
// a function like `toString(T)` and then rely on argument-dependent lookup. However, this has
// the problem that it pollutes other people's namespaces and even the global namespace.
// Declaring `operator*` with `Stringifier` as the left operand does not harm any other
// namespaces.
inline ArrayPtr<const char> operator*(ArrayPtr<const char> s) const { return s; }
inline ArrayPtr<const char> operator*(const char* s) const { return arrayPtr(s, strlen(s)); }
inline FixedArray<char, 1> operator*(char c) const {
FixedArray<char, 1> result;
result[0] = c;
return result;
}
CappedArray<char, sizeof(short) * 4> operator*(short i) const;
CappedArray<char, sizeof(unsigned short) * 4> operator*(unsigned short i) const;
CappedArray<char, sizeof(int) * 4> operator*(int i) const;
CappedArray<char, sizeof(unsigned int) * 4> operator*(unsigned int i) const;
CappedArray<char, sizeof(long) * 4> operator*(long i) const;
CappedArray<char, sizeof(unsigned long) * 4> operator*(unsigned long i) const;
CappedArray<char, sizeof(long long) * 4> operator*(long long i) const;
CappedArray<char, sizeof(unsigned long long) * 4> operator*(unsigned long long i) const;
CappedArray<char, 24> operator*(float f) const;
CappedArray<char, 32> operator*(double f) const;
};
static constexpr Stringifier STR;
template <typename... Params>
Array<char> str(Params&&... params) {
return concat<char>(STR * std::forward<Params>(params)...);
}
} // namespace capnproto
#endif // CAPNPROTO_UTIL_H_
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