Commit 8351e0f3 authored by Dominik Charousset's avatar Dominik Charousset

Add either/or interface definitions

The new `replies_to<...>::with_either<...>::or_else<...>` notation allows
developers to have success/failure messages for typed actors. Message handlers
need to use the corresponding `either<...>::or_else<...>` result type.
parent 8467b355
Subproject commit ed985aed8bbd0f3eb6c149beff01a5a32276433f Subproject commit 43956c13001caa832d7adeca06d2ed4409875817
Subproject commit 8d709e6da3796161680c726802ea6152e88eaf08 Subproject commit 429df03e9483483b9fe78a6676b67c5407b5ac11
...@@ -62,6 +62,7 @@ set (LIBCAF_CORE_SRCS ...@@ -62,6 +62,7 @@ set (LIBCAF_CORE_SRCS
src/node_id.cpp src/node_id.cpp
src/ref_counted.cpp src/ref_counted.cpp
src/response_promise.cpp src/response_promise.cpp
src/replies_to.cpp
src/resumable.cpp src/resumable.cpp
src/ripemd_160.cpp src/ripemd_160.cpp
src/scoped_actor.cpp src/scoped_actor.cpp
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "caf/match.hpp" #include "caf/match.hpp"
#include "caf/spawn.hpp" #include "caf/spawn.hpp"
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/either.hpp"
#include "caf/extend.hpp" #include "caf/extend.hpp"
#include "caf/channel.hpp" #include "caf/channel.hpp"
#include "caf/message.hpp" #include "caf/message.hpp"
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "caf/intrusive_ptr.hpp" #include "caf/intrusive_ptr.hpp"
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/either.hpp"
#include "caf/message.hpp" #include "caf/message.hpp"
#include "caf/duration.hpp" #include "caf/duration.hpp"
#include "caf/ref_counted.hpp" #include "caf/ref_counted.hpp"
...@@ -95,15 +96,19 @@ struct optional_message_visitor : static_visitor<bhvr_invoke_result> { ...@@ -95,15 +96,19 @@ struct optional_message_visitor : static_visitor<bhvr_invoke_result> {
} }
template <class T, class... Ts> template <class T, class... Ts>
typename std::enable_if<optional_message_visitor_enable_tpl<T>::value, typename std::enable_if<
bhvr_invoke_result>::type optional_message_visitor_enable_tpl<T>::value,
bhvr_invoke_result
>::type
operator()(T& v, Ts&... vs) const { operator()(T& v, Ts&... vs) const {
return make_message(std::move(v), std::move(vs)...); return make_message(std::move(v), std::move(vs)...);
} }
template <class T> template <class T>
typename std::enable_if<is_message_id_wrapper<T>::value, typename std::enable_if<
bhvr_invoke_result>::type is_message_id_wrapper<T>::value,
bhvr_invoke_result
>::type
operator()(T& value) const { operator()(T& value) const {
return make_message(atom("MESSAGE_ID"), return make_message(atom("MESSAGE_ID"),
value.get_message_id().integer_value()); value.get_message_id().integer_value());
...@@ -113,6 +118,11 @@ struct optional_message_visitor : static_visitor<bhvr_invoke_result> { ...@@ -113,6 +118,11 @@ struct optional_message_visitor : static_visitor<bhvr_invoke_result> {
return std::move(value); return std::move(value);
} }
template <class L, class R>
bhvr_invoke_result operator()(either_or_t<L, R>& value) const {
return std::move(value.value);
}
template <class... Ts> template <class... Ts>
bhvr_invoke_result operator()(std::tuple<Ts...>& value) const { bhvr_invoke_result operator()(std::tuple<Ts...>& value) const {
return detail::apply_args(*this, detail::get_indices(value), value); return detail::apply_args(*this, detail::get_indices(value), value);
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* 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 "caf/replies_to.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 T>
struct ctm_cmp<T, T> : std::true_type { };
template <class In, class Out>
struct ctm_cmp<typed_mpi<In, Out, empty_type_list>,
typed_mpi<In, type_list<typed_continue_helper<Out>>, empty_type_list>>
: std::true_type { };
template <class In, class L, class R>
struct ctm_cmp<typed_mpi<In, L, R>,
typed_mpi<In, type_list<skip_message_t>, empty_type_list>>
: std::true_type { };
template <class A, class B>
struct ctm : std::false_type { };
template <>
struct ctm<empty_type_list, empty_type_list> : std::true_type { };
template <class A, class... As, class... Bs>
struct ctm<type_list<A, As...>, type_list<Bs...>>
: std::conditional<
sizeof...(As) + 1 != sizeof...(Bs),
std::false_type,
ctm<type_list<As...>,
typename tl_filter_not<
type_list<Bs...>,
tbind<ctm_cmp, A>::template type
>::type
>
>::type { };
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_CTM_HPP
...@@ -256,6 +256,23 @@ struct is_mutable_ref { ...@@ -256,6 +256,23 @@ struct is_mutable_ref {
&& !std::is_const<T>::value; && !std::is_const<T>::value;
}; };
/**
* Checks whether `T::static_type_name()` exists.
*/
template <class T>
class has_static_type_name {
private:
template <class U,
class = typename std::enable_if<
!std::is_member_pointer<decltype(&U::is_baz)>::value
>::type>
static std::true_type sfinae_fun(int);
template <class>
static std::false_type sfinae_fun(...);
public:
static constexpr bool value = decltype(sfinae_fun<T>(0))::value;
};
/** /**
* Returns either `T` or `T::type` if `T` is an option. * Returns either `T` or `T::type` if `T` is an option.
*/ */
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <tuple> #include <tuple>
#include "caf/replies_to.hpp" #include "caf/replies_to.hpp"
#include "caf/system_messages.hpp"
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
...@@ -36,26 +37,56 @@ class typed_continue_helper; ...@@ -36,26 +37,56 @@ class typed_continue_helper;
namespace caf { namespace caf {
namespace detail { namespace detail {
template <class R, typename T> template <class T>
struct deduce_signature_helper; struct unwrap_std_tuple {
using type = type_list<T>;
};
template <class R, class... Ts> template <class... Ts>
struct deduce_signature_helper<R, type_list<Ts...>> { struct unwrap_std_tuple<std::tuple<Ts...>> {
using type = typename replies_to<Ts...>::template with<R>; using type = type_list<Ts...>;
};
template <class T>
struct deduce_lhs_result {
using type = typename unwrap_std_tuple<T>::type;
}; };
template <class... Rs, class... Ts> template <class L, class R>
struct deduce_signature_helper<std::tuple<Rs...>, type_list<Ts...>> { struct deduce_lhs_result<either_or_t<L, R>> {
using type = typename replies_to<Ts...>::template with<Rs...>; using type = L;
};
template <class T>
struct deduce_rhs_result {
using type = type_list<>;
}; };
template <class L, class R>
struct deduce_rhs_result<either_or_t<L, R>> {
using type = R;
};
template <class T>
struct is_hidden_msg_handler : std::false_type { };
template <>
struct is_hidden_msg_handler<typed_mpi<type_list<exit_msg>,
type_list<void>,
empty_type_list>> : std::true_type { };
template <>
struct is_hidden_msg_handler<typed_mpi<type_list<down_msg>,
type_list<void>,
empty_type_list>> : std::true_type { };
template <class T> template <class T>
struct deduce_signature { struct deduce_mpi {
using result_ = typename implicit_conversions<typename T::result_type>::type; using result = typename implicit_conversions<typename T::result_type>::type;
using arg_t = typename tl_map<typename T::arg_types, std::decay>::type; using arg_t = typename tl_map<typename T::arg_types, std::decay>::type;
using type = typename deduce_signature_helper<result_, arg_t>::type; using type = typed_mpi<arg_t,
typename deduce_lhs_result<result>::type,
typename deduce_rhs_result<result>::type>;
}; };
template <class Arguments> template <class Arguments>
...@@ -67,20 +98,48 @@ struct input_is { ...@@ -67,20 +98,48 @@ struct input_is {
}; };
}; };
template <class OutputList, typename F> template <class OutputPair, class... Fs>
inline void assert_types() { struct type_checker;
template <class OutputList, class F1>
struct type_checker<OutputList, F1> {
static void check() {
using arg_types = using arg_types =
typename tl_map< typename tl_map<
typename get_callable_trait<F>::arg_types, typename get_callable_trait<F1>::arg_types,
std::decay std::decay
>::type; >::type;
static constexpr size_t fun_args = tl_size<arg_types>::value; static constexpr size_t args = tl_size<arg_types>::value;
static_assert(fun_args <= tl_size<OutputList>::value, static_assert(args <= tl_size<OutputList>::value,
"functor takes too much arguments"); "functor takes too much arguments");
using recv_types = typename tl_right<OutputList, fun_args>::type; using rtypes = typename tl_right<OutputList, args>::type;
static_assert(std::is_same<arg_types, recv_types>::value, static_assert(std::is_same<arg_types, rtypes>::value,
"wrong functor signature"); "wrong functor signature");
} }
};
template <class Opt1, class Opt2, class F1>
struct type_checker<type_pair<Opt1, Opt2>, F1> {
static void check() {
type_checker<Opt1, F1>::check();
}
};
template <class OutputPair, class F1>
struct type_checker<OutputPair, F1, none_t> : type_checker<OutputPair, F1> { };
template <class Opt1, class Opt2, class F1, class F2>
struct type_checker<type_pair<Opt1, Opt2>, F1, F2> {
static void check() {
type_checker<Opt1, F1>::check();
type_checker<Opt2, F2>::check();
}
};
template <class A, class B, template <class, class> class Predicate>
struct static_asserter {
static_assert(Predicate<A, B>::value, "exact match needed");
};
template <class T> template <class T>
struct lifted_result_type { struct lifted_result_type {
...@@ -93,12 +152,12 @@ struct lifted_result_type<std::tuple<Ts...>> { ...@@ -93,12 +152,12 @@ struct lifted_result_type<std::tuple<Ts...>> {
}; };
template <class T> template <class T>
struct deduce_output_type_step2 { struct deduce_lifted_output_type {
using type = T; using type = T;
}; };
template <class R> template <class R>
struct deduce_output_type_step2<type_list<typed_continue_helper<R>>> { struct deduce_lifted_output_type<type_list<typed_continue_helper<R>>> {
using type = typename lifted_result_type<R>::type; using type = typename lifted_result_type<R>::type;
}; };
...@@ -111,9 +170,26 @@ struct deduce_output_type { ...@@ -111,9 +170,26 @@ struct deduce_output_type {
static_assert(input_pos >= 0, "typed actor does not support given input"); static_assert(input_pos >= 0, "typed actor does not support given input");
using signature = typename tl_at<Signatures, input_pos>::type; using signature = typename tl_at<Signatures, input_pos>::type;
using type = using type =
typename deduce_output_type_step2< detail::type_pair<typename signature::output_opt1_types,
typename signature::output_types typename signature::output_opt2_types>;
>::type; };
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;
}; };
} // namespace detail } // namespace detail
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* 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_EITHER_HPP
#define CAF_EITHER_HPP
#include <tuple>
#include <string>
#include <type_traits>
#include "caf/message.hpp"
#include "caf/replies_to.hpp"
#include "caf/type_name_access.hpp"
#include "caf/illegal_message_element.hpp"
#include "caf/detail/type_list.hpp"
namespace caf {
/** @cond PRIVATE */
std::string either_or_else_type_name(size_t lefts_size,
const std::string* lefts,
size_t rights_size,
const std::string* rights);
template <class L, class R>
struct either_or_t;
template <class... Ls, class... Rs>
struct either_or_t<detail::type_list<Ls...>,
detail::type_list<Rs...>> : illegal_message_element {
static_assert(!std::is_same<detail::type_list<Ls...>,
detail::type_list<Rs...>>::value,
"template parameter packs must differ");
either_or_t(Ls... ls) : value(make_message(std::move(ls)...)) {
// nop
}
either_or_t(Rs... rs) : value(make_message(std::move(rs)...)) {
// nop
}
using opt1_type = std::tuple<Ls...>;
using opt2_type = std::tuple<Rs...>;
message value;
static std::string static_type_name() {
std::string lefts[] = {type_name_access<Ls>::get()...};
std::string rights[] = {type_name_access<Rs>::get()...};
return either_or_else_type_name(sizeof...(Ls), lefts,
sizeof...(Rs), rights);
}
};
/** @endcond */
template <class... Ts>
struct either {
template <class... Us>
using or_else = either_or_t<detail::type_list<Ts...>,
detail::type_list<Us...>>;
};
} // namespace caf
#endif // CAF_EITHER_HPP
...@@ -48,6 +48,7 @@ class message_handler; ...@@ -48,6 +48,7 @@ class message_handler;
class uniform_type_info; class uniform_type_info;
class event_based_actor; class event_based_actor;
class forwarding_actor_proxy; class forwarding_actor_proxy;
class illegal_message_element;
// structs // structs
struct anything; struct anything;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* 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_ILLEGAL_MESSAGE_ELEMENT_HPP
#define CAF_ILLEGAL_MESSAGE_ELEMENT_HPP
#include <type_traits>
namespace caf {
/**
* Marker class identifying classes in CAF that are not allowed
* to be used as message element.
*/
struct illegal_message_element {
// no members (marker class)
};
template <class T>
struct is_illegal_message_element
: std::is_base_of<illegal_message_element, T> { };
} // namespace caf
#endif // CAF_ILLEGAL_MESSAGE_ELEMENT_HPP
...@@ -36,7 +36,6 @@ template <class Base, class Subtype, class ResponseHandleTag> ...@@ -36,7 +36,6 @@ template <class Base, class Subtype, class ResponseHandleTag>
class sync_sender_impl : public Base { class sync_sender_impl : public Base {
public: public:
using response_handle_type = response_handle<Subtype, message, using response_handle_type = response_handle<Subtype, message,
ResponseHandleTag>; ResponseHandleTag>;
...@@ -130,9 +129,11 @@ class sync_sender_impl : public Base { ...@@ -130,9 +129,11 @@ class sync_sender_impl : public Base {
**************************************************************************/ **************************************************************************/
template <class... Rs, class... Ts> template <class... Rs, class... Ts>
response_handle< response_handle<Subtype,
Subtype, typename detail::deduce_output_type< typename detail::deduce_output_type<
detail::type_list<Rs...>, detail::type_list<Ts...>>::type, detail::type_list<Rs...>,
detail::type_list<Ts...>
>::type,
ResponseHandleTag> ResponseHandleTag>
sync_send_tuple(message_priority prio, const typed_actor<Rs...>& dest, sync_send_tuple(message_priority prio, const typed_actor<Rs...>& dest,
std::tuple<Ts...> what) { std::tuple<Ts...> what) {
...@@ -157,7 +158,8 @@ class sync_sender_impl : public Base { ...@@ -157,7 +158,8 @@ class sync_sender_impl : public Base {
typename detail::deduce_output_type< typename detail::deduce_output_type<
detail::type_list<Rs...>, detail::type_list<Rs...>,
typename detail::implicit_conversions< typename detail::implicit_conversions<
typename std::decay<Ts>::type>::type...>::type, typename std::decay<Ts>::type>::type...
>::type,
ResponseHandleTag> ResponseHandleTag>
sync_send(message_priority prio, const typed_actor<Rs...>& dest, sync_send(message_priority prio, const typed_actor<Rs...>& dest,
Ts&&... what) { Ts&&... what) {
......
...@@ -20,47 +20,86 @@ ...@@ -20,47 +20,86 @@
#ifndef CAF_REPLIES_TO_HPP #ifndef CAF_REPLIES_TO_HPP
#define CAF_REPLIES_TO_HPP #define CAF_REPLIES_TO_HPP
#include <string>
#include "caf/either.hpp"
#include "caf/uniform_typeid.hpp"
#include "caf/type_name_access.hpp"
#include "caf/uniform_type_info.hpp" #include "caf/uniform_type_info.hpp"
#include "caf/illegal_message_element.hpp"
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
#include "caf/detail/type_pair.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf { namespace caf {
template <class... Is> /** @cond PRIVATE */
struct replies_to { std::string replies_to_type_name(size_t input_size,
template <class... Os> const std::string* input,
struct with { size_t output_opt1_size,
const std::string* output_opt1,
size_t output_opt2_size,
const std::string* output_opt2);
/** @endcond */
template <class InputTypes, class LeftOutputTypes, class RightOutputTypes>
struct typed_mpi;
template <class... Is, class... Ls, class... Rs>
struct typed_mpi<detail::type_list<Is...>,
detail::type_list<Ls...>,
detail::type_list<Rs...>> {
static_assert(sizeof...(Is) > 0, "template parameter pack Is empty"); static_assert(sizeof...(Is) > 0, "template parameter pack Is empty");
static_assert(sizeof...(Os) > 0, "template parameter pack Os empty"); static_assert(sizeof...(Ls) > 0, "template parameter pack Ls empty");
using input_types = detail::type_list<Is...>; using input_types = detail::type_list<Is...>;
using output_types = detail::type_list<Os...>; using output_opt1_types = detail::type_list<Ls...>;
static std::string as_string() { using output_opt2_types = detail::type_list<Rs...>;
const uniform_type_info* inputs[] = {uniform_typeid<Is>(true)...}; static_assert(!std::is_same<output_opt1_types, output_opt2_types>::value,
const uniform_type_info* outputs[] = {uniform_typeid<Os>(true)...}; "result types must differ when using with_either<>::or_else<>");
std::string result; static_assert(!detail::tl_exists<
// 'void' is not an announced type, hence we check whether uniform_typeid input_types,
// did return a valid pointer to identify 'void' (this has the is_illegal_message_element
// possibility of false positives, but those will be catched anyways) >::value
auto plot = [&](const uniform_type_info** arr) { && !detail::tl_exists<
auto uti = arr[0]; output_opt1_types,
result += uti ? uti->name() : "void"; is_illegal_message_element
for (size_t i = 1; i < sizeof...(Is); ++i) { >::value
uti = arr[i]; && !detail::tl_exists<
result += ","; output_opt2_types,
result += uti ? uti->name() : "void"; is_illegal_message_element
} >::value,
}; "interface definition contains an illegal message type, "
result = "caf::replies_to<"; "did you use with<either...> instead of with_either<...>?");
plot(inputs); static std::string static_type_name() {
result += ">::with<"; std::string input[] = {type_name_access<Is>::get()...};
plot(outputs); std::string output_opt1[] = {type_name_access<Ls>::get()...};
result += ">"; std::string output_opt2[] = {type_name_access<Rs>::get()...};
return result; return replies_to_type_name(sizeof...(Is), input,
sizeof...(Ls), output_opt1,
sizeof...(Rs), output_opt2);
} }
};
template <class... Is>
struct replies_to {
template <class... Os>
using with = typed_mpi<detail::type_list<Is...>,
detail::type_list<Os...>,
detail::empty_type_list>;
template <class... Ls>
struct with_either {
template <class... Rs>
using or_else = typed_mpi<detail::type_list<Is...>,
detail::type_list<Ls...>,
detail::type_list<Rs...>>;
}; };
}; };
template <class... Is> template <class... Is>
using reacts_to = typename replies_to<Is...>::template with<void>; using reacts_to = typed_mpi<detail::type_list<Is...>,
detail::type_list<void>,
detail::empty_type_list>;
} // namespace caf } // namespace caf
......
...@@ -51,7 +51,7 @@ struct blocking_response_handle_tag {}; ...@@ -51,7 +51,7 @@ struct blocking_response_handle_tag {};
* This helper class identifies an expected response message * This helper class identifies an expected response message
* and enables `sync_send(...).then(...)`. * and enables `sync_send(...).then(...)`.
*/ */
template <class Self, class Result, class Tag> template <class Self, class ResultOptPairOrMessage, class Tag>
class response_handle; class response_handle;
/****************************************************************************** /******************************************************************************
...@@ -92,9 +92,8 @@ class response_handle<Self, message, nonblocking_response_handle_tag> { ...@@ -92,9 +92,8 @@ class response_handle<Self, message, nonblocking_response_handle_tag> {
/****************************************************************************** /******************************************************************************
* nonblocking + typed * * nonblocking + typed *
******************************************************************************/ ******************************************************************************/
template <class Self, class... Ts> template <class Self, class TypedOutputPair>
class response_handle<Self, detail::type_list<Ts...>, class response_handle<Self, TypedOutputPair, nonblocking_response_handle_tag> {
nonblocking_response_handle_tag> {
public: public:
response_handle() = delete; response_handle() = delete;
response_handle(const response_handle&) = default; response_handle(const response_handle&) = default;
...@@ -104,20 +103,23 @@ class response_handle<Self, detail::type_list<Ts...>, ...@@ -104,20 +103,23 @@ class response_handle<Self, detail::type_list<Ts...>,
// nop // nop
} }
template <class F, template <class... Fs>
class Enable = typename std::enable_if<
detail::is_callable<F>::value
&& !is_match_expr<F>::value
>::type>
typed_continue_helper< typed_continue_helper<
typename detail::lifted_result_type< typename detail::lifted_result_type<
typename detail::get_callable_trait<F>::result_type typename detail::common_result_type<
typename detail::get_callable_trait<Fs>::result_type...
>::type
>::type> >::type>
then(F fun) { then(Fs... fs) {
detail::assert_types<detail::type_list<Ts...>, F>(); static_assert(sizeof...(Fs) > 0, "at least one functor is requried");
static_assert(detail::conjunction<detail::is_callable<Fs>::value...>::value,
"all arguments must be callable");
static_assert(detail::conjunction<!is_match_expr<Fs>::value...>::value,
"match expressions are not allowed in this context");
detail::type_checker<TypedOutputPair, Fs...>::check();
auto selfptr = m_self; auto selfptr = m_self;
behavior tmp{ behavior tmp{
fun, fs...,
on<sync_timeout_msg>() >> [selfptr]() -> skip_message_t { on<sync_timeout_msg>() >> [selfptr]() -> skip_message_t {
selfptr->handle_sync_timeout(); selfptr->handle_sync_timeout();
return {}; return {};
...@@ -173,12 +175,9 @@ class response_handle<Self, message, blocking_response_handle_tag> { ...@@ -173,12 +175,9 @@ class response_handle<Self, message, blocking_response_handle_tag> {
/****************************************************************************** /******************************************************************************
* blocking + typed * * blocking + typed *
******************************************************************************/ ******************************************************************************/
template <class Self, class... Ts> template <class Self, class OutputPair>
class response_handle<Self, detail::type_list<Ts...>, class response_handle<Self, OutputPair, blocking_response_handle_tag> {
blocking_response_handle_tag> {
public: public:
using result_types = detail::type_list<Ts...>;
response_handle() = delete; response_handle() = delete;
response_handle(const response_handle&) = default; response_handle(const response_handle&) = default;
response_handle& operator=(const response_handle&) = default; response_handle& operator=(const response_handle&) = default;
...@@ -187,26 +186,27 @@ class response_handle<Self, detail::type_list<Ts...>, ...@@ -187,26 +186,27 @@ class response_handle<Self, detail::type_list<Ts...>,
// nop // nop
} }
template <class F> static constexpr bool is_either_or_handle =
void await(F fun) { !std::is_same<
using arg_types = none_t,
typename detail::tl_map< typename OutputPair::second
typename detail::get_callable_trait<F>::arg_types, >::value;
std::decay
>::type; template <class... Fs>
static constexpr size_t fun_args = detail::tl_size<arg_types>::value; void await(Fs... fs) {
static_assert(fun_args <= detail::tl_size<result_types>::value, static_assert(sizeof...(Fs) > 0,
"functor takes too much arguments"); "at least one argument is required");
using recv_types = static_assert((is_either_or_handle && sizeof...(Fs) == 2)
typename detail::tl_right< || sizeof...(Fs) == 1,
result_types, "wrong number of functors");
fun_args static_assert(detail::conjunction<detail::is_callable<Fs>::value...>::value,
>::type; "all arguments must be callable");
static_assert(std::is_same<arg_types, recv_types>::value, static_assert(detail::conjunction<!is_match_expr<Fs>::value...>::value,
"wrong functor signature"); "match expressions are not allowed in this context");
detail::type_checker<OutputPair, Fs...>::check();
auto selfptr = m_self; auto selfptr = m_self;
behavior tmp{ behavior tmp{
fun, fs...,
on<sync_timeout_msg>() >> [selfptr]() -> skip_message_t { on<sync_timeout_msg>() >> [selfptr]() -> skip_message_t {
selfptr->handle_sync_timeout(); selfptr->handle_sync_timeout();
return {}; return {};
......
...@@ -44,11 +44,6 @@ std::string to_string(const actor_addr& what); ...@@ -44,11 +44,6 @@ std::string to_string(const actor_addr& what);
std::string to_string(const actor& what); std::string to_string(const actor& what);
/**
* @relates node_id
*/
//std::string to_string(const node_id::host_id_type& node_id);
/** /**
* @relates node_id * @relates node_id
*/ */
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* 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_TYPE_NAME_ACCESS_HPP
#define CAF_TYPE_NAME_ACCESS_HPP
#include <string>
#include "caf/uniform_typeid.hpp"
#include "caf/uniform_type_info.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
template <class T, bool HasTypeName = detail::has_static_type_name<T>::value>
struct type_name_access {
static std::string get() {
auto uti = uniform_typeid<T>(true);
return uti ? uti->name() : "void";
}
};
template <class T>
struct type_name_access<T, true> {
static std::string get() {
return T::static_type_name();
}
};
} // namespace caf
#endif // CAF_TYPE_NAME_ACCESS_HPP
...@@ -131,7 +131,7 @@ class typed_actor ...@@ -131,7 +131,7 @@ class typed_actor
} }
static std::set<std::string> message_types() { static std::set<std::string> message_types() {
return {Rs::as_string()...}; return {Rs::static_type_name()...};
} }
explicit operator bool() const { return static_cast<bool>(m_ptr); } explicit operator bool() const { return static_cast<bool>(m_ptr); }
......
...@@ -20,11 +20,13 @@ ...@@ -20,11 +20,13 @@
#ifndef TYPED_BEHAVIOR_HPP #ifndef TYPED_BEHAVIOR_HPP
#define TYPED_BEHAVIOR_HPP #define TYPED_BEHAVIOR_HPP
#include "caf/either.hpp"
#include "caf/behavior.hpp" #include "caf/behavior.hpp"
#include "caf/match_expr.hpp" #include "caf/match_expr.hpp"
#include "caf/system_messages.hpp" #include "caf/system_messages.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 {
...@@ -46,17 +48,27 @@ struct input_only<detail::type_list<Ts...>> { ...@@ -46,17 +48,27 @@ struct input_only<detail::type_list<Ts...>> {
using skip_list = detail::type_list<skip_message_t>; using skip_list = detail::type_list<skip_message_t>;
template <class Opt1, class Opt2>
struct collapse_replies_to_statement {
using type = typename either<Opt1>::template or_else<Opt2>;
};
template <class List>
struct collapse_replies_to_statement<List, none_t> {
using type = List;
};
/*
template <class List> template <class List>
struct unbox_typed_continue_helper { struct unbox_typed_continue_helper {
// do nothing if List is actually a list, i.e., not a typed_continue_helper
using type = List; using type = List;
}; };
template <class List> template <class List>
struct unbox_typed_continue_helper< struct unbox_typed_continue_helper<typed_continue_helper<List>> {
detail::type_list<typed_continue_helper<List>>> {
using type = List; using type = List;
}; };
*/
template <class Input, class RepliesToWith> template <class Input, class RepliesToWith>
struct same_input : std::is_same<Input, typename RepliesToWith::input_types> {}; struct same_input : std::is_same<Input, typename RepliesToWith::input_types> {};
...@@ -75,8 +87,9 @@ struct valid_input_predicate { ...@@ -75,8 +87,9 @@ struct valid_input_predicate {
struct inner { struct inner {
using input_types = typename Expr::input_types; using input_types = typename Expr::input_types;
using output_types = using output_types =
typename unbox_typed_continue_helper< typename collapse_replies_to_statement<
typename Expr::output_types typename Expr::output_opt1_types,
typename Expr::output_opt2_types
>::type; >::type;
// get matching elements for input type // get matching elements for input type
using filtered_slist = using filtered_slist =
...@@ -215,10 +228,12 @@ class typed_behavior { ...@@ -215,10 +228,12 @@ class typed_behavior {
template <class... Cs> template <class... Cs>
void set(match_expr<Cs...>&& expr) { void set(match_expr<Cs...>&& expr) {
using input = using mpi =
detail::type_list<typename detail::deduce_signature<Cs>::type...>; typename detail::tl_filter_not<
// check types detail::type_list<typename detail::deduce_mpi<Cs>::type...>,
detail::static_check_typed_behavior_input<signatures, input>(); detail::is_hidden_msg_handler
>::type;
detail::static_asserter<signatures, mpi, detail::ctm> asserter;
// final (type-erasure) step // final (type-erasure) step
m_bhvr = std::move(expr); m_bhvr = std::move(expr);
} }
......
...@@ -46,7 +46,7 @@ class typed_continue_helper { ...@@ -46,7 +46,7 @@ class typed_continue_helper {
template <class F> template <class F>
typed_continue_helper<typename detail::get_callable_trait<F>::result_type> typed_continue_helper<typename detail::get_callable_trait<F>::result_type>
continue_with(F fun) { continue_with(F fun) {
detail::assert_types<OutputList, F>(); detail::type_checker<OutputList, F>::check();
m_ch.continue_with(std::move(fun)); m_ch.continue_with(std::move(fun));
return {m_ch}; return {m_ch};
} }
......
...@@ -49,7 +49,7 @@ class typed_event_based_actor : public ...@@ -49,7 +49,7 @@ class typed_event_based_actor : public
using behavior_type = typed_behavior<Rs...>; using behavior_type = typed_behavior<Rs...>;
std::set<std::string> message_types() const override { std::set<std::string> message_types() const override {
return {Rs::as_string()...}; return {Rs::static_type_name()...};
} }
protected: protected:
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* 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/either.hpp"
#include "caf/to_string.hpp"
#include "caf/string_algorithms.hpp"
namespace caf {
std::string either_or_else_type_name(size_t lefts_size,
const std::string* lefts,
size_t rights_size,
const std::string* rights) {
std::string glue = ",";
std::string result;
result = "caf::either<";
result += join(lefts, lefts + lefts_size, glue);
result += ">::or_else<";
result += join(rights, rights + rights_size, glue);
result += ">";
return result;
}
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* 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/replies_to.hpp"
#include "caf/to_string.hpp"
#include "caf/string_algorithms.hpp"
namespace caf {
std::string replies_to_type_name(size_t input_size,
const std::string* input,
size_t output_opt1_size,
const std::string* output_opt1,
size_t output_opt2_size,
const std::string* output_opt2) {
std::string glue = ",";
std::string result;
// 'void' is not an announced type, hence we check whether uniform_typeid
// did return a valid pointer to identify 'void' (this has the
// possibility of false positives, but those will be catched anyways)
result = "caf::replies_to<";
result += join(input, input + input_size, glue);
if (output_opt2_size == 0) {
result += ">::with<";
result += join(output_opt1, output_opt1 + output_opt1_size, glue);
} else {
result += ">::with_either<";
result += join(output_opt1, output_opt1 + output_opt1_size, glue);
result += ">::or_else<";
result += join(output_opt2, output_opt2 + output_opt2_size, glue);
}
result += ">";
return result;
}
} // namespace caf
Subproject commit 5a11746201ff5dca5306278f1b07384d0cb4345a Subproject commit d70a2108082adff5b8c96ef0be7018b5afffadfe
Subproject commit abcc82a94eb67ee453365dc3b158fc523dce9015 Subproject commit c985a4af6a017b3b517db12b03fdc0ffb1d24f7c
...@@ -29,6 +29,7 @@ add_unit_test(spawn ping_pong.cpp) ...@@ -29,6 +29,7 @@ add_unit_test(spawn ping_pong.cpp)
add_unit_test(simple_reply_response) add_unit_test(simple_reply_response)
add_unit_test(serial_reply) add_unit_test(serial_reply)
add_unit_test(or_else) add_unit_test(or_else)
add_unit_test(either)
add_unit_test(continuation) add_unit_test(continuation)
add_unit_test(constructor_attach) add_unit_test(constructor_attach)
add_unit_test(custom_exception_handler) add_unit_test(custom_exception_handler)
......
#include "test.hpp"
#include "caf/all.hpp"
using namespace caf;
using foo = typed_actor<replies_to<int>::with_either<int>::or_else<float>>;
foo::behavior_type my_foo(foo::pointer self) {
return {
[](int arg) -> either<int>::or_else<float> {
if (arg == 42) {
return 42;
}
return static_cast<float>(arg);
}
};
}
void test_either() {
auto f1 = []() -> either<int>::or_else<float> {
return 42;
};
auto f2 = []() -> either<int>::or_else<float> {
return 42.f;
};
auto f3 = [](bool flag) -> either<int, int>::or_else<float, float> {
if (flag) {
return {1, 2};
}
return {3.f, 4.f};
};
f1();
f2();
f3(true);
f3(false);
either<int>::or_else<float> x1{4};
either<int>::or_else<float> x2{4.f};
auto mf = spawn_typed(my_foo);
scoped_actor self;
self->sync_send(mf, 42).await(
[](int val) {
CAF_CHECK_EQUAL(val, 42);
},
[](float) {
CAF_FAILURE("expected an integer");
}
);
self->sync_send(mf, 10).await(
[](int) {
CAF_FAILURE("expected a float");
},
[](float val) {
CAF_CHECK_EQUAL(val, 10.f);
}
);
}
int main() {
CAF_TEST(test_either);
test_either();
return CAF_TEST_RESULT();
}
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "caf/uniform_type_info.hpp" #include "caf/uniform_type_info.hpp"
#include "caf/detail/ctm.hpp"
#include "caf/detail/int_list.hpp" #include "caf/detail/int_list.hpp"
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
...@@ -27,6 +28,32 @@ int main() { ...@@ -27,6 +28,32 @@ int main() {
CAF_TEST(test_metaprogramming); CAF_TEST(test_metaprogramming);
CAF_CHECK((ctm<type_list<int, float, double>, type_list<double, int, float>>::value));
CAF_CHECK((! ctm<type_list<int, float, double>, type_list<double, int, float, int>>::value));
CAF_CHECK((! ctm<type_list<int, float, double>, type_list<>>::value));
CAF_CHECK((! ctm<type_list<>, type_list<double, int, float, int>>::value));
using if1 = type_list<replies_to<int, double>::with<void>,
replies_to<int>::with<int>>;
using if2 = type_list<replies_to<int>::with<int>,
replies_to<int, double>::with<void>>;
using if3 = type_list<replies_to<int, double>::with<void>>;
using if4 = type_list<replies_to<int>::with<skip_message_t>,
replies_to<int, double>::with<void>>;
CAF_CHECK((ctm<if1, if2>::value));
CAF_CHECK((! ctm<if1, if3>::value));
CAF_CHECK((! ctm<if2, if3>::value));
CAF_CHECK((ctm<if1, if4>::value));
CAF_CHECK((ctm<if2, if4>::value));
CAF_CHECK((ctm<if4, if1>::value));
CAF_CHECK((ctm<if4, if2>::value));
return 0;
using l1 = type_list<int, float, std::string>; using l1 = type_list<int, float, std::string>;
using r1 = typename tl_reverse<l1>::type; using r1 = typename tl_reverse<l1>::type;
......
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