Unverified Commit 0a03d7d6 authored by Dominik Charousset's avatar Dominik Charousset Committed by GitHub

Merge pull request #913

Reduce stack usage and indirections in serializers
parents 58f1d373 ae331cb6
...@@ -242,7 +242,7 @@ public: ...@@ -242,7 +242,7 @@ public:
for (auto& x : xs) { for (auto& x : xs) {
using value_type = typename std::remove_reference<decltype(x)>::type; using value_type = typename std::remove_reference<decltype(x)>::type;
using mutable_type = typename std::remove_const<value_type>::type; using mutable_type = typename std::remove_const<value_type>::type;
if (auto err = apply_derived(const_cast<mutable_type&>(x))) if (auto err = dref().apply(const_cast<mutable_type&>(x)))
return err; return err;
} }
return none; return none;
...@@ -252,7 +252,7 @@ public: ...@@ -252,7 +252,7 @@ public:
template <class U, class T> template <class U, class T>
error consume_range_c(T& xs) { error consume_range_c(T& xs) {
for (U x : xs) { for (U x : xs) {
if (auto err = apply_derived(x)) if (auto err = dref().apply(x))
return err; return err;
} }
return none; return none;
...@@ -264,7 +264,7 @@ public: ...@@ -264,7 +264,7 @@ public:
auto insert_iter = std::inserter(xs, xs.end()); auto insert_iter = std::inserter(xs, xs.end());
for (size_t i = 0; i < num_elements; ++i) { for (size_t i = 0; i < num_elements; ++i) {
typename std::remove_const<typename T::value_type>::type x; typename std::remove_const<typename T::value_type>::type x;
if (auto err = apply_derived(x)) if (auto err = dref().apply(x))
return err; return err;
*insert_iter++ = std::move(x); *insert_iter++ = std::move(x);
} }
...@@ -278,7 +278,7 @@ public: ...@@ -278,7 +278,7 @@ public:
auto insert_iter = std::inserter(xs, xs.end()); auto insert_iter = std::inserter(xs, xs.end());
for (size_t i = 0; i < num_elements; ++i) { for (size_t i = 0; i < num_elements; ++i) {
U x; U x;
if (auto err = apply_derived(x)) if (auto err = dref().apply(x))
return err; return err;
*insert_iter++ = std::move(x); *insert_iter++ = std::move(x);
} }
...@@ -375,8 +375,9 @@ public: ...@@ -375,8 +375,9 @@ public:
using t0 = typename std::remove_const<F>::type; using t0 = typename std::remove_const<F>::type;
// This cast allows the data processor to cope with // This cast allows the data processor to cope with
// `pair<const K, V>` value types used by `std::map`. // `pair<const K, V>` value types used by `std::map`.
return error::eval([&] { return apply_derived(const_cast<t0&>(xs.first)); }, if (auto err = dref().apply(const_cast<t0&>(xs.first)))
[&] { return apply_derived(xs.second); }); return err;
return dref().apply(xs.second);
} }
template <class... Ts> template <class... Ts>
...@@ -470,29 +471,31 @@ public: ...@@ -470,29 +471,31 @@ public:
// -- operator() ------------------------------------------------------------- // -- operator() -------------------------------------------------------------
inline error operator()() { error operator()() {
return none; return none;
} }
template <class F, class... Ts> template <class F, class... Ts>
error operator()(meta::save_callback_t<F> x, Ts&&... xs) { error operator()(meta::save_callback_t<F> x, Ts&&... xs) {
error e; // TODO: use `if constexpr` when switching to C++17.
if (Derived::reads_state) if (Derived::reads_state)
e = x.fun(); if (auto err = x.fun())
return e ? e : (*this)(std::forward<Ts>(xs)...); return err;
return dref()(std::forward<Ts>(xs)...);
} }
template <class F, class... Ts> template <class F, class... Ts>
error operator()(meta::load_callback_t<F> x, Ts&&... xs) { error operator()(meta::load_callback_t<F> x, Ts&&... xs) {
error e; // TODO: use `if constexpr` when switching to C++17.
if (Derived::writes_state) if (Derived::writes_state)
e = x.fun(); if (auto err = x.fun())
return e ? e : (*this)(std::forward<Ts>(xs)...); return err;
return dref()(std::forward<Ts>(xs)...);
} }
template <class... Ts> template <class... Ts>
error operator()(const meta::annotation&, Ts&&... xs) { error operator()(const meta::annotation&, Ts&&... xs) {
return (*this)(std::forward<Ts>(xs)...); return dref()(std::forward<Ts>(xs)...);
} }
template <class T, class... Ts> template <class T, class... Ts>
...@@ -501,7 +504,7 @@ public: ...@@ -501,7 +504,7 @@ public:
error error
>::type >::type
operator()(const T&, Ts&&... xs) { operator()(const T&, Ts&&... xs) {
return (*this)(std::forward<Ts>(xs)...); return dref()(std::forward<Ts>(xs)...);
} }
template <class T, class... Ts> template <class T, class... Ts>
...@@ -519,7 +522,7 @@ public: ...@@ -519,7 +522,7 @@ public:
"a loading inspector requires mutable lvalue references"); "a loading inspector requires mutable lvalue references");
if (auto err = apply(deconst(x))) if (auto err = apply(deconst(x)))
return err; return err;
return apply_derived(std::forward<Ts>(xs)...); return dref()(std::forward<Ts>(xs)...);
} }
protected: protected:
...@@ -578,12 +581,6 @@ private: ...@@ -578,12 +581,6 @@ private:
return *static_cast<Derived*>(this); return *static_cast<Derived*>(this);
} }
// Applies `xs...` to `dref()`.
template <class... Ts>
error apply_derived(Ts&&... xs) {
return dref()(std::forward<Ts>(xs)...);
}
execution_unit* context_; execution_unit* context_;
}; };
......
...@@ -38,9 +38,16 @@ std::string deep_to_string(const Ts&... xs) { ...@@ -38,9 +38,16 @@ std::string deep_to_string(const Ts&... xs) {
return result; return result;
} }
/// Wrapper to `deep_to_string` for using the function as an inspector.
struct deep_to_string_t { struct deep_to_string_t {
using result_type = std::string;
static constexpr bool reads_state = true;
static constexpr bool writes_state = false;
template <class... Ts> template <class... Ts>
std::string operator()(const Ts&... xs) const { result_type operator()(const Ts&... xs) const {
return deep_to_string(xs...); return deep_to_string(xs...);
} }
}; };
......
...@@ -19,18 +19,15 @@ ...@@ -19,18 +19,15 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <utility>
#include <functional>
#include "caf/fwd.hpp"
#include "caf/atom.hpp"
#include "caf/none.hpp"
#include "caf/atom.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 +53,8 @@ public: ...@@ -56,8 +53,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 +88,6 @@ using enable_if_has_make_error_t ...@@ -91,13 +88,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 +131,12 @@ public: ...@@ -141,12 +131,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 +146,14 @@ public: ...@@ -156,6 +146,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 +161,75 @@ public: ...@@ -163,47 +161,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 +278,3 @@ bool operator!=(E x, const error& y) { ...@@ -252,4 +278,3 @@ bool operator!=(E x, const error& y) {
} }
} // namespace caf } // namespace caf
...@@ -18,11 +18,11 @@ ...@@ -18,11 +18,11 @@
#include "caf/error.hpp" #include "caf/error.hpp"
#include "caf/config.hpp" #include <utility>
#include "caf/message.hpp"
#include "caf/serializer.hpp"
#include "caf/deserializer.hpp"
#include "caf/deep_to_string.hpp" #include "caf/deep_to_string.hpp"
#include "caf/make_message.hpp"
#include "caf/message.hpp"
namespace caf { namespace caf {
...@@ -55,8 +55,11 @@ error& error::operator=(error&& x) noexcept { ...@@ -55,8 +55,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 +76,12 @@ error& error::operator=(const error& x) { ...@@ -73,13 +76,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 +141,16 @@ int error::compare(uint8_t x, atom_value y) const noexcept { ...@@ -139,6 +141,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 +163,27 @@ void error::clear() noexcept { ...@@ -151,25 +163,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