Commit 398a1f67 authored by Dominik Charousset's avatar Dominik Charousset

Reduce stack usage in error::eval, fix inspect

The previous implementation in error::eval put an error to the stack and
then optionally calling the next function object. As a result, all
errors remained on the stack until unrolling the stack eventually. By
putting the error into an if-block, temporary objects now go
out-of-scope before calling the next function object.

Also, the previous implementation of `inspect` did not work properly for
any visitor not producing an error as result. The new implementation is
generic and also avoids using std::function to reduce heap allocations.
parent f6fe0618
......@@ -19,18 +19,17 @@
#pragma once
#include <cstdint>
#include <utility>
#include <functional>
#include <utility>
#include "caf/fwd.hpp"
#include "caf/atom.hpp"
#include "caf/none.hpp"
#include "caf/atom.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/meta/omittable_if_empty.hpp"
#include "caf/detail/comparable.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/fwd.hpp"
#include "caf/meta/load_callback.hpp"
#include "caf/meta/omittable_if_empty.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/none.hpp"
namespace caf {
......@@ -56,8 +55,8 @@ public:
/// Convenience alias for `std::enable_if<has_make_error<T>::value, U>::type`.
template <class T, class U = void>
using enable_if_has_make_error_t
= typename std::enable_if<has_make_error<T>::value, U>::type;
using enable_if_has_make_error_t = typename std::enable_if<
has_make_error<T>::value, U>::type;
/// A serializable type for storing error codes with category and optional,
/// human-readable context information. Unlike error handling classes from
......@@ -91,13 +90,6 @@ using enable_if_has_make_error_t
/// rendering error messages via `actor_system::render(const error&)`.
class error : detail::comparable<error> {
public:
// -- member types -----------------------------------------------------------
using inspect_fun = std::function<error (meta::type_name_t,
uint8_t&, atom_value&,
meta::omittable_if_empty_t,
message&)>;
// -- constructors, destructors, and assignment operators --------------------
error() noexcept;
......@@ -141,12 +133,12 @@ public:
const message& context() const noexcept;
/// Returns `*this != none`.
inline explicit operator bool() const noexcept {
explicit operator bool() const noexcept {
return data_ != nullptr;
}
/// Returns `*this == none`.
inline bool operator!() const noexcept {
bool operator!() const noexcept {
return data_ == nullptr;
}
......@@ -156,6 +148,14 @@ public:
// -- modifiers --------------------------------------------------------------
/// Returns the category-specific error code, whereas `0` means "no error".
/// @pre `*this != none`
uint8_t& code() noexcept;
/// Returns the category of this error.
/// @pre `*this != none`
atom_value& category() noexcept;
/// Returns context information to this error.
/// @pre `*this != none`
message& context() noexcept;
......@@ -163,46 +163,74 @@ public:
/// Sets the error code to 0.
void clear() noexcept;
/// Assigns new code and category to this error.
void assign(uint8_t code, atom_value category);
/// Assigns new code, category and context to this error.
void assign(uint8_t code, atom_value category, message context);
// -- static convenience functions -------------------------------------------
/// @cond PRIVATE
static inline error eval() {
static error eval() {
return none;
}
template <class F, class... Fs>
static error eval(F&& f, Fs&&... fs) {
auto x = f();
return x ? x : eval(std::forward<Fs>(fs)...);
if (auto err = f())
return err;
return eval(std::forward<Fs>(fs)...);
}
/// @endcond
// -- friend functions -------------------------------------------------------
// -- nested classes ---------------------------------------------------------
struct data;
template <class Inspector>
friend typename Inspector::result_type inspect(Inspector& f, error& x) {
auto fun = [&](meta::type_name_t x0, uint8_t& x1, atom_value& x2,
meta::omittable_if_empty_t x3, message& x4) -> error{
return f(x0, x1, x2, x3, x4);
struct inspect_helper {
uint8_t code;
error& err;
};
return x.apply(fun);
}
private:
// -- inspection support -----------------------------------------------------
// -- member variables -------------------------------------------------------
error apply(const inspect_fun& f);
data* data_;
};
// -- nested classes ---------------------------------------------------------
// -- operators and free functions ---------------------------------------------
struct data;
/// @relates error
template <class Inspector>
detail::enable_if_t<Inspector::reads_state, typename Inspector::result_type>
inspect(Inspector& f, const error& x) {
if (x)
return f(meta::type_name("error"), x.code(), x.category(),
meta::omittable_if_empty(), x.context());
return f(meta::type_name("error"), uint8_t{0});
}
// -- member variables -------------------------------------------------------
template <class Inspector>
detail::enable_if_t<Inspector::writes_state, typename Inspector::result_type>
inspect(Inspector& f, error::inspect_helper& x) {
if (x.code == 0) {
x.err.clear();
return f();
}
x.err.assign(x.code, atom(""));
return f(x.err.category(), x.err.context());
}
data* data_;
};
/// @relates error
template <class Inspector>
detail::enable_if_t<Inspector::writes_state, typename Inspector::result_type>
inspect(Inspector& f, error& x) {
error::inspect_helper helper{0, x};
return f(helper.code, helper);
}
/// @relates error
std::string to_string(const error& x);
......@@ -252,4 +280,3 @@ bool operator!=(E x, const error& y) {
}
} // namespace caf
......@@ -19,10 +19,11 @@
#include "caf/error.hpp"
#include "caf/config.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/deserializer.hpp"
#include "caf/make_message.hpp"
#include "caf/message.hpp"
#include "caf/serializer.hpp"
#include "caf/deserializer.hpp"
#include "caf/deep_to_string.hpp"
namespace caf {
......@@ -55,8 +56,11 @@ error& error::operator=(error&& x) noexcept {
return *this;
}
error::error(const error& x) : data_(x ? new data(*x.data_) : nullptr) {
// nop
error::error(const error& x) {
if (x)
data_ = new data(*x.data_);
else
data_ = nullptr;
}
error& error::operator=(const error& x) {
......@@ -73,8 +77,7 @@ error& error::operator=(const error& x) {
return *this;
}
error::error(uint8_t x, atom_value y)
: data_(x != 0 ? new data{x, y, none} : nullptr) {
error::error(uint8_t x, atom_value y) : error(x, y, make_message()) {
// nop
}
......@@ -139,6 +142,16 @@ int error::compare(uint8_t x, atom_value y) const noexcept {
// -- modifiers --------------------------------------------------------------
uint8_t& error::code() noexcept {
CAF_ASSERT(data_ != nullptr);
return data_->code;
}
atom_value& error::category() noexcept {
CAF_ASSERT(data_ != nullptr);
return data_->category;
}
message& error::context() noexcept {
CAF_ASSERT(data_ != nullptr);
return data_->context;
......@@ -151,25 +164,27 @@ void error::clear() noexcept {
}
}
// -- inspection support -----------------------------------------------------
void error::assign(uint8_t code, atom_value category) {
return assign(code, category, make_message());
}
error error::apply(const inspect_fun& f) {
data tmp{0, atom(""), message{}};
data& ref = data_ != nullptr ? *data_ : tmp;
auto result = f(meta::type_name("error"), ref.code, ref.category,
meta::omittable_if_empty(), ref.context);
if (ref.code == 0)
clear();
else if (&tmp == &ref)
data_ = new data(std::move(tmp));
return result;
void error::assign(uint8_t code, atom_value category, message context) {
if (data_ == nullptr) {
data_ = new data{code, category, std::move(context)};
} else {
data_->code = code;
data_->category = category;
data_->context = std::move(context);
}
}
// -- operators and free functions ---------------------------------------------
std::string to_string(const error& x) {
if (!x)
return "none";
return deep_to_string(meta::type_name("error"), x.code(), x.category(),
meta::omittable_if_empty(), x.context());
deep_to_string_t f;
return inspect(f, x);
}
} // namespace caf
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