Commit b749cc1f authored by Dominik Charousset's avatar Dominik Charousset

Prepare type system for streams plus cleanup

parent bc9c2f75
...@@ -27,7 +27,6 @@ set (LIBCAF_CORE_SRCS ...@@ -27,7 +27,6 @@ set (LIBCAF_CORE_SRCS
src/actor_registry.cpp src/actor_registry.cpp
src/actor_system.cpp src/actor_system.cpp
src/actor_system_config.cpp src/actor_system_config.cpp
src/adapter.cpp
src/atom.cpp src/atom.cpp
src/attachable.cpp src/attachable.cpp
src/behavior.cpp src/behavior.cpp
......
...@@ -135,12 +135,6 @@ public: ...@@ -135,12 +135,6 @@ public:
/// Exchange content of `*this` and `other`. /// Exchange content of `*this` and `other`.
void swap(actor& other) noexcept; void swap(actor& other) noexcept;
/// Create a new actor decorator that presets or reorders inputs.
template <class... Ts>
actor bind(Ts&&... xs) const {
return bind_impl(make_message(std::forward<Ts>(xs)...));
}
/// Queries whether this object was constructed using /// Queries whether this object was constructed using
/// `unsafe_actor_handle_init` or is in moved-from state. /// `unsafe_actor_handle_init` or is in moved-from state.
bool unsafe() const { bool unsafe() const {
...@@ -180,8 +174,6 @@ public: ...@@ -180,8 +174,6 @@ public:
private: private:
actor() = default; actor() = default;
actor bind_impl(message msg) const;
inline actor_control_block* get() const noexcept { inline actor_control_block* get() const noexcept {
return ptr_.get(); return ptr_.get();
} }
......
...@@ -54,6 +54,7 @@ namespace caf { ...@@ -54,6 +54,7 @@ namespace caf {
/// during the enqueue operation. Any user-defined policy thus has to dispatch /// during the enqueue operation. Any user-defined policy thus has to dispatch
/// messages with as little overhead as possible, because the dispatching /// messages with as little overhead as possible, because the dispatching
/// runs in the context of the sender. /// runs in the context of the sender.
/// @experimental
class actor_pool : public monitorable_actor { class actor_pool : public monitorable_actor {
public: public:
using uplock = upgrade_lock<detail::shared_spinlock>; using uplock = upgrade_lock<detail::shared_spinlock>;
......
...@@ -97,7 +97,7 @@ struct typed_mpi_access; ...@@ -97,7 +97,7 @@ struct typed_mpi_access;
template <class... Is, class... Ls> template <class... Is, class... Ls>
struct typed_mpi_access<typed_mpi<detail::type_list<Is...>, struct typed_mpi_access<typed_mpi<detail::type_list<Is...>,
detail::type_list<Ls...>>> { output_tuple<Ls...>>> {
std::string operator()(const uniform_type_info_map& types) const { std::string operator()(const uniform_type_info_map& types) const {
static_assert(sizeof...(Is) > 0, "typed MPI without inputs"); static_assert(sizeof...(Is) > 0, "typed MPI without inputs");
static_assert(sizeof...(Ls) > 0, "typed MPI without outputs"); static_assert(sizeof...(Ls) > 0, "typed MPI without outputs");
......
...@@ -97,7 +97,6 @@ ...@@ -97,7 +97,6 @@
#include "caf/typed_event_based_actor.hpp" #include "caf/typed_event_based_actor.hpp"
#include "caf/abstract_composable_behavior.hpp" #include "caf/abstract_composable_behavior.hpp"
#include "caf/decorator/adapter.hpp"
#include "caf/decorator/sequencer.hpp" #include "caf/decorator/sequencer.hpp"
#include "caf/meta/type_name.hpp" #include "caf/meta/type_name.hpp"
......
...@@ -43,6 +43,9 @@ struct signatures_of { ...@@ -43,6 +43,9 @@ struct signatures_of {
using type = typename std::remove_pointer<T>::type::signatures; using type = typename std::remove_pointer<T>::type::signatures;
}; };
template <class T>
using signatures_of_t = typename signatures_of<T>::type;
template <class T> template <class T>
constexpr bool statically_typed() { constexpr bool statically_typed() {
return !std::is_same< return !std::is_same<
...@@ -51,36 +54,6 @@ constexpr bool statically_typed() { ...@@ -51,36 +54,6 @@ constexpr bool statically_typed() {
>::value; >::value;
} }
template <class Signatures, class Input>
struct actor_accepts_message;
template <class Input>
struct actor_accepts_message<none_t, Input> : std::true_type {};
template <class... Ts, class Input>
struct actor_accepts_message<detail::type_list<Ts...>, Input>
: detail::tl_exists<detail::type_list<Ts...>,
detail::input_is<Input>::template eval> {};
template <class Signatures, class Input>
struct response_to;
template <class Input>
struct response_to<none_t, Input> {
using type = none_t;
};
template <class... Ts, class Input>
struct response_to<detail::type_list<Ts...>, Input> {
using type =
typename output_types_of<
typename detail::tl_find<
detail::type_list<Ts...>,
detail::input_is<Input>::template eval
>::type
>::type;
};
template <class T> template <class T>
struct is_void_response : std::false_type {}; struct is_void_response : std::false_type {};
......
...@@ -37,7 +37,7 @@ class composable_behavior_base; ...@@ -37,7 +37,7 @@ class composable_behavior_base;
template <class... Xs, class... Ys> template <class... Xs, class... Ys>
class composable_behavior_base<typed_mpi<detail::type_list<Xs...>, class composable_behavior_base<typed_mpi<detail::type_list<Xs...>,
detail::type_list<Ys...>>> { output_tuple<Ys...>>> {
public: public:
virtual ~composable_behavior_base() noexcept { virtual ~composable_behavior_base() noexcept {
// nop // nop
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_COMPOSED_TYPE_HPP
#define CAF_COMPOSED_TYPE_HPP
#include "caf/fwd.hpp"
#include "caf/replies_to.hpp"
#include "caf/detail/type_list.hpp"
namespace caf {
/// Computes the type for f*g (actor composition).
///
/// ~~~
/// let output_type x = case x of Stream y -> y ; Single y -> y
///
/// let propagate_stream from to = case from of
/// Stream _ -> Stream (output_type to)
/// Single _ -> to
/// let composed_type f g =
/// [(fst x, propagate_stream (snd x) (snd y)) | x <- g, y <- f,
/// output_type (snd x) == fst y]
/// ~~~
///
/// This class implements the list comprehension above in a
/// single shot with worst case n*m template instantiations using an
/// inner and outer loop, where n is the size
/// of Xs and m the size of Ys. Zs is a helper that models the
/// "inner loop variable" for generating the cross product of Xs and Ys.
/// The helper function propagate_stream is integrated into the loop with
/// four cases for the matching case. Rs collects the results.
template <class Xs, class Ys, class Zs, class Rs>
struct composed_type;
// end of outer loop over Xs
template <class Ys, class Zs, class... Rs>
struct composed_type<detail::type_list<>, Ys, Zs, detail::type_list<Rs...>> {
using type = typed_actor<Rs...>;
};
// end of inner loop Ys (Zs)
template <class X, class... Xs, class Ys, class Rs>
struct composed_type<detail::type_list<X, Xs...>, Ys, detail::type_list<>, Rs>
: composed_type<detail::type_list<Xs...>, Ys, Ys, Rs> {};
// case #1
template <class... In, class... Out, class... Xs, class Ys,
class... MapsTo, class... Zs, class... Rs>
struct composed_type<detail::type_list<typed_mpi<detail::type_list<In...>,
output_tuple<Out...>>, Xs...>,
Ys,
detail::type_list<typed_mpi<detail::type_list<Out...>,
output_tuple<MapsTo...>>, Zs...>,
detail::type_list<Rs...>>
: composed_type<detail::type_list<Xs...>, Ys, Ys,
detail::type_list<Rs..., typed_mpi<detail::type_list<In...>,
output_tuple<MapsTo...>>>> {};
// case #2
template <class... In, class... Out, class... Xs, class Ys,
class... MapsTo, class... Zs, class... Rs>
struct composed_type<detail::type_list<typed_mpi<detail::type_list<In...>,
output_tuple<Out...>>, Xs...>,
Ys,
detail::type_list<typed_mpi<detail::type_list<Out...>,
output_stream<MapsTo...>>,
Zs...>,
detail::type_list<Rs...>>
: composed_type<detail::type_list<Xs...>, Ys, Ys,
detail::type_list<Rs...,
typed_mpi<detail::type_list<In...>,
output_stream<MapsTo...>>>> {};
// case #3
template <class... In, class... Out, class... Xs, class Ys,
class... MapsTo, class... Zs, class... Rs>
struct composed_type<detail::type_list<typed_mpi<detail::type_list<In...>,
output_stream<Out...>>, Xs...>,
Ys,
detail::type_list<typed_mpi<detail::type_list<Out...>,
output_tuple<MapsTo...>>, Zs...>,
detail::type_list<Rs...>>
: composed_type<detail::type_list<Xs...>, Ys, Ys,
detail::type_list<Rs...,
typed_mpi<detail::type_list<In...>,
output_stream<MapsTo...>>>> {};
// case #4
template <class... In, class... Out, class... Xs, class Ys,
class... MapsTo, class... Zs, class... Rs>
struct composed_type<detail::type_list<typed_mpi<detail::type_list<In...>,
output_stream<Out...>>, Xs...>,
Ys,
detail::type_list<typed_mpi<detail::type_list<Out...>,
output_stream<MapsTo...>>, Zs...>,
detail::type_list<Rs...>>
: composed_type<detail::type_list<Xs...>, Ys, Ys,
detail::type_list<Rs...,
typed_mpi<detail::type_list<In...>,
output_stream<MapsTo...>>>> {};
// default case (recurse over Zs)
template <class In, class Out, class... Xs, class Ys,
class Unrelated, class MapsTo, class... Zs, class Rs>
struct composed_type<detail::type_list<typed_mpi<In, Out>, Xs...>,
Ys,
detail::type_list<typed_mpi<Unrelated, MapsTo>, Zs...>,
Rs>
: composed_type<detail::type_list<typed_mpi<In, Out>, Xs...>,
Ys, detail::type_list<Zs...>, Rs> {};
/// Convenience type alias.
/// @relates composed_type
template <class F, class G>
using composed_type_t =
typename composed_type<G, F, F, detail::type_list<>>::type;
} // namespace caf
#endif // CAF_COMPOSED_TYPE_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DECORATOR_ADAPTER_HPP
#define CAF_DECORATOR_ADAPTER_HPP
#include "caf/actor.hpp"
#include "caf/message.hpp"
#include "caf/attachable.hpp"
#include "caf/monitorable_actor.hpp"
namespace caf {
namespace decorator {
/// An actor decorator implementing `std::bind`-like compositions.
/// Bound actors are hidden actors. A bound actor exits when its
/// decorated actor exits. The decorated actor has no dependency
/// on the bound actor by default, and exit of a bound actor has
/// no effect on the decorated actor. Bound actors are hosted on
/// the same actor system and node as decorated actors.
class adapter : public monitorable_actor {
public:
adapter(strong_actor_ptr decorated, message msg);
// non-system messages are processed and then forwarded;
// system messages are handled and consumed on the spot;
// in either case, the processing is done synchronously
void enqueue(mailbox_element_ptr what, execution_unit* host) override;
protected:
void on_cleanup() override;
private:
strong_actor_ptr decorated_;
message merger_;
};
} // namespace decorator
} // namespace caf
#endif // CAF_DECORATOR_ADAPTER_HPP
...@@ -17,56 +17,105 @@ ...@@ -17,56 +17,105 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#ifndef CAF_DETAIL_BOXED_HPP #ifndef CAF_DEDUCE_MPI_HPP
#define CAF_DETAIL_BOXED_HPP #define CAF_DEDUCE_MPI_HPP
#include "caf/detail/wrapped.hpp" #include <type_traits>
#include "caf/fwd.hpp"
#include "caf/expected.hpp"
#include "caf/optional.hpp"
#include "caf/replies_to.hpp"
#include "caf/detail/implicit_conversions.hpp"
namespace caf { namespace caf {
namespace detail { namespace detail {
// dmi = deduce_mpi_implementation
template <class T> template <class T>
struct boxed { struct dmi;
constexpr boxed() {
// nop // case #1: function returning a single value
} template <class Y, class... Xs>
using type = detail::wrapped<T>; struct dmi<Y (Xs...)> {
using type = typed_mpi<type_list<typename std::decay<Xs>::type...>,
output_tuple<implicit_conversions_t<Y>>>;
}; };
template <class T> // case #2a: function returning a result<...>
struct boxed<detail::wrapped<T>> { template <class... Ys, class... Xs>
constexpr boxed() { struct dmi<result<Ys...> (Xs...)> {
// nop using type = typed_mpi<type_list<typename std::decay<Xs>::type...>,
} output_tuple<implicit_conversions_t<Ys>...>>;
using type = detail::wrapped<T>;
}; };
template <class T> // case #2b: function returning a std::tuple<...>
struct is_boxed { template <class... Ys, class... Xs>
static constexpr bool value = false; struct dmi<std::tuple<Ys...> (Xs...)> {
using type = typed_mpi<type_list<typename std::decay<Xs>::type...>,
output_tuple<implicit_conversions_t<Ys>...>>;
}; };
template <class T> // case #2c: function returning a std::tuple<...>
struct is_boxed<detail::wrapped<T>> { template <class... Ys, class... Xs>
static constexpr bool value = true; struct dmi<delegated<Ys...> (Xs...)> {
using type = typed_mpi<type_list<typename std::decay<Xs>::type...>,
output_tuple<implicit_conversions_t<Ys>...>>;
}; };
template <class T> // case #2d: function returning a typed_response_promise<...>
struct is_boxed<detail::wrapped<T>()> { template <class... Ys, class... Xs>
static constexpr bool value = true; struct dmi<typed_response_promise<Ys...> (Xs...)> {
using type = typed_mpi<type_list<typename std::decay<Xs>::type...>,
output_tuple<implicit_conversions_t<Ys>...>>;
}; };
// case #3: function returning an optional<>
template <class Y, class... Xs>
struct dmi<optional<Y> (Xs...)> : dmi<Y (Xs...)> {};
// case #4: function returning an expected<>
template <class Y, class... Xs>
struct dmi<expected<Y> (Xs...)> : dmi<Y (Xs...)> {};
// -- dmfou = deduce_mpi_function_object_unboxing
template <class T, bool isClass = std::is_class<T>::value>
struct dmfou;
// case #1: const member function pointer
template <class C, class Result, class... Ts>
struct dmfou<Result (C::*)(Ts...) const, false> : dmi<Result (Ts...)> {};
// case #2: member function pointer
template <class C, class Result, class... Ts>
struct dmfou<Result (C::*)(Ts...), false> : dmi<Result (Ts...)> {};
// case #3: good ol' function
template <class Result, class... Ts>
struct dmfou<Result(Ts...), false> : dmi<Result (Ts...)> {};
template <class T> template <class T>
struct is_boxed<detail::wrapped<T>(&)()> { struct dmfou<T, true> : dmfou<decltype(&T::operator()), false> {};
static constexpr bool value = true;
};
// this specialization leaves timeout definitions untouched,
// later stages such as interface_mismatch need to deal with them later
template <class T> template <class T>
struct is_boxed<detail::wrapped<T>(*)()> { struct dmfou<timeout_definition<T>, true> {
static constexpr bool value = true; using type = timeout_definition<T>;
}; };
template <class T>
struct dmfou<trivial_match_case<T>, true> : dmfou<T> {};
} // namespace detail } // namespace detail
/// Deduces the message passing interface from a function object.
template <class T>
using deduce_mpi_t = typename detail::dmfou<typename std::decay<T>::type>::type;
} // namespace caf } // namespace caf
#endif // CAF_DETAIL_BOXED_HPP #endif // CAF_DEDUCE_MPI_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_CTM_HPP
#define CAF_DETAIL_CTM_HPP
#include <tuple>
#include "caf/optional.hpp"
#include "caf/delegated.hpp"
#include "caf/replies_to.hpp"
#include "caf/typed_response_promise.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/typed_actor_util.hpp"
namespace caf {
namespace detail {
// CTM: Compile-Time Match
// left hand side is the MPI we are comparing to, this is *not* commutative
template <class A, class B>
struct ctm_cmp : std::false_type { };
template <class In, class Out>
struct ctm_cmp<typed_mpi<In, Out>,
typed_mpi<In, Out>> {
static constexpr bool value = true;
};
template <class In, class OutList>
struct ctm_cmp<typed_mpi<In, OutList>,
typed_mpi<In, type_list<typed_continue_helper<OutList>>>>
: std::true_type { };
template <class In, class Out>
struct ctm_cmp<typed_mpi<In, type_list<Out>>,
typed_mpi<In, type_list<optional<Out>>>>
: std::true_type { };
template <class In, class... Ts>
struct ctm_cmp<typed_mpi<In, type_list<Ts...>>,
typed_mpi<In, type_list<typed_response_promise<Ts...>>>>
: std::true_type { };
template <class In, class... Ts>
struct ctm_cmp<typed_mpi<In, type_list<Ts...>>,
typed_mpi<In, type_list<optional<std::tuple<Ts...>>>>>
: std::true_type { };
template <class In, class... Ts>
struct ctm_cmp<typed_mpi<In, type_list<Ts...>>,
typed_mpi<In, type_list<result<Ts...>>>>
: std::true_type { };
template <class In, class T>
struct ctm_cmp<typed_mpi<In, type_list<T>>,
typed_mpi<In, type_list<expected<T>>>>
: std::true_type { };
template <class In, class Out>
struct ctm_cmp<typed_mpi<In, Out>,
typed_mpi<In, type_list<skip_t>>>
: std::true_type { };
template <class In, class... Ts>
struct ctm_cmp<typed_mpi<In, type_list<Ts...>>,
typed_mpi<In, type_list<delegated<Ts...>>>>
: std::true_type { };
template <class Xs, class Ys>
constexpr int ctm_impl(int pos) {
return tl_empty<Xs>::value
? -1 // consumed each X
: (tl_exists<Ys, tbind<ctm_cmp, typename tl_head<Xs>::type>::template type>::value
? ctm_impl<typename tl_tail<Xs>::type, Ys>(pos + 1)
: pos);
}
template <class Xs, class Ys>
struct ctm {
// -3 means too many handler, -2 means too few, -1 means OK, everything else
// mismatch at that position
static constexpr size_t num_xs = tl_size<Xs>::value;
static constexpr size_t num_ys = tl_size<Ys>::value;
static constexpr int value = num_xs == num_ys ? ctm_impl<Xs, Ys>(0)
: (num_xs < num_ys ? -2 : -3);
};
template <class Xs, class Ys>
constexpr int ctm<Xs, Ys>::value;
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_CTM_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_DISABLABLE_DELETE_HPP
#define CAF_DETAIL_DISABLABLE_DELETE_HPP
namespace caf {
namespace detail {
class disablable_delete {
public:
constexpr disablable_delete() : enabled_(true) {}
inline void disable() { enabled_ = false; }
inline void enable() { enabled_ = true; }
template <class T>
inline void operator()(T* ptr) {
if (enabled_) delete ptr;
}
private:
bool enabled_;
};
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_DISABLABLE_DELETE_HPP
...@@ -31,42 +31,83 @@ namespace detail { ...@@ -31,42 +31,83 @@ namespace detail {
template <class T> template <class T>
struct implicit_conversions { struct implicit_conversions {
// convert C strings to std::string if possible using type =
using step1 =
typename std::conditional< typename std::conditional<
std::is_convertible<T, std::string>::value, std::is_convertible<T, error>::value,
std::string, error,
T T
>::type; >::type;
// convert C strings to std::u16string if possible };
using step2 =
typename std::conditional< template <class T>
std::is_convertible<step1, std::u16string>::value, struct implicit_conversions<T*> {
std::u16string,
step1
>::type;
// convert C strings to std::u32string if possible
using step3 =
typename std::conditional<
std::is_convertible<step2, std::u32string>::value,
std::u32string,
step2
>::type;
using step4 =
typename std::conditional<
std::is_convertible<step3, abstract_actor*>::value
|| std::is_same<scoped_actor, step3>::value,
actor,
step3
>::type;
using type = using type =
typename std::conditional< typename std::conditional<
std::is_convertible<step4, error>::value, std::is_base_of<abstract_actor, T>::value,
error, actor,
step4 T*
>::type; >::type;
}; };
template <>
struct implicit_conversions<char*> {
using type = std::string;
};
template <size_t N>
struct implicit_conversions<char[N]>
: implicit_conversions<char*> {};
template <>
struct implicit_conversions<const char*>
: implicit_conversions<char*> {};
template <size_t N>
struct implicit_conversions<const char[N]>
: implicit_conversions<char*> {};
template <>
struct implicit_conversions<char16_t*> {
using type = std::u16string;
};
template <size_t N>
struct implicit_conversions<char16_t[N]>
: implicit_conversions<char16_t*> {};
template <>
struct implicit_conversions<const char16_t*>
: implicit_conversions<char16_t*> {};
template <size_t N>
struct implicit_conversions<const char16_t[N]>
: implicit_conversions<char16_t*> {};
template <>
struct implicit_conversions<char32_t*> {
using type = std::u16string;
};
template <size_t N>
struct implicit_conversions<char32_t[N]>
: implicit_conversions<char32_t*> {};
template <>
struct implicit_conversions<const char32_t*>
: implicit_conversions<char32_t*> {};
template <size_t N>
struct implicit_conversions<const char32_t[N]>
: implicit_conversions<char32_t*> {};
template <>
struct implicit_conversions<scoped_actor> {
using type = actor;
};
template <class T>
using implicit_conversions_t = typename implicit_conversions<T>::type;
template <class T> template <class T>
struct strip_and_convert { struct strip_and_convert {
using type = using type =
...@@ -79,6 +120,9 @@ struct strip_and_convert { ...@@ -79,6 +120,9 @@ struct strip_and_convert {
>::type; >::type;
}; };
template <class T>
using strip_and_convert_t = typename strip_and_convert<T>::type;
} // namespace detail } // namespace detail
} // namespace caf } // namespace caf
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_LEFT_OR_RIGHT_HPP
#define CAF_DETAIL_LEFT_OR_RIGHT_HPP
#include "caf/unit.hpp"
namespace caf {
namespace detail {
/// Evaluates to `Right` if `Left` == unit_t, `Left` otherwise.
template <class Left, typename Right>
struct left_or_right {
using type = Left;
};
template <class Right>
struct left_or_right<unit_t, Right> {
using type = Right;
};
template <class Right>
struct left_or_right<unit_t&, Right> {
using type = Right;
};
template <class Right>
struct left_or_right<const unit_t&, Right> {
using type = Right;
};
/// Evaluates to `Right` if `Left` != unit_t, `unit_t` otherwise.
template <class Left, typename Right>
struct if_not_left {
using type = unit_t;
};
template <class Right>
struct if_not_left<unit_t, Right> {
using type = Right;
};
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_LEFT_OR_RIGHT_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_MPI_BIND_HPP
#define CAF_DETAIL_MPI_BIND_HPP
#include <functional>
#include "caf/none.hpp"
#include "caf/replies_to.hpp"
#include "caf/detail/type_list.hpp"
namespace caf {
namespace detail {
template <class T, size_t Pos>
class mpi_bind_sig_arg_t {
// nop
};
template <class T,
int PlaceholderValue,
size_t Pos,
size_t Size,
bool InRange = PlaceholderValue <= static_cast<int>(Size)>
struct mpi_bind_sig_single {
using type =
mpi_bind_sig_arg_t<
typename detail::tl_at<T, Pos>::type,
PlaceholderValue - 1
>;
};
template <class T, size_t Pos, size_t Size>
struct mpi_bind_sig_single<T, 0, Pos, Size, true> {
using type = void;
};
template <class T, int PlaceholderValue, size_t Pos, size_t Size>
struct mpi_bind_sig_single<T, PlaceholderValue, Pos, Size, false> {
using type = none_t;
};
template <size_t I, size_t Size, class Args, class In, class... Ts>
struct mpi_bind_sig_impl {
using sub =
typename mpi_bind_sig_single<
In,
std::is_placeholder<typename detail::tl_at<Args, I>::type>::value,
I,
Size
>::type;
using type =
typename std::conditional<
std::is_same<sub, none_t>::value,
void,
typename std::conditional<
std::is_same<sub, void>::value,
typename mpi_bind_sig_impl<I + 1, Size, Args, In, Ts...>::type,
typename mpi_bind_sig_impl<I + 1, Size, Args, In, Ts..., sub>::type
>::type
>::type;
};
template <size_t Size, class Args, class In>
struct mpi_bind_sig_impl<Size, Size, Args, In> {
using type = void;
};
template <size_t Size, class Args, class In, class T, class... Ts>
struct mpi_bind_sig_impl<Size, Size, Args, In, T, Ts...> {
using type = detail::type_list<T, Ts...>;
};
template <size_t I, class In, class Out, class... Ts>
struct mpi_bind_sort;
template <size_t I, class Out, class... Ts>
struct mpi_bind_sort<I, detail::type_list<>, Out, Ts...> {
using type = typed_mpi<detail::type_list<Ts...>, Out>;
};
template <size_t I, size_t X, class Arg, class... Args, class Out, class... Ts>
struct mpi_bind_sort<I, detail::type_list<mpi_bind_sig_arg_t<Arg, X>, Args...>,
Out, Ts...> {
using type =
typename mpi_bind_sort<
I,
detail::type_list<Args..., mpi_bind_sig_arg_t<Arg, X>>,
Out,
Ts...
>::type;
};
template <size_t I, class Arg, class... Args, class Out, class... Ts>
struct mpi_bind_sort<I, detail::type_list<mpi_bind_sig_arg_t<Arg, I>, Args...>,
Out, Ts...> {
using type =
typename mpi_bind_sort<
I + 1,
detail::type_list<Args...>,
Out,
Ts...,
Arg
>::type;
};
template <class Sig, class Args>
struct mpi_bind_sig {
static constexpr size_t num_args = detail::tl_size<Args>::value;
using type =
typename mpi_bind_sort<
0,
typename mpi_bind_sig_impl<
0,
num_args,
Args,
typename Sig::input_types
>::type,
typename Sig::output_types
>::type;
};
template <template <class...> class Target, class Sigs, class... Args>
struct mpi_bind;
template <template <class...> class Target, class... Sigs, class... Ts>
struct mpi_bind<Target, detail::type_list<Sigs...>, Ts...> {
using args = detail::type_list<Ts...>;
using bound_sigs = detail::type_list<typename mpi_bind_sig<Sigs, args>::type...>;
// drop any mismatch (void) and rebuild typed actor handle
using type =
typename detail::tl_apply<
typename detail::tl_filter_not_type<bound_sigs, void>::type,
Target
>::type;
};
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_MPI_BIND_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_PURGE_REFS_HPP
#define CAF_DETAIL_PURGE_REFS_HPP
#include <functional>
#include "caf/detail/type_traits.hpp"
namespace caf {
namespace detail {
template <class T>
struct purge_refs_impl {
using type = T;
};
template <class T>
struct purge_refs_impl<std::reference_wrapper<T>> {
using type = T;
};
template <class T>
struct purge_refs_impl<std::reference_wrapper<const T>> {
using type = T;
};
/// Removes references and reference wrappers.
template <class T>
struct purge_refs {
using type =
typename purge_refs_impl<
typename std::decay<T>::type
>::type;
};
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_PURGE_REFS_HPP
...@@ -41,34 +41,6 @@ class typed_continue_helper; ...@@ -41,34 +41,6 @@ class typed_continue_helper;
namespace caf { namespace caf {
namespace detail { namespace detail {
template <class T>
struct unwrap_std_tuple {
using type = type_list<T>;
};
template <class... Ts>
struct unwrap_std_tuple<std::tuple<Ts...>> {
using type = type_list<Ts...>;
};
template <class T>
struct deduce_lhs_result {
using type = typename unwrap_std_tuple<T>::type;
};
template <class T>
struct deduce_rhs_result {
using type = type_list<>;
};
template <class T>
struct deduce_mpi {
using result = typename implicit_conversions<typename T::result_type>::type;
using arg_t = typename tl_map<typename T::arg_types, std::decay>::type;
using type = typed_mpi<arg_t,
typename deduce_lhs_result<result>::type>;
};
template <class Arguments, class Signature> template <class Arguments, class Signature>
struct input_is_eval_impl : std::false_type {}; struct input_is_eval_impl : std::false_type {};
...@@ -104,30 +76,35 @@ struct type_checker<message, F> { ...@@ -104,30 +76,35 @@ struct type_checker<message, F> {
} }
}; };
template <int X, int Pos, class A> /// Generates an error using static_assert on an interface mismatch.
/// @tparam NumMessageHandlers The number of message handlers
/// provided by the user.
/// @tparam Pos The index at which an error was detected or a negative value
/// if too many or too few handlers were provided.
/// @tparam RemainingXs The remaining deduced messaging interfaces of the
/// provided message handlers at the time of the error.
/// @tparam RemainingYs The remaining unimplemented message handler
/// signatures at the time of the error.
template <int NumMessageHandlers, int Pos, class RemainingXs, class RemainingYs>
struct static_error_printer { struct static_error_printer {
static_assert(X != Pos, "unexpected handler some position > 20"); static_assert(NumMessageHandlers == Pos,
"unexpected handler some index > 20");
}; };
template <int X, class A> template <int N, class Xs, class Ys>
struct static_error_printer<X, -3, A> { struct static_error_printer<N, -2, Xs, Ys> {
static_assert(X == -1, "too few message handlers defined"); static_assert(N == -1, "too many message handlers");
}; };
template <int X, class A> template <int N, class Xs, class Ys>
struct static_error_printer<X, -2, A> { struct static_error_printer<N, -1, Xs, Ys> {
static_assert(X == -1, "too many message handlers defined"); static_assert(N == -1, "not enough message handlers");
}; };
template <int X, class A> #define CAF_STATICERR(x) \
struct static_error_printer<X, -1, A> { template <int N, class Xs, class Ys> \
// everything' fine struct static_error_printer<N, x, Xs, Ys> { \
}; static_assert(N == x, "unexpected handler at index " #x ); \
#define CAF_STATICERR(Pos) \
template <int X, class A> \
struct static_error_printer< X, Pos, A > { \
static_assert(X == -1, "unexpected handler at position " #Pos ); \
} }
CAF_STATICERR( 0); CAF_STATICERR( 1); CAF_STATICERR( 2); CAF_STATICERR( 0); CAF_STATICERR( 1); CAF_STATICERR( 2);
...@@ -138,110 +115,6 @@ CAF_STATICERR(12); CAF_STATICERR(13); CAF_STATICERR(14); ...@@ -138,110 +115,6 @@ CAF_STATICERR(12); CAF_STATICERR(13); CAF_STATICERR(14);
CAF_STATICERR(15); CAF_STATICERR(16); CAF_STATICERR(17); CAF_STATICERR(15); CAF_STATICERR(16); CAF_STATICERR(17);
CAF_STATICERR(18); CAF_STATICERR(19); CAF_STATICERR(20); CAF_STATICERR(18); CAF_STATICERR(19); CAF_STATICERR(20);
template <class A, class B, template <class, class> class Predicate>
struct static_asserter {
static void verify_match() {
static constexpr int x = Predicate<A, B>::value;
using type_x = typename tl_at<B, (x < 0 ? 0 : x)>::type;
static_error_printer<x, x, type_x> dummy;
static_cast<void>(dummy);
}
};
template <class T>
struct lifted_result_type {
using type = type_list<typename implicit_conversions<T>::type>;
};
template <class... Ts>
struct lifted_result_type<std::tuple<Ts...>> {
using type = type_list<Ts...>;
};
template <class T>
struct deduce_lifted_output_type {
using type = T;
};
template <class R>
struct deduce_lifted_output_type<type_list<typed_continue_helper<R>>> {
using type = typename lifted_result_type<R>::type;
};
template <class Signatures, class InputTypes>
struct deduce_output_type_impl {
using signature =
typename tl_find<
Signatures,
input_is<InputTypes>::template eval
>::type;
static_assert(!std::is_same<signature, none_t>::value,
"typed actor does not support given input");
using type = typename signature::output_types;
// generates the appropriate `delegated<...>` type from given signatures
using delegated_type = typename detail::tl_apply<type, delegated>::type;
// generates the appropriate `std::tuple<...>` type from given signature
using tuple_type = typename detail::tl_apply<type, std::tuple>::type;
};
template <class InputTypes>
struct deduce_output_type_impl<none_t, InputTypes> {
using type = message;
using delegated_type = delegated<message>;
using tuple_type = std::tuple<message>;
};
template <class Handle, class InputTypes>
struct deduce_output_type
: deduce_output_type_impl<typename Handle::signatures, InputTypes> {
// nop
};
template <class T, class InputTypes>
struct deduce_output_type<T*, InputTypes>
: deduce_output_type_impl<typename T::signatures, InputTypes> {
// nop
};
template <class... Ts>
struct common_result_type;
template <class T>
struct common_result_type<T> {
using type = T;
};
template <class T, class... Us>
struct common_result_type<T, T, Us...> {
using type = typename common_result_type<T, Us...>::type;
};
template <class T1, class T2, class... Us>
struct common_result_type<T1, T2, Us...> {
using type = void;
};
template <class OrigSigs, class DestSigs, class ArgTypes>
struct sender_signature_checker {
static void check() {
using dest_output_types =
typename deduce_output_type<
DestSigs, ArgTypes
>::type;
sender_signature_checker<DestSigs, OrigSigs, dest_output_types>::check();
}
};
template <class OrigSigs, class DestSigs>
struct sender_signature_checker<OrigSigs, DestSigs, detail::type_list<void>> {
static void check() {}
};
template <class OrigSigs, class DestSigs>
struct sender_signature_checker<OrigSigs, DestSigs, detail::type_list<>> {
static void check() {}
};
template <class... Ts> template <class... Ts>
struct extend_with_helper; struct extend_with_helper;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_UNBOXED_HPP
#define CAF_DETAIL_UNBOXED_HPP
#include <memory>
#include "caf/atom.hpp"
#include "caf/detail/wrapped.hpp"
namespace caf {
namespace detail {
// strips `wrapped` and converts `atom_constant` to `atom_value`
template <class T>
struct unboxed {
using type = T;
};
template <class T>
struct unboxed<detail::wrapped<T>> {
using type = typename detail::wrapped<T>::type;
};
template <class T>
struct unboxed<detail::wrapped<T>(&)()> {
using type = typename detail::wrapped<T>::type;
};
template <class T>
struct unboxed<detail::wrapped<T>()> {
using type = typename detail::wrapped<T>::type;
};
template <class T>
struct unboxed<detail::wrapped<T>(*)()> {
using type = typename detail::wrapped<T>::type;
};
template <atom_value V>
struct unboxed<atom_constant<V>> {
using type = atom_value;
};
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_UNBOXED_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_WRAPPED_HPP
#define CAF_DETAIL_WRAPPED_HPP
namespace caf {
namespace detail {
template <class T>
struct wrapped {
constexpr wrapped() {
// nop
}
using type = T;
};
template <class T>
struct wrapped<wrapped<T>> {
constexpr wrapped() {
// nop
}
using type = typename wrapped<T>::type;
};
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_WRAPPED_HPP
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "caf/expected.hpp" #include "caf/expected.hpp"
#include "caf/typed_actor.hpp" #include "caf/typed_actor.hpp"
#include "caf/scoped_actor.hpp" #include "caf/scoped_actor.hpp"
#include "caf/response_type.hpp"
namespace caf { namespace caf {
...@@ -111,6 +112,10 @@ struct function_view_flattened_result<std::tuple<void>> { ...@@ -111,6 +112,10 @@ struct function_view_flattened_result<std::tuple<void>> {
using type = unit_t; using type = unit_t;
}; };
template <class T>
using function_view_flattened_result_t =
typename function_view_flattened_result<T>::type;
template <class T> template <class T>
struct function_view_result { struct function_view_result {
T value; T value;
...@@ -171,15 +176,13 @@ public: ...@@ -171,15 +176,13 @@ public:
/// Sends a request message to the assigned actor and returns the result. /// Sends a request message to the assigned actor and returns the result.
template <class... Ts, template <class... Ts,
class R = class R =
typename function_view_flattened_result< function_view_flattened_result_t<
typename detail::deduce_output_type< typename response_type<
type, typename type::signatures,
detail::type_list< detail::implicit_conversions_t<
typename detail::implicit_conversions< typename std::decay<Ts>::type
typename std::decay<Ts>::type >...
>::type...> >::tuple_type>>
>::tuple_type
>::type>
expected<R> operator()(Ts&&... xs) { expected<R> operator()(Ts&&... xs) {
if (impl_.unsafe()) if (impl_.unsafe())
return sec::bad_function_call; return sec::bad_function_call;
......
...@@ -32,9 +32,12 @@ template <class> class optional; ...@@ -32,9 +32,12 @@ template <class> class optional;
template <class> class expected; template <class> class expected;
template <class> class intrusive_ptr; template <class> class intrusive_ptr;
template <class> class behavior_type_of; template <class> class behavior_type_of;
template <class> class trivial_match_case;
template <class> class weak_intrusive_ptr; template <class> class weak_intrusive_ptr;
template <class> class typed_continue_helper; template <class> class typed_continue_helper;
template <class> struct timeout_definition;
// -- 3 param templates -------------------------------------------------------- // -- 3 param templates --------------------------------------------------------
template <class, class, int> class actor_cast_access; template <class, class, int> class actor_cast_access;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_INTERFACE_MISMATCH_HPP
#define CAF_INTERFACE_MISMATCH_HPP
#include "caf/fwd.hpp"
#include "caf/replies_to.hpp"
#include "caf/detail/type_list.hpp"
namespace caf {
namespace detail {
// imi = interface_mismatch_implementation
// Precondition: Pos == 0 && len(Xs) == len(Ys) && len(Zs) == 0
// Iterate over Xs to find a match in Ys; Zs is used as temporary storage
// to iterate over Ys. On a match, the element is removed from Xs and Ys and
// Zs are prepended to Ys again for next iteration.
// Evaluates to:
// * len(Xs) if interface check succeeds
// * Pos on a mismatch (incremented per iteration to reflect position in Xs)
template <int Pos, class Xs, class Ys, class Zs>
struct imi;
// end of recursion: success (consumed both lists)
template <int Pos>
struct imi<Pos, type_list<>, type_list<>, type_list<>> {
static constexpr int value = Pos;
using xs = type_list<>;
using ys = type_list<>;
};
// end of recursion: success (consumed both lists, except the timeout)
template <int Pos, class X>
struct imi<Pos, type_list<timeout_definition<X>>, type_list<>, type_list<>> {
static constexpr int value = Pos + 1; // count timeout def. as consumed
using xs = type_list<>;
using ys = type_list<>;
};
// end of recursion: failure (consumed all Xs but not all Ys)
template <int Pos, class Yin, class Yout, class... Ys>
struct imi<Pos, type_list<>, type_list<typed_mpi<Yin, Yout>, Ys...>, type_list<>> {
static constexpr int value = -1;
using xs = type_list<>;
using ys = type_list<typed_mpi<Yin, Yout>, Ys...>;
};
// end of recursion: failure (consumed all Ys but not all Xs)
template <int Pos, class Xin, class Xout, class... Xs>
struct imi<Pos, type_list<typed_mpi<Xin, Xout>, Xs...>, type_list<>, type_list<>> {
static constexpr int value = -2;
using xs = type_list<typed_mpi<Xin, Xout>, Xs...>;
using ys = type_list<>;
};
// end of recursion: failure (consumed all Ys except timeout but not all Xs)
template <int Pos, class X, class Y, class... Ys>
struct imi<Pos, type_list<timeout_definition<X>>,
type_list<Y, Ys...>, type_list<>> {
static constexpr int value = -2;
using xs = type_list<>;
using ys = type_list<Y, Ys...>;
};
// case #1: exact match
template <int Pos, class In, class Out, class... Xs, class... Ys, class... Zs>
struct imi<Pos,
type_list<typed_mpi<In, Out>, Xs...>,
type_list<typed_mpi<In, Out>, Ys...>,
type_list<Zs...>>
: imi<Pos + 1, type_list<Xs...>, type_list<Zs..., Ys...>, type_list<>> {};
// case #2: match with skip_t
template <int Pos, class In, class... Xs, class Out, class... Ys, class... Zs>
struct imi<Pos,
type_list<typed_mpi<In, output_tuple<skip_t>>, Xs...>,
type_list<typed_mpi<In, Out>, Ys...>,
type_list<Zs...>>
: imi<Pos + 1, type_list<Xs...>, type_list<Zs..., Ys...>, type_list<>> {};
// case #3: no match at position
template <int Pos, class Xin, class Xout, class... Xs,
class Yin, class Yout, class... Ys, class... Zs>
struct imi<Pos,
type_list<typed_mpi<Xin, Xout>, Xs...>,
type_list<typed_mpi<Yin, Yout>, Ys...>,
type_list<Zs...>>
: imi<Pos,
type_list<typed_mpi<Xin, Xout>, Xs...>,
type_list<Ys...>,
type_list<Zs..., typed_mpi<Yin, Yout>>> {};
// case #4: no match (error)
template <int Pos, class X, class... Xs, class... Zs>
struct imi<Pos, type_list<X, Xs...>, type_list<>, type_list<Zs...>> {
static constexpr int value = Pos;
using xs = type_list<X, Xs...>;
using ys = type_list<Zs...>;
};
} // namespace detail
/// Scans two typed MPI lists for compatibility, returning the index of the
/// first mismatch. Returns the number of elements on a match.
/// @pre len(Found) == len(Expected)
template <class Found, class Expected>
using interface_mismatch_t = detail::imi<0, Found, Expected,
detail::type_list<>>;
} // namespace caf
#endif // CAF_INTERFACE_MISMATCH_HPP
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#include "caf/typed_actor.hpp" #include "caf/typed_actor.hpp"
#include "caf/actor_config.hpp" #include "caf/actor_config.hpp"
#include "caf/actor_system.hpp" #include "caf/actor_system.hpp"
#include "caf/response_type.hpp"
#include "caf/spawn_options.hpp" #include "caf/spawn_options.hpp"
#include "caf/abstract_actor.hpp" #include "caf/abstract_actor.hpp"
#include "caf/abstract_group.hpp" #include "caf/abstract_group.hpp"
...@@ -311,13 +312,9 @@ public: ...@@ -311,13 +312,9 @@ public:
template <message_priority P = message_priority::normal, template <message_priority P = message_priority::normal,
class Handle = actor, class... Ts> class Handle = actor, class... Ts>
typename detail::deduce_output_type< typename response_type<
Handle, typename Handle::signatures,
detail::type_list< detail::implicit_conversions_t<typename std::decay<Ts>::type>...
typename detail::implicit_conversions<
typename std::decay<Ts>::type
>::type...
>
>::delegated_type >::delegated_type
delegate(const Handle& dest, Ts&&... xs) { delegate(const Handle& dest, Ts&&... xs) {
static_assert(sizeof...(Ts) > 0, "nothing to delegate"); static_assert(sizeof...(Ts) > 0, "nothing to delegate");
...@@ -326,9 +323,7 @@ public: ...@@ -326,9 +323,7 @@ public:
typename detail::implicit_conversions< typename detail::implicit_conversions<
typename std::decay<Ts>::type typename std::decay<Ts>::type
>::type...>; >::type...>;
static_assert(actor_accepts_message< static_assert(response_type_unbox<signatures_of_t<Handle>, token>::valid,
typename signatures_of<Handle>::type, token
>::value,
"receiver does not accept given message"); "receiver does not accept given message");
auto mid = current_element_->mid; auto mid = current_element_->mid;
current_element_->mid = P == message_priority::high current_element_->mid = P == message_priority::high
......
...@@ -35,7 +35,6 @@ ...@@ -35,7 +35,6 @@
#include "caf/detail/apply_args.hpp" #include "caf/detail/apply_args.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/detail/pseudo_tuple.hpp" #include "caf/detail/pseudo_tuple.hpp"
#include "caf/detail/left_or_right.hpp"
#include "caf/detail/invoke_result_visitor.hpp" #include "caf/detail/invoke_result_visitor.hpp"
namespace caf { namespace caf {
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "caf/actor.hpp" #include "caf/actor.hpp"
#include "caf/message.hpp" #include "caf/message.hpp"
#include "caf/duration.hpp" #include "caf/duration.hpp"
#include "caf/response_type.hpp"
#include "caf/response_handle.hpp" #include "caf/response_handle.hpp"
#include "caf/message_priority.hpp" #include "caf/message_priority.hpp"
#include "caf/check_typed_input.hpp" #include "caf/check_typed_input.hpp"
...@@ -62,13 +63,11 @@ public: ...@@ -62,13 +63,11 @@ public:
template <message_priority P = message_priority::normal, template <message_priority P = message_priority::normal,
class Handle = actor, class... Ts> class Handle = actor, class... Ts>
response_handle<Subtype, response_handle<Subtype,
typename detail::deduce_output_type< response_type_t<
Handle, typename Handle::signatures,
detail::type_list< typename detail::implicit_conversions<
typename detail::implicit_conversions< typename std::decay<Ts>::type
typename std::decay<Ts>::type >::type...>,
>::type...>
>::type,
is_blocking_requester<Subtype>::value> is_blocking_requester<Subtype>::value>
request(const Handle& dest, const duration& timeout, Ts&&... xs) { request(const Handle& dest, const duration& timeout, Ts&&... xs) {
static_assert(sizeof...(Ts) > 0, "no message to send"); static_assert(sizeof...(Ts) > 0, "no message to send");
...@@ -77,7 +76,7 @@ public: ...@@ -77,7 +76,7 @@ public:
typename detail::implicit_conversions< typename detail::implicit_conversions<
typename std::decay<Ts>::type typename std::decay<Ts>::type
>::type...>; >::type...>;
static_assert(actor_accepts_message<typename signatures_of<Handle>::type, token>::value, static_assert(response_type_unbox<signatures_of_t<Handle>, token>::valid,
"receiver does not accept given message"); "receiver does not accept given message");
auto dptr = static_cast<Subtype*>(this); auto dptr = static_cast<Subtype*>(this);
auto req_id = dptr->new_request_id(P); auto req_id = dptr->new_request_id(P);
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "caf/actor.hpp" #include "caf/actor.hpp"
#include "caf/message.hpp" #include "caf/message.hpp"
#include "caf/duration.hpp" #include "caf/duration.hpp"
#include "caf/response_type.hpp"
#include "caf/response_handle.hpp" #include "caf/response_handle.hpp"
#include "caf/message_priority.hpp" #include "caf/message_priority.hpp"
#include "caf/check_typed_input.hpp" #include "caf/check_typed_input.hpp"
...@@ -58,35 +59,23 @@ public: ...@@ -58,35 +59,23 @@ public:
template <message_priority P = message_priority::normal, template <message_priority P = message_priority::normal,
class Dest = actor, class... Ts> class Dest = actor, class... Ts>
void send(const Dest& dest, Ts&&... xs) { void send(const Dest& dest, Ts&&... xs) {
using detail::type_list;
static_assert(sizeof...(Ts) > 0, "no message to send"); static_assert(sizeof...(Ts) > 0, "no message to send");
using token = using res_t = response_type<
detail::type_list< signatures_of_t<Dest>,
typename detail::implicit_conversions< detail::implicit_conversions_t<
typename std::decay<Ts>::type typename std::decay<Ts>::type
>::type...>; >...>;
static_assert(!statically_typed<Subtype>() || statically_typed<Dest>(), static_assert(!statically_typed<Subtype>() || statically_typed<Dest>(),
"statically typed actors can only send() to other " "statically typed actors can only send() to other "
"statically typed actors; use anon_send() or request() when " "statically typed actors; use anon_send() or request() when "
"communicating with dynamically typed actors"); "communicating with dynamically typed actors");
static_assert(actor_accepts_message< static_assert(res_t::valid, "receiver does not accept given message");
typename signatures_of<Dest>::type, static_assert(std::is_same<typename res_t::type, type_list<>>::value
token || response_type_unbox<
>::value, signatures_of_t<Subtype>,
"receiver does not accept given message"); typename res_t::type
// TODO: this only checks one way, we should check for loops >::valid,
static_assert(is_void_response<
typename response_to<
typename signatures_of<Dest>::type,
token
>::type
>::value
|| actor_accepts_message<
typename signatures_of<Subtype>::type,
typename response_to<
typename signatures_of<Dest>::type,
token
>::type
>::value,
"this actor does not accept the response message"); "this actor does not accept the response message");
dest->eq_impl(message_id::make(P), this->ctrl(), dest->eq_impl(message_id::make(P), this->ctrl(),
this->context(), std::forward<Ts>(xs)...); this->context(), std::forward<Ts>(xs)...);
...@@ -101,10 +90,10 @@ public: ...@@ -101,10 +90,10 @@ public:
typename detail::implicit_conversions< typename detail::implicit_conversions<
typename std::decay<Ts>::type typename std::decay<Ts>::type
>::type...>; >::type...>;
static_assert(actor_accepts_message< static_assert(response_type_unbox<
typename signatures_of<Dest>::type, signatures_of_t<Dest>,
token token
>::value, >::valid,
"receiver does not accept given message"); "receiver does not accept given message");
dest->eq_impl(message_id::make(P), nullptr, dest->eq_impl(message_id::make(P), nullptr,
this->context(), std::forward<Ts>(xs)...); this->context(), std::forward<Ts>(xs)...);
...@@ -122,25 +111,29 @@ public: ...@@ -122,25 +111,29 @@ public:
"statically typed actors are only allowed to send() to other " "statically typed actors are only allowed to send() to other "
"statically typed actors; use anon_send() or request() when " "statically typed actors; use anon_send() or request() when "
"communicating with dynamically typed actors"); "communicating with dynamically typed actors");
static_assert(actor_accepts_message< static_assert(response_type_unbox<
typename signatures_of<Dest>::type, signatures_of_t<Dest>,
token token
>::value, >::valid,
"receiver does not accept given message"); "receiver does not accept given message");
// TODO: this only checks one way, we should check for loops // TODO: this only checks one way, we should check for loops
static_assert(is_void_response< static_assert(is_void_response<
typename response_to< typename response_type<
typename signatures_of<Dest>::type, signatures_of_t<Dest>,
token detail::implicit_conversions_t<
typename std::decay<Ts>::type
>...
>::type >::type
>::value >::value
|| actor_accepts_message< || response_type_unbox<
typename signatures_of<Subtype>::type, signatures_of_t<Subtype>,
typename response_to< typename response_type<
typename signatures_of<Dest>::type, signatures_of_t<Dest>,
token detail::implicit_conversions_t<
typename std::decay<Ts>::type
>...
>::type >::type
>::value, >::valid,
"this actor does not accept the response message"); "this actor does not accept the response message");
dptr()->system().scheduler().delayed_send( dptr()->system().scheduler().delayed_send(
rtime, dptr()->ctrl(), actor_cast<strong_actor_ptr>(dest), rtime, dptr()->ctrl(), actor_cast<strong_actor_ptr>(dest),
...@@ -156,10 +149,10 @@ public: ...@@ -156,10 +149,10 @@ public:
typename detail::implicit_conversions< typename detail::implicit_conversions<
typename std::decay<Ts>::type typename std::decay<Ts>::type
>::type...>; >::type...>;
static_assert(actor_accepts_message< static_assert(response_type_unbox<
typename signatures_of<Dest>::type, signatures_of_t<Dest>,
token token
>::value, >::valid,
"receiver does not accept given message"); "receiver does not accept given message");
dptr()->system().scheduler().delayed_send( dptr()->system().scheduler().delayed_send(
rtime, nullptr, actor_cast<strong_actor_ptr>(dest), rtime, nullptr, actor_cast<strong_actor_ptr>(dest),
......
...@@ -37,16 +37,22 @@ std::string replies_to_type_name(size_t input_size, ...@@ -37,16 +37,22 @@ std::string replies_to_type_name(size_t input_size,
const std::string* output_opt1); const std::string* output_opt1);
/// @endcond /// @endcond
template <class InputTypes, class OutputTypes> template <class...>
struct typed_mpi; struct output_stream {};
template <class... Is, class... Ls> template <class...>
struct typed_mpi<detail::type_list<Is...>, struct output_tuple {};
template <class Input, class Output>
struct typed_mpi {};
/*
<detail::type_list<Is...>,
detail::type_list<Ls...>> { detail::type_list<Ls...>> {
static_assert(sizeof...(Is) > 0, "template parameter pack Is empty"); static_assert(sizeof...(Is) > 0, "template parameter pack Is empty");
static_assert(sizeof...(Ls) > 0, "template parameter pack Ls empty"); static_assert(sizeof...(Ls) > 0, "template parameter pack Ls empty");
using input_types = detail::type_list<Is...>; using input = detail::type_list<Is...>;
using output_types = detail::type_list<Ls...>; using output = detail::type_list<Ls...>;
static_assert(!detail::tl_exists< static_assert(!detail::tl_exists<
input_types, input_types,
is_illegal_message_element is_illegal_message_element
...@@ -57,15 +63,20 @@ struct typed_mpi<detail::type_list<Is...>, ...@@ -57,15 +63,20 @@ struct typed_mpi<detail::type_list<Is...>,
>::value, >::value,
"interface definition contains an illegal message type"); "interface definition contains an illegal message type");
}; };
*/
template <class... Is> template <class... Is>
struct replies_to { struct replies_to {
template <class... Os> template <class... Os>
using with = typed_mpi<detail::type_list<Is...>, detail::type_list<Os...>>; using with = typed_mpi<detail::type_list<Is...>, output_tuple<Os...>>;
/// @private
template <class... Os>
using with_stream = typed_mpi<detail::type_list<Is...>, output_stream<Os...>>;
}; };
template <class... Is> template <class... Is>
using reacts_to = typed_mpi<detail::type_list<Is...>, detail::type_list<void>>; using reacts_to = typed_mpi<detail::type_list<Is...>, output_tuple<void>>;
} // namespace caf } // namespace caf
......
...@@ -17,53 +17,78 @@ ...@@ -17,53 +17,78 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#ifndef CAF_DETAIL_MPI_SEQUENCER_HPP #ifndef CAF_RESPONSE_TYPE_HPP
#define CAF_DETAIL_MPI_SEQUENCER_HPP #define CAF_RESPONSE_TYPE_HPP
#include "caf/replies_to.hpp" #include <tuple>
#include "caf/fwd.hpp"
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
namespace caf { namespace caf {
namespace detail {
template <class X, class Y> /// Defines:
struct mpi_sequencer_one { /// - `valid` if typedefs are set, can be used to check if actors accept input
using type = void; /// - `type` list of output types or `message` for dynamically typed actors
/// - `delegated_type` type above wrapped in a `delegated`
/// - `tuple_type` output types wrapped in a `std::tuple` or `message`
template <class Ts, class... Xs>
struct response_type;
// short-circuit for dynamically typed messaging
template <class... Xs>
struct response_type<none_t, Xs...> {
static constexpr bool valid = true;
using type = message;
using delegated_type = delegated<message>;
using tuple_type = message;
}; };
template <class... Xs, class... Ys, class... Zs> // end of recursion (suppress type definitions for enabling SFINAE)
struct mpi_sequencer_one<typed_mpi<type_list<Xs...>, type_list<Ys...>>, template <class... Xs>
typed_mpi<type_list<Ys...>, type_list<Zs...>>> { struct response_type<detail::type_list<>, Xs...> {
using type = typed_mpi<type_list<Xs...>, type_list<Zs...>>; static constexpr bool valid = false;
}; };
template <class X, class Y> // case #1: no match
struct mpi_sequencer_all; template <class In, class Out, class... Ts, class... Xs>
struct response_type<detail::type_list<typed_mpi<In, Out>, Ts...>, Xs...>
: response_type<detail::type_list<Ts...>, Xs...> {};
template <class X, class... Ys> // case #2: match
struct mpi_sequencer_all<X, type_list<Ys...>> { template <class... Out, class... Ts, class... Xs>
using type = type_list<typename mpi_sequencer_one<X, Ys>::type...>; struct response_type<detail::type_list<typed_mpi<detail::type_list<Xs...>,
output_tuple<Out...>>,
Ts...>,
Xs...> {
static constexpr bool valid = true;
using type = detail::type_list<Out...>;
using tuple_type = std::tuple<Out...>;
using delegated_type = delegated<Out...>;
}; };
template <template <class...> class Target, class Ys, class... Xs> /// Computes the response message for input `Xs...` from the list
struct mpi_sequencer { /// of message passing interfaces `Ts`.
// combine each X with all Ys template <class Ts, class... Xs>
using all = using response_type_t = typename response_type<Ts, Xs...>::type;
typename tl_concat<
typename mpi_sequencer_all<Xs, Ys>::type... /// Unboxes `Xs` and calls `response_type`.
>::type; template <class Ts, class Xs>
// drop all mismatches (void results) struct response_type_unbox;
using filtered = typename tl_filter_not_type<all, void>::type;
// raise error if we don't have a single match template <class Ts, class... Xs>
static_assert(tl_size<filtered>::value > 0, struct response_type_unbox<Ts, detail::type_list<Xs...>>
"Left-hand actor type does not produce a single result which " : response_type<Ts, Xs...> {};
"is valid as input to the right-hand actor type.");
// compute final actor type template <class Ts>
using type = typename tl_apply<filtered, Target>::type; struct response_type_unbox<Ts, message> : response_type<Ts, message> {};
};
/// Computes the response message for input `Xs` from the list
/// of message passing interfaces `Ts`.
template <class Ts, class Xs>
using response_type_unbox_t = typename response_type_unbox<Ts, Xs>::type;
} // namespace detail
} // namespace caf } // namespace caf
#endif // CAF_DETAIL_MPI_SEQUENCER_HPP #endif // CAF_RESPONSE_TYPE_HPP
...@@ -26,9 +26,10 @@ ...@@ -26,9 +26,10 @@
#include "caf/actor_addr.hpp" #include "caf/actor_addr.hpp"
#include "caf/message_id.hpp" #include "caf/message_id.hpp"
#include "caf/typed_actor.hpp" #include "caf/typed_actor.hpp"
#include "caf/response_type.hpp"
#include "caf/system_messages.hpp"
#include "caf/is_message_sink.hpp" #include "caf/is_message_sink.hpp"
#include "caf/message_priority.hpp" #include "caf/message_priority.hpp"
#include "caf/system_messages.hpp"
#include "caf/check_typed_input.hpp" #include "caf/check_typed_input.hpp"
namespace caf { namespace caf {
...@@ -38,31 +39,28 @@ template <message_priority P = message_priority::normal, ...@@ -38,31 +39,28 @@ template <message_priority P = message_priority::normal,
class Source = actor, class Dest = actor, class... Ts> class Source = actor, class Dest = actor, class... Ts>
void send_as(const Source& src, const Dest& dest, Ts&&... xs) { void send_as(const Source& src, const Dest& dest, Ts&&... xs) {
static_assert(sizeof...(Ts) > 0, "no message to send"); static_assert(sizeof...(Ts) > 0, "no message to send");
using token = using token = detail::type_list<detail::strip_and_convert_t<Ts>...>;
detail::type_list<
typename detail::implicit_conversions<
typename std::decay<Ts>::type
>::type...>;
static_assert(!statically_typed<Source>() || statically_typed<Dest>(), static_assert(!statically_typed<Source>() || statically_typed<Dest>(),
"statically typed actors can only send() to other " "statically typed actors can only send() to other "
"statically typed actors; use anon_send() or request() when " "statically typed actors; use anon_send() or request() when "
"communicating with dynamically typed actors"); "communicating with dynamically typed actors");
static_assert(actor_accepts_message<typename signatures_of<Dest>::type, token>::value, static_assert(response_type_unbox<
signatures_of_t<Dest>,
token
>::valid,
"receiver does not accept given message"); "receiver does not accept given message");
// TODO: this only checks one way, we should check for loops // TODO: this only checks one way, we should check for loops
static_assert(is_void_response< static_assert(is_void_response<
typename response_to< response_type_unbox_t<
typename signatures_of<Dest>::type, signatures_of_t<Dest>,
token token>
>::type
>::value >::value
|| actor_accepts_message< || response_type_unbox<
typename signatures_of<Source>::type, signatures_of_t<Source>,
typename response_to< response_type_unbox_t<
typename signatures_of<Dest>::type, signatures_of_t<Dest>,
token token>
>::type >::valid,
>::value,
"this actor does not accept the response message"); "this actor does not accept the response message");
dest->eq_impl(message_id::make(P), actor_cast<strong_actor_ptr>(src), dest->eq_impl(message_id::make(P), actor_cast<strong_actor_ptr>(src),
nullptr, std::forward<Ts>(xs)...); nullptr, std::forward<Ts>(xs)...);
...@@ -73,15 +71,8 @@ template <message_priority P = message_priority::normal, ...@@ -73,15 +71,8 @@ template <message_priority P = message_priority::normal,
class Dest = actor, class... Ts> class Dest = actor, class... Ts>
void anon_send(const Dest& dest, Ts&&... xs) { void anon_send(const Dest& dest, Ts&&... xs) {
static_assert(sizeof...(Ts) > 0, "no message to send"); static_assert(sizeof...(Ts) > 0, "no message to send");
using token = using token = detail::type_list<detail::strip_and_convert_t<Ts>...>;
detail::type_list< static_assert(response_type_unbox<signatures_of_t<Dest>, token>::valid,
typename detail::implicit_conversions<
typename std::decay<Ts>::type
>::type...>;
static_assert(actor_accepts_message<
typename signatures_of<Dest>::type,
token
>::value,
"receiver does not accept given message"); "receiver does not accept given message");
dest->eq_impl(message_id::make(P), nullptr, nullptr, std::forward<Ts>(xs)...); dest->eq_impl(message_id::make(P), nullptr, nullptr, std::forward<Ts>(xs)...);
} }
......
...@@ -26,20 +26,17 @@ ...@@ -26,20 +26,17 @@
#include "caf/actor_cast.hpp" #include "caf/actor_cast.hpp"
#include "caf/replies_to.hpp" #include "caf/replies_to.hpp"
#include "caf/actor_system.hpp" #include "caf/actor_system.hpp"
#include "caf/composed_type.hpp"
#include "caf/abstract_actor.hpp" #include "caf/abstract_actor.hpp"
#include "caf/stateful_actor.hpp" #include "caf/stateful_actor.hpp"
#include "caf/typed_behavior.hpp" #include "caf/typed_behavior.hpp"
#include "caf/typed_response_promise.hpp" #include "caf/typed_response_promise.hpp"
#include "caf/unsafe_actor_handle_init.hpp" #include "caf/unsafe_actor_handle_init.hpp"
#include "caf/decorator/adapter.hpp" #include "caf/detail/mpi_splice.hpp"
#include "caf/decorator/splitter.hpp" #include "caf/decorator/splitter.hpp"
#include "caf/decorator/sequencer.hpp" #include "caf/decorator/sequencer.hpp"
#include "caf/detail/mpi_bind.hpp"
#include "caf/detail/mpi_splice.hpp"
#include "caf/detail/mpi_sequencer.hpp"
namespace caf { namespace caf {
template <class... Sigs> template <class... Sigs>
...@@ -196,19 +193,6 @@ class typed_actor : detail::comparable<typed_actor<Sigs...>>, ...@@ -196,19 +193,6 @@ class typed_actor : detail::comparable<typed_actor<Sigs...>>,
ptr_.swap(other.ptr_); ptr_.swap(other.ptr_);
} }
template <class... Ts>
typename detail::mpi_bind<
caf::typed_actor,
detail::type_list<Sigs...>,
typename std::decay<Ts>::type...
>::type
bind(Ts&&... xs) const {
auto& sys = *(ptr_->home_system);
auto ptr = make_actor<decorator::adapter, strong_actor_ptr>(
sys.next_actor_id(), sys.node(), &sys, ptr_, make_message(xs...));
return {ptr.release(), false};
}
/// Queries whether this object was constructed using /// Queries whether this object was constructed using
/// `unsafe_actor_handle_init` or is in moved-from state. /// `unsafe_actor_handle_init` or is in moved-from state.
bool unsafe() const { bool unsafe() const {
...@@ -290,18 +274,10 @@ bool operator!=(const typed_actor<Xs...>& x, ...@@ -290,18 +274,10 @@ bool operator!=(const typed_actor<Xs...>& x,
/// Returns a new actor that implements the composition `f.g(x) = f(g(x))`. /// Returns a new actor that implements the composition `f.g(x) = f(g(x))`.
/// @relates typed_actor /// @relates typed_actor
template <class... Xs, class... Ys> template <class... Xs, class... Ys>
typename detail::mpi_sequencer< composed_type_t<detail::type_list<Xs...>, detail::type_list<Ys...>>
typed_actor,
detail::type_list<Xs...>,
Ys...
>::type
operator*(typed_actor<Xs...> f, typed_actor<Ys...> g) { operator*(typed_actor<Xs...> f, typed_actor<Ys...> g) {
using result = using result = composed_type_t<detail::type_list<Xs...>,
typename detail::mpi_sequencer< detail::type_list<Ys...>>;
typed_actor,
detail::type_list<Xs...>,
Ys...
>::type;
auto& sys = g->home_system(); auto& sys = g->home_system();
auto mts = sys.message_types(detail::type_list<result>{}); auto mts = sys.message_types(detail::type_list<result>{});
return make_actor<decorator::sequencer, result>( return make_actor<decorator::sequencer, result>(
......
...@@ -21,11 +21,12 @@ ...@@ -21,11 +21,12 @@
#define CAF_TYPED_BEHAVIOR_HPP #define CAF_TYPED_BEHAVIOR_HPP
#include "caf/behavior.hpp" #include "caf/behavior.hpp"
#include "caf/deduce_mpi.hpp"
#include "caf/message_handler.hpp" #include "caf/message_handler.hpp"
#include "caf/system_messages.hpp" #include "caf/system_messages.hpp"
#include "caf/interface_mismatch.hpp"
#include "caf/typed_continue_helper.hpp" #include "caf/typed_continue_helper.hpp"
#include "caf/detail/ctm.hpp"
#include "caf/detail/typed_actor_util.hpp" #include "caf/detail/typed_actor_util.hpp"
namespace caf { namespace caf {
...@@ -150,26 +151,34 @@ class behavior_stack_based_impl; ...@@ -150,26 +151,34 @@ class behavior_stack_based_impl;
template <class... Sigs> template <class... Sigs>
class typed_behavior { class typed_behavior {
public: public:
// -- friends ----------------------------------------------------------------
template <class... OtherSigs> template <class... OtherSigs>
friend class typed_actor; friend class typed_actor;
template <class, class, class> template <class, class, class>
friend class mixin::behavior_stack_based_impl; friend class mixin::behavior_stack_based_impl;
// -- member types -----------------------------------------------------------
/// Stores the template parameter pack in a type list.
using signatures = detail::type_list<Sigs...>;
/// Empty struct tag for constructing from an untyped behavior.
struct unsafe_init { };
// -- constructors, destructors, and assignment operators --------------------
typed_behavior(typed_behavior&&) = default; typed_behavior(typed_behavior&&) = default;
typed_behavior(const typed_behavior&) = default; typed_behavior(const typed_behavior&) = default;
typed_behavior& operator=(typed_behavior&&) = default; typed_behavior& operator=(typed_behavior&&) = default;
typed_behavior& operator=(const typed_behavior&) = default; typed_behavior& operator=(const typed_behavior&) = default;
using signatures = detail::type_list<Sigs...>;
template <class T, class... Ts> template <class T, class... Ts>
typed_behavior(T x, Ts... xs) { typed_behavior(T x, Ts... xs) {
set(detail::make_behavior(std::move(x), std::move(xs)...)); set(detail::make_behavior(std::move(x), std::move(xs)...));
} }
struct unsafe_init { };
typed_behavior(unsafe_init, behavior x) : bhvr_(std::move(x)) { typed_behavior(unsafe_init, behavior x) : bhvr_(std::move(x)) {
// nop // nop
} }
...@@ -178,19 +187,25 @@ public: ...@@ -178,19 +187,25 @@ public:
// nop // nop
} }
// -- modifiers --------------------------------------------------------------
/// Exchanges the contents of this and other.
inline void swap(typed_behavior& other) { inline void swap(typed_behavior& other) {
bhvr_.swap(other.bhvr_); bhvr_.swap(other.bhvr_);
} }
explicit operator bool() const { /// Invokes the timeout callback.
return static_cast<bool>(bhvr_);
}
/// Invokes the timeout callback.
void handle_timeout() { void handle_timeout() {
bhvr_.handle_timeout(); bhvr_.handle_timeout();
} }
// -- observers --------------------------------------------------------------
/// Returns whether this behavior contains any callbacks.
explicit operator bool() const {
return static_cast<bool>(bhvr_);
}
/// Returns the duration after which receives using /// Returns the duration after which receives using
/// this behavior should time out. /// this behavior should time out.
const duration& timeout() const { const duration& timeout() const {
...@@ -214,15 +229,12 @@ private: ...@@ -214,15 +229,12 @@ private:
template <class... Ts> template <class... Ts>
void set(intrusive_ptr<detail::default_behavior_impl<std::tuple<Ts...>>> bp) { void set(intrusive_ptr<detail::default_behavior_impl<std::tuple<Ts...>>> bp) {
using impl = detail::default_behavior_impl<std::tuple<Ts...>>; using found_signatures = detail::type_list<deduce_mpi_t<Ts>...>;
using mpi = using m = interface_mismatch_t<found_signatures, signatures>;
typename detail::tl_map< // trigger static assert on mismatch
typename impl::cases, detail::static_error_printer<sizeof...(Ts), m::value,
detail::deduce_mpi typename m::xs, typename m::ys> guard;
>::type; CAF_IGNORE_UNUSED(guard);
static_assert(detail::tl_is_distinct<mpi>::value,
"multiple handler defintions found");
detail::static_asserter<signatures, mpi, detail::ctm>::verify_match();
// final (type-erasure) step // final (type-erasure) step
intrusive_ptr<detail::behavior_impl> ptr = std::move(bp); intrusive_ptr<detail::behavior_impl> ptr = std::move(bp);
bhvr_.assign(std::move(ptr)); bhvr_.assign(std::move(ptr));
......
...@@ -30,7 +30,6 @@ ...@@ -30,7 +30,6 @@
#include "caf/scoped_actor.hpp" #include "caf/scoped_actor.hpp"
#include "caf/event_based_actor.hpp" #include "caf/event_based_actor.hpp"
#include "caf/decorator/adapter.hpp"
#include "caf/decorator/splitter.hpp" #include "caf/decorator/splitter.hpp"
#include "caf/decorator/sequencer.hpp" #include "caf/decorator/sequencer.hpp"
...@@ -77,12 +76,6 @@ actor_addr actor::address() const noexcept { ...@@ -77,12 +76,6 @@ actor_addr actor::address() const noexcept {
return actor_cast<actor_addr>(ptr_); return actor_cast<actor_addr>(ptr_);
} }
actor actor::bind_impl(message msg) const {
auto& sys = *(ptr_->home_system);
return make_actor<decorator::adapter, actor>(sys.next_actor_id(), sys.node(),
&sys, ptr_, std::move(msg));
}
actor operator*(actor f, actor g) { actor operator*(actor f, actor g) {
auto& sys = f->home_system(); auto& sys = f->home_system();
return make_actor<decorator::sequencer, actor>( return make_actor<decorator::sequencer, actor>(
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/decorator/adapter.hpp"
#include "caf/sec.hpp"
#include "caf/actor_cast.hpp"
#include "caf/actor_system.hpp"
#include "caf/mailbox_element.hpp"
#include "caf/system_messages.hpp"
#include "caf/default_attachable.hpp"
#include "caf/detail/disposer.hpp"
#include "caf/detail/merged_tuple.hpp"
#include "caf/detail/sync_request_bouncer.hpp"
namespace caf {
namespace decorator {
adapter::adapter(strong_actor_ptr decorated, message msg)
: monitorable_actor(actor_config{}.add_flag(is_actor_bind_decorator_flag)),
decorated_(std::move(decorated)),
merger_(std::move(msg)) {
// bound actor has dependency on the decorated actor by default;
// if the decorated actor is already dead upon establishing the
// dependency, the actor is spawned dead
decorated_->get()->attach(
default_attachable::make_monitor(decorated_->get()->address(), address()));
}
void adapter::enqueue(mailbox_element_ptr x, execution_unit* context) {
CAF_ASSERT(x);
auto down_msg_handler = [&](down_msg& dm) {
cleanup(std::move(dm.reason), context);
};
if (handle_system_message(*x, context, false, down_msg_handler))
return;
strong_actor_ptr decorated;
message merger;
error fail_state;
shared_critical_section([&] {
decorated = decorated_;
merger = merger_;
fail_state = fail_state_;
});
if (!decorated) {
bounce(x, fail_state);
return;
}
message tmp{detail::merged_tuple::make(merger, x->move_content_to_message())};
decorated->enqueue(make_mailbox_element(std::move(x->sender), x->mid,
std::move(x->stages), std::move(tmp)),
context);
}
void adapter::on_cleanup() {
decorated_.reset();
merger_.reset();
}
} // namespace decorator
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE adapter
#include "caf/test/unit_test.hpp"
#include "caf/all.hpp"
using namespace caf;
namespace {
behavior testee(event_based_actor* self) {
return {
[](int v) {
return 2 * v;
},
[=] {
self->quit();
}
};
}
struct fixture {
template <class Actor>
static bool exited(const Actor& handle) {
auto ptr = actor_cast<abstract_actor*>(handle);
auto dptr = dynamic_cast<monitorable_actor*>(ptr);
CAF_REQUIRE(dptr != nullptr);
return dptr->getf(abstract_actor::is_terminated_flag);
}
fixture() : system(cfg) {
// nop
}
actor_system_config cfg;
actor_system system;
scoped_actor self{system, true};
};
void handle_err(const error& err) {
throw std::runtime_error("AUT responded with an error: " + to_string(err));
}
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(adapter_tests, fixture)
CAF_TEST(identity) {
auto dbl = system.spawn(testee);
CAF_CHECK_EQUAL(system.registry().running(), 1u);
auto bound = dbl.bind(1);
CAF_CHECK_EQUAL(system.registry().running(), 1u);
CAF_CHECK_EQUAL(&bound->home_system(), &dbl->home_system());
CAF_CHECK_EQUAL(bound->node(), dbl->node());
CAF_CHECK_NOT_EQUAL(bound, dbl);
CAF_CHECK_NOT_EQUAL(bound->id(), dbl->id());
//anon_send_exit(bound, exit_reason::kill);
anon_send_exit(dbl, exit_reason::kill);
// killing dbl triggers a down message to bound, which becomes unreachable
// when it no longer monitors dbl as a result and goes out of scope here
}
// bound actor spawned dead if decorated
// actor is already dead upon spawning
CAF_TEST(lifetime_1) {
auto dbl = system.spawn(testee);
self->monitor(dbl);
anon_send_exit(dbl, exit_reason::kill);
self->wait_for(dbl);
auto bound = dbl.bind(1);
CAF_CHECK(exited(bound));
}
// bound actor exits when decorated actor exits
CAF_TEST(lifetime_2) {
auto dbl = system.spawn(testee);
auto bound = dbl.bind(1);
self->monitor(bound);
anon_send(dbl, message{});
self->wait_for(dbl);
}
CAF_TEST(request_response_promise) {
auto dbl = system.spawn(testee);
auto bound = dbl.bind(1);
anon_send_exit(bound, exit_reason::kill);
CAF_CHECK(exited(bound));
self->request(bound, infinite, message{}).receive(
[](int) {
throw std::runtime_error("received unexpected integer");
},
[](error err) {
CAF_CHECK_EQUAL(err.code(),
static_cast<uint8_t>(sec::request_receiver_down));
}
);
anon_send_exit(dbl, exit_reason::kill);
}
CAF_TEST(partial_currying) {
using namespace std::placeholders;
auto impl = []() -> behavior {
return {
[](ok_atom, int x) {
return x;
},
[](ok_atom, double x) {
return x;
}
};
};
auto aut = system.spawn(impl);
CAF_CHECK_EQUAL(system.registry().running(), 1u);
auto bound = aut.bind(ok_atom::value, _1);
CAF_CHECK_NOT_EQUAL(aut.id(), bound.id());
CAF_CHECK_NOT_EQUAL(aut, bound);
CAF_CHECK_EQUAL(aut.node(), bound.node());
CAF_CHECK_EQUAL(system.registry().running(), 1u);
self->request(bound, infinite, 2.0).receive(
[](double y) {
CAF_CHECK_EQUAL(y, 2.0);
},
handle_err
);
self->request(bound, infinite, 10).receive(
[](int y) {
CAF_CHECK_EQUAL(y, 10);
},
handle_err
);
self->send_exit(aut, exit_reason::kill);
}
CAF_TEST(full_currying) {
auto dbl_actor = system.spawn(testee);
auto bound = dbl_actor.bind(1);
self->request(bound, infinite, message{}).receive(
[](int v) {
CAF_CHECK_EQUAL(v, 2);
},
handle_err
);
anon_send_exit(bound, exit_reason::kill);
anon_send_exit(dbl_actor, exit_reason::kill);
}
CAF_TEST(type_safe_currying) {
using namespace std::placeholders;
using testee = typed_actor<replies_to<ok_atom, int>::with<int>,
replies_to<ok_atom, double>::with<double>>;
auto impl = []() -> testee::behavior_type {
return {
[](ok_atom, int x) {
return x;
},
[](ok_atom, double x) {
return x;
}
};
};
auto aut = system.spawn(impl);
CAF_CHECK_EQUAL(system.registry().running(), 1u);
using curried_signature = typed_actor<replies_to<int>::with<int>,
replies_to<double>::with<double>>;
auto bound = aut.bind(ok_atom::value, _1);
CAF_CHECK_NOT_EQUAL(aut.address(), bound.address());
CAF_CHECK_EQUAL(system.registry().running(), 1u);
static_assert(std::is_same<decltype(bound), curried_signature>::value,
"bind returned wrong actor handle");
self->request(bound, infinite, 2.0).receive(
[](double y) {
CAF_CHECK_EQUAL(y, 2.0);
},
handle_err
);
self->request(bound, infinite, 10).receive(
[](int y) {
CAF_CHECK_EQUAL(y, 10);
},
handle_err
);
self->send_exit(aut, exit_reason::kill);
}
CAF_TEST(reordering) {
auto impl = []() -> behavior {
return {
[](int x, double y) {
return x * y;
}
};
};
auto aut = system.spawn(impl);
CAF_CHECK_EQUAL(system.registry().running(), 1u);
using namespace std::placeholders;
auto bound = aut.bind(_2, _1);
CAF_CHECK_NOT_EQUAL(aut, bound);
CAF_CHECK_EQUAL(system.registry().running(), 1u);
self->request(bound, infinite, 2.0, 10).receive(
[](double y) {
CAF_CHECK_EQUAL(y, 20.0);
},
handle_err
);
self->send_exit(aut, exit_reason::kill);
}
CAF_TEST(type_safe_reordering) {
using testee = typed_actor<replies_to<int, double>::with<double>>;
auto impl = []() -> testee::behavior_type {
return {
[](int x, double y) {
return x * y;
}
};
};
auto aut = system.spawn(impl);
CAF_CHECK_EQUAL(system.registry().running(), 1u);
using namespace std::placeholders;
using swapped_signature = typed_actor<replies_to<double, int>::with<double>>;
auto bound = aut.bind(_2, _1);
CAF_CHECK_NOT_EQUAL(aut.address(), bound.address());
CAF_CHECK_EQUAL(system.registry().running(), 1u);
static_assert(std::is_same<decltype(bound), swapped_signature>::value,
"bind returned wrong actor handle");
self->request(bound, infinite, 2.0, 10).receive(
[](double y) {
CAF_CHECK_EQUAL(y, 20.0);
},
handle_err
);
self->send_exit(aut, exit_reason::kill);
}
CAF_TEST_FIXTURE_SCOPE_END()
This diff is collapsed.
...@@ -32,7 +32,8 @@ using namespace caf; ...@@ -32,7 +32,8 @@ using namespace caf;
namespace { namespace {
using first_stage = typed_actor<replies_to<double>::with<double, double>>; using first_stage = typed_actor<replies_to<double>::with<double, double>>;
using second_stage = typed_actor<replies_to<double, double>::with<double>>; using second_stage = typed_actor<replies_to<double, double>::with<double>,
replies_to<double>::with<double>>;
first_stage::behavior_type typed_first_stage() { first_stage::behavior_type typed_first_stage() {
return [](double x) { return [](double x) {
...@@ -41,8 +42,13 @@ first_stage::behavior_type typed_first_stage() { ...@@ -41,8 +42,13 @@ first_stage::behavior_type typed_first_stage() {
} }
second_stage::behavior_type typed_second_stage() { second_stage::behavior_type typed_second_stage() {
return [](double x, double y) { return {
return x * y; [](double x, double y) {
return x * y;
},
[](double x) {
return 23.0f * x;
}
}; };
} }
...@@ -75,7 +81,7 @@ struct fixture { ...@@ -75,7 +81,7 @@ struct fixture {
using namespace std::placeholders; using namespace std::placeholders;
first = system.spawn(untyped_first_stage); first = system.spawn(untyped_first_stage);
second = system.spawn(untyped_second_stage); second = system.spawn(untyped_second_stage);
first_and_second = splice(first, second.bind(23.0, _1)); first_and_second = splice(first, second);
} }
}; };
...@@ -114,11 +120,12 @@ CAF_TEST(untyped_splicing) { ...@@ -114,11 +120,12 @@ CAF_TEST(untyped_splicing) {
); );
} }
/*
CAF_TEST(typed_splicing) { CAF_TEST(typed_splicing) {
using namespace std::placeholders; using namespace std::placeholders;
auto stage0 = system.spawn(typed_first_stage); auto stage0 = system.spawn(typed_first_stage);
auto stage1 = system.spawn(typed_second_stage); auto stage2 = system.spawn(typed_second_stage);
auto stages = splice(stage0, stage1.bind(23.0, _1)); auto stages = splice(stage0, stage2);
using expected_type = typed_actor<replies_to<double> using expected_type = typed_actor<replies_to<double>
::with<double, double, double>>; ::with<double, double, double>>;
static_assert(std::is_same<decltype(stages), expected_type>::value, static_assert(std::is_same<decltype(stages), expected_type>::value,
...@@ -131,8 +138,9 @@ CAF_TEST(typed_splicing) { ...@@ -131,8 +138,9 @@ CAF_TEST(typed_splicing) {
}, },
ERROR_HANDLER ERROR_HANDLER
); );
// stage0 and stage1 go out of scope, leaving only the references // stage0 and stage2 go out of scope, leaving only the references
// in stages, which will also go out of scope // in stages, which will also go out of scope
} }
*/
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
...@@ -211,7 +211,7 @@ using string_actor = typed_actor<replies_to<string>::with<string>>; ...@@ -211,7 +211,7 @@ using string_actor = typed_actor<replies_to<string>::with<string>>;
string_actor::behavior_type string_reverter() { string_actor::behavior_type string_reverter() {
return { return {
[](string& str) { [](string& str) -> string {
std::reverse(str.begin(), str.end()); std::reverse(str.begin(), str.end());
return std::move(str); return std::move(str);
} }
......
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