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 @@ ...@@ -19,18 +19,17 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <utility>
#include <functional> #include <functional>
#include <utility>
#include "caf/fwd.hpp"
#include "caf/atom.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/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 { namespace caf {
...@@ -56,8 +55,8 @@ public: ...@@ -56,8 +55,8 @@ public:
/// Convenience alias for `std::enable_if<has_make_error<T>::value, U>::type`. /// Convenience alias for `std::enable_if<has_make_error<T>::value, U>::type`.
template <class T, class U = void> template <class T, class U = void>
using enable_if_has_make_error_t using enable_if_has_make_error_t = typename std::enable_if<
= typename std::enable_if<has_make_error<T>::value, U>::type; has_make_error<T>::value, U>::type;
/// A serializable type for storing error codes with category and optional, /// A serializable type for storing error codes with category and optional,
/// human-readable context information. Unlike error handling classes from /// human-readable context information. Unlike error handling classes from
...@@ -91,13 +90,6 @@ using enable_if_has_make_error_t ...@@ -91,13 +90,6 @@ using enable_if_has_make_error_t
/// rendering error messages via `actor_system::render(const error&)`. /// rendering error messages via `actor_system::render(const error&)`.
class error : detail::comparable<error> { class error : detail::comparable<error> {
public: 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 -------------------- // -- constructors, destructors, and assignment operators --------------------
error() noexcept; error() noexcept;
...@@ -141,12 +133,12 @@ public: ...@@ -141,12 +133,12 @@ public:
const message& context() const noexcept; const message& context() const noexcept;
/// Returns `*this != none`. /// Returns `*this != none`.
inline explicit operator bool() const noexcept { explicit operator bool() const noexcept {
return data_ != nullptr; return data_ != nullptr;
} }
/// Returns `*this == none`. /// Returns `*this == none`.
inline bool operator!() const noexcept { bool operator!() const noexcept {
return data_ == nullptr; return data_ == nullptr;
} }
...@@ -156,6 +148,14 @@ public: ...@@ -156,6 +148,14 @@ public:
// -- modifiers -------------------------------------------------------------- // -- 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. /// Returns context information to this error.
/// @pre `*this != none` /// @pre `*this != none`
message& context() noexcept; message& context() noexcept;
...@@ -163,47 +163,75 @@ public: ...@@ -163,47 +163,75 @@ public:
/// Sets the error code to 0. /// Sets the error code to 0.
void clear() noexcept; 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 ------------------------------------------- // -- static convenience functions -------------------------------------------
/// @cond PRIVATE /// @cond PRIVATE
static inline error eval() { static error eval() {
return none; return none;
} }
template <class F, class... Fs> template <class F, class... Fs>
static error eval(F&& f, Fs&&... fs) { static error eval(F&& f, Fs&&... fs) {
auto x = f(); if (auto err = f())
return x ? x : eval(std::forward<Fs>(fs)...); return err;
return eval(std::forward<Fs>(fs)...);
} }
/// @endcond /// @endcond
// -- friend functions -------------------------------------------------------
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);
};
return x.apply(fun);
}
private:
// -- inspection support -----------------------------------------------------
error apply(const inspect_fun& f);
// -- nested classes --------------------------------------------------------- // -- nested classes ---------------------------------------------------------
struct data; struct data;
struct inspect_helper {
uint8_t code;
error& err;
};
private:
// -- member variables ------------------------------------------------------- // -- member variables -------------------------------------------------------
data* data_; data* data_;
}; };
// -- operators and free functions ---------------------------------------------
/// @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});
}
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());
}
/// @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 /// @relates error
std::string to_string(const error& x); std::string to_string(const error& x);
...@@ -252,4 +280,3 @@ bool operator!=(E x, const error& y) { ...@@ -252,4 +280,3 @@ bool operator!=(E x, const error& y) {
} }
} // namespace caf } // namespace caf
...@@ -19,10 +19,11 @@ ...@@ -19,10 +19,11 @@
#include "caf/error.hpp" #include "caf/error.hpp"
#include "caf/config.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/message.hpp"
#include "caf/serializer.hpp" #include "caf/serializer.hpp"
#include "caf/deserializer.hpp"
#include "caf/deep_to_string.hpp"
namespace caf { namespace caf {
...@@ -55,8 +56,11 @@ error& error::operator=(error&& x) noexcept { ...@@ -55,8 +56,11 @@ error& error::operator=(error&& x) noexcept {
return *this; return *this;
} }
error::error(const error& x) : data_(x ? new data(*x.data_) : nullptr) { error::error(const error& x) {
// nop if (x)
data_ = new data(*x.data_);
else
data_ = nullptr;
} }
error& error::operator=(const error& x) { error& error::operator=(const error& x) {
...@@ -73,13 +77,12 @@ error& error::operator=(const error& x) { ...@@ -73,13 +77,12 @@ error& error::operator=(const error& x) {
return *this; return *this;
} }
error::error(uint8_t x, atom_value y) error::error(uint8_t x, atom_value y) : error(x, y, make_message()) {
: data_(x != 0 ? new data{x, y, none} : nullptr) {
// nop // nop
} }
error::error(uint8_t x, atom_value y, message z) error::error(uint8_t x, atom_value y, message z)
: data_(x != 0 ? new data{x, y, std::move(z)} : nullptr) { : data_(x != 0 ? new data{x, y, std::move(z)} : nullptr) {
// nop // nop
} }
...@@ -139,6 +142,16 @@ int error::compare(uint8_t x, atom_value y) const noexcept { ...@@ -139,6 +142,16 @@ int error::compare(uint8_t x, atom_value y) const noexcept {
// -- modifiers -------------------------------------------------------------- // -- 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 { message& error::context() noexcept {
CAF_ASSERT(data_ != nullptr); CAF_ASSERT(data_ != nullptr);
return data_->context; return data_->context;
...@@ -151,25 +164,27 @@ void error::clear() noexcept { ...@@ -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) { void error::assign(uint8_t code, atom_value category, message context) {
data tmp{0, atom(""), message{}}; if (data_ == nullptr) {
data& ref = data_ != nullptr ? *data_ : tmp; data_ = new data{code, category, std::move(context)};
auto result = f(meta::type_name("error"), ref.code, ref.category, } else {
meta::omittable_if_empty(), ref.context); data_->code = code;
if (ref.code == 0) data_->category = category;
clear(); data_->context = std::move(context);
else if (&tmp == &ref) }
data_ = new data(std::move(tmp));
return result;
} }
// -- operators and free functions ---------------------------------------------
std::string to_string(const error& x) { std::string to_string(const error& x) {
if (!x) if (!x)
return "none"; return "none";
return deep_to_string(meta::type_name("error"), x.code(), x.category(), deep_to_string_t f;
meta::omittable_if_empty(), x.context()); return inspect(f, x);
} }
} // namespace caf } // 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