Commit 1c883a73 authored by neverlord's avatar neverlord

improved guard expressions

parent cf13709e
...@@ -171,8 +171,12 @@ class projection<util::type_list<> > ...@@ -171,8 +171,12 @@ class projection<util::type_list<> >
template<class PartialFun> template<class PartialFun>
bool operator()(PartialFun& fun) const bool operator()(PartialFun& fun) const
{ {
fun(); if (fun.defined_at())
return true; {
fun();
return true;
}
return false;
} }
}; };
......
...@@ -38,8 +38,10 @@ ...@@ -38,8 +38,10 @@
#include <type_traits> #include <type_traits>
#include "cppa/config.hpp" #include "cppa/config.hpp"
#include "cppa/option.hpp"
#include "cppa/util/at.hpp" #include "cppa/util/at.hpp"
#include "cppa/util/rm_ref.hpp"
#include "cppa/util/void_type.hpp" #include "cppa/util/void_type.hpp"
#include "cppa/util/apply_tuple.hpp" #include "cppa/util/apply_tuple.hpp"
...@@ -113,6 +115,63 @@ struct guard_expr ...@@ -113,6 +115,63 @@ struct guard_expr
SubMacro (not_equal_op, !=) SubMacro (not_equal_op, !=)
template<typename T>
struct ge_mutable_reference_wrapper
{
T* value;
ge_mutable_reference_wrapper() : value(nullptr) { }
ge_mutable_reference_wrapper(T&&) = delete;
ge_mutable_reference_wrapper(T& vref) : value(&vref) { }
ge_mutable_reference_wrapper(ge_mutable_reference_wrapper const&) = default;
ge_mutable_reference_wrapper& operator=(T& vref)
{
value = &vref;
return *this;
}
ge_mutable_reference_wrapper& operator=(ge_mutable_reference_wrapper const&) = default;
T& get() const { CPPA_REQUIRE(value != 0); return *value; }
operator T& () const { CPPA_REQUIRE(value != 0); return *value; }
};
template<typename T>
struct ge_reference_wrapper
{
T const* value;
ge_reference_wrapper(T&&) = delete;
ge_reference_wrapper() : value(nullptr) { }
ge_reference_wrapper(T const& val_ref) : value(&val_ref) { }
ge_reference_wrapper(ge_reference_wrapper const&) = default;
ge_reference_wrapper& operator=(T const& vref)
{
value = &vref;
return *this;
}
ge_reference_wrapper& operator=(ge_reference_wrapper const&) = default;
T const& get() const { CPPA_REQUIRE(value != 0); return *value; }
operator T const& () const { CPPA_REQUIRE(value != 0); return *value; }
};
// support use of gref(BooleanVariable) as receive loop 'guard'
template<>
struct ge_reference_wrapper<bool>
{
bool const* value;
ge_reference_wrapper(bool&&) = delete;
ge_reference_wrapper(bool const& val_ref) : value(&val_ref) { }
ge_reference_wrapper(ge_reference_wrapper const&) = default;
ge_reference_wrapper& operator=(ge_reference_wrapper const&) = default;
bool const& get() const { CPPA_REQUIRE(value != 0); return *value; }
operator bool const& () const { CPPA_REQUIRE(value != 0); return *value; }
bool operator()() const { CPPA_REQUIRE(value != 0); return *value; }
};
/**
* @brief Create a reference wrapper similar to std::reference_wrapper<const T>
* that could be used in guard expressions or to enforce lazy evaluation.
*/
template<typename T>
ge_reference_wrapper<T> gref(T const& value) { return {value}; }
// bind utility for placeholders // bind utility for placeholders
template<typename Fun, typename T1> template<typename Fun, typename T1>
...@@ -176,6 +235,7 @@ struct ge_search_container ...@@ -176,6 +235,7 @@ struct ge_search_container
{ {
bool sc; bool sc;
ge_search_container(bool should_contain) : sc(should_contain) { } ge_search_container(bool should_contain) : sc(should_contain) { }
template<class C> template<class C>
bool operator()(C const& haystack, bool operator()(C const& haystack,
typename C::value_type const& needle) const typename C::value_type const& needle) const
...@@ -187,12 +247,55 @@ struct ge_search_container ...@@ -187,12 +247,55 @@ struct ge_search_container
return std::none_of(haystack.begin(), haystack.end(), return std::none_of(haystack.begin(), haystack.end(),
[&](vtype const& val) { return needle == val; }); [&](vtype const& val) { return needle == val; });
} }
};
struct ge_get_size
{
template<class C> template<class C>
bool operator()(std::reference_wrapper<C> const& haystack_ref, inline auto operator()(C const& what) const -> decltype(what.size())
typename C::value_type const& needle) const {
return what.size();
}
};
struct ge_is_empty
{
bool expected;
ge_is_empty(bool expected_value) : expected(expected_value) { }
template<class C>
inline bool operator()(C const& what) const
{
return what.empty() == expected;
}
};
struct ge_get_front
{
template<class C>
inline auto operator()(C const& what,
typename std::enable_if<
std::is_reference<
decltype(what.front())
>::value
>::type* = 0) const
-> option<
std::reference_wrapper<
const typename util::rm_ref<decltype(what.front())>::type> >
{
if (what.empty() == false) return {what.front()};
return {};
}
template<class C>
inline auto operator()(C const& what,
typename std::enable_if<
std::is_reference<
decltype(what.front())
>::value == false
>::type* = 0) const
-> option<decltype(what.front())>
{ {
return (*this)(haystack_ref.get(), needle); if (what.empty() == false) return {what.front()};
return {};
} }
}; };
...@@ -220,16 +323,42 @@ struct guard_placeholder ...@@ -220,16 +323,42 @@ struct guard_placeholder
return std::equal(rhs.begin(), rhs.end(), lhs.begin()); return std::equal(rhs.begin(), rhs.end(), lhs.begin());
} }
/**
* @brief Evaluates to the size of a container.
*/
typename gcall1<ge_get_size, guard_placeholder>::result size() const
{
return gcall(ge_get_size{}, *this);
}
typename gcall1<ge_is_empty, guard_placeholder>::result empty() const
{
return gcall(ge_is_empty{true}, *this);
}
typename gcall1<ge_is_empty, guard_placeholder>::result not_empty() const
{
return gcall(ge_is_empty{false}, *this);
}
/**
* @brief Evaluates to the first element of a container if it's not empty.
*/
typename gcall1<ge_get_front, guard_placeholder>::result front() const
{
return gcall(ge_get_front{}, *this);
}
/** /**
* @brief Evaluates to true if unbound argument starts with @p str. * @brief Evaluates to true if unbound argument starts with @p str.
*/ */
typename gcall2<decltype(&guard_placeholder::u8_starts_with), typename gcall2<decltype(&guard_placeholder::u8_starts_with),
guard_placeholder, guard_placeholder,
std::string std::string
>::result >::result
starts_with(std::string str) const starts_with(std::string str) const
{ {
return gcall(&guard_placeholder::u8_starts_with, *this, str); return gcall(&guard_placeholder::u8_starts_with, *this, std::move(str));
} }
/** /**
...@@ -240,21 +369,7 @@ struct guard_placeholder ...@@ -240,21 +369,7 @@ struct guard_placeholder
typename gcall2<ge_search_container, C, guard_placeholder>::result typename gcall2<ge_search_container, C, guard_placeholder>::result
in(C container) const in(C container) const
{ {
return gcall(ge_search_container{true}, container, *this); return gcall(ge_search_container{true}, std::move(container), *this);
}
/**
* @brief Evaluates to true if unbound argument
* is contained in @p container.
*/
template<class C>
typename gcall2<ge_search_container,
std::reference_wrapper<C>,
guard_placeholder
>::result
in(std::reference_wrapper<C> container) const
{
return gcall(ge_search_container{true}, container, *this);
} }
/** /**
...@@ -270,7 +385,7 @@ struct guard_placeholder ...@@ -270,7 +385,7 @@ struct guard_placeholder
{ {
std::vector<typename detail::strip_and_convert<T>::type> vec; std::vector<typename detail::strip_and_convert<T>::type> vec;
for (auto& i : list) vec.emplace_back(i); for (auto& i : list) vec.emplace_back(i);
return in(vec); return in(std::move(vec));
} }
/** /**
...@@ -281,21 +396,7 @@ struct guard_placeholder ...@@ -281,21 +396,7 @@ struct guard_placeholder
typename gcall2<ge_search_container, C, guard_placeholder>::result typename gcall2<ge_search_container, C, guard_placeholder>::result
not_in(C container) const not_in(C container) const
{ {
return gcall(ge_search_container{false}, container, *this); return gcall(ge_search_container{false}, std::move(container), *this);
}
/**
* @brief Evaluates to true if unbound argument
* is not contained in @p container.
*/
template<class C>
typename gcall2<ge_search_container,
std::reference_wrapper<C>,
guard_placeholder
>::result
not_in(std::reference_wrapper<C> container) const
{
return gcall(ge_search_container{false}, container, *this);
} }
/** /**
...@@ -316,85 +417,31 @@ struct guard_placeholder ...@@ -316,85 +417,31 @@ struct guard_placeholder
}; };
template<typename T> // result type computation
struct ge_mutable_reference_wrapper
{
T* value;
ge_mutable_reference_wrapper() : value(nullptr) { }
ge_mutable_reference_wrapper(T&&) = delete;
ge_mutable_reference_wrapper(T& vref) : value(&vref) { }
ge_mutable_reference_wrapper(ge_mutable_reference_wrapper const&) = default;
ge_mutable_reference_wrapper& operator=(T& vref)
{
value = &vref;
return *this;
}
ge_mutable_reference_wrapper& operator=(ge_mutable_reference_wrapper const&) = default;
T& get() const { CPPA_REQUIRE(value != 0); return *value; }
operator T& () const { CPPA_REQUIRE(value != 0); return *value; }
};
template<typename T>
struct ge_reference_wrapper
{
T const* value;
ge_reference_wrapper(T&&) = delete;
ge_reference_wrapper() : value(nullptr) { }
ge_reference_wrapper(T const& val_ref) : value(&val_ref) { }
ge_reference_wrapper(ge_reference_wrapper const&) = default;
ge_reference_wrapper& operator=(T const& vref)
{
value = &vref;
return *this;
}
ge_reference_wrapper& operator=(ge_reference_wrapper const&) = default;
T const& get() const { CPPA_REQUIRE(value != 0); return *value; }
operator T const& () const { CPPA_REQUIRE(value != 0); return *value; }
};
// support use of gref(BooleanVariable) as receive loop 'guard'
template<>
struct ge_reference_wrapper<bool>
{
bool const* value;
ge_reference_wrapper(bool&&) = delete;
ge_reference_wrapper(bool const& val_ref) : value(&val_ref) { }
ge_reference_wrapper(ge_reference_wrapper const&) = default;
ge_reference_wrapper& operator=(ge_reference_wrapper const&) = default;
bool get() const { CPPA_REQUIRE(value != 0); return *value; }
operator bool () const { CPPA_REQUIRE(value != 0); return *value; }
bool operator()() const { CPPA_REQUIRE(value != 0); return *value; }
};
/**
* @brief Create a reference wrapper similar to std::reference_wrapper<const T>
* that could be used in guard expressions or to enforce lazy evaluation.
*/
template<typename T>
ge_reference_wrapper<T> gref(T const& value) { return {value}; }
template<typename T, class Tuple>
struct ge_unbound { typedef T type; };
// result type computation template<typename T, class Tuple>
struct ge_unbound<ge_reference_wrapper<T>, Tuple> { typedef T type; };
template<typename T, class Tuple> template<typename T, class Tuple>
struct ge_unbound struct ge_unbound<std::reference_wrapper<T>, Tuple> { typedef T type; };
{
typedef T type;
};
template<typename T, class Tuple> template<typename T, class Tuple>
struct ge_unbound<ge_reference_wrapper<T>, Tuple> struct ge_unbound<std::reference_wrapper<const T>, Tuple> { typedef T type; };
{
typedef T type;
};
// unbound type of placeholder // unbound type of placeholder
template<int X, typename... Ts> template<int X, typename... Ts>
struct ge_unbound<guard_placeholder<X>, detail::tdata<std::reference_wrapper<Ts>...> > struct ge_unbound<guard_placeholder<X>, detail::tdata<Ts...> >
{ {
static_assert(X < sizeof...(Ts), static_assert(X < sizeof...(Ts),
"Cannot unbind placeholder (too few arguments)"); "Cannot unbind placeholder (too few arguments)");
typedef typename util::at<X, Ts...>::type type; typedef typename ge_unbound<
typename util::at<X, Ts...>::type,
detail::tdata<std::reference_wrapper<Ts>...>
>::type
type;
}; };
// operators, operators, operators // operators, operators, operators
...@@ -552,6 +599,18 @@ inline T const& ge_resolve(Tuple const&, T const& value) ...@@ -552,6 +599,18 @@ inline T const& ge_resolve(Tuple const&, T const& value)
return value; return value;
} }
template<class Tuple, typename T>
inline T const& ge_resolve(Tuple const&, std::reference_wrapper<T> const& value)
{
return value.get();
}
template<class Tuple, typename T>
inline T const& ge_resolve(Tuple const&, std::reference_wrapper<const T> const& value)
{
return value.get();
}
template<class Tuple, typename T> template<class Tuple, typename T>
inline T const& ge_resolve(Tuple const&, ge_reference_wrapper<T> const& value) inline T const& ge_resolve(Tuple const&, ge_reference_wrapper<T> const& value)
{ {
......
...@@ -220,45 +220,45 @@ class option ...@@ -220,45 +220,45 @@ class option
}; };
/** @relates option */ /** @relates option */
template<typename T> template<typename T, typename U>
bool operator==(option<T> const& lhs, option<T> const& rhs) bool operator==(option<T> const& lhs, option<U> const& rhs)
{ {
if ((lhs) && (rhs)) return *lhs == *rhs; if ((lhs) && (rhs)) return *lhs == *rhs;
return false; return false;
} }
/** @relates option */ /** @relates option */
template<typename T> template<typename T, typename U>
bool operator==(option<T> const& lhs, T const& rhs) bool operator==(option<T> const& lhs, U const& rhs)
{ {
if (lhs) return *lhs == rhs; if (lhs) return *lhs == rhs;
return false; return false;
} }
/** @relates option */ /** @relates option */
template<typename T> template<typename T, typename U>
bool operator==(T const& lhs, option<T> const& rhs) bool operator==(T const& lhs, option<U> const& rhs)
{ {
return rhs == lhs; return rhs == lhs;
} }
/** @relates option */ /** @relates option */
template<typename T> template<typename T, typename U>
bool operator!=(option<T> const& lhs, option<T> const& rhs) bool operator!=(option<T> const& lhs, option<U> const& rhs)
{ {
return !(lhs == rhs); return !(lhs == rhs);
} }
/** @relates option */ /** @relates option */
template<typename T> template<typename T, typename U>
bool operator!=(option<T> const& lhs, T const& rhs) bool operator!=(option<T> const& lhs, U const& rhs)
{ {
return !(lhs == rhs); return !(lhs == rhs);
} }
/** @relates option */ /** @relates option */
template<typename T> template<typename T, typename U>
bool operator!=(T const& lhs, option<T> const& rhs) bool operator!=(T const& lhs, option<U> const& rhs)
{ {
return !(lhs == rhs); return !(lhs == rhs);
} }
......
#include <functional> #include <functional>
#include "test.hpp" #include "test.hpp"
#include "cppa/cppa.hpp"
#include "cppa/on.hpp" #include "cppa/on.hpp"
#include "cppa/match.hpp" #include "cppa/match.hpp"
#include "cppa/announce.hpp" #include "cppa/announce.hpp"
...@@ -34,15 +35,19 @@ size_t test__match() ...@@ -34,15 +35,19 @@ size_t test__match()
using namespace std::placeholders; using namespace std::placeholders;
using namespace cppa::placeholders; using namespace cppa::placeholders;
auto expr0 = gcall(ascending, _x1, _x2, _x3); auto expr0_a = gcall(ascending, _x1, _x2, _x3);
CPPA_CHECK(ge_invoke(expr0, 1, 2, 3)); CPPA_CHECK(ge_invoke(expr0_a, 1, 2, 3));
CPPA_CHECK(!ge_invoke(expr0, 3, 2, 1)); CPPA_CHECK(!ge_invoke(expr0_a, 3, 2, 1));
int ival0 = 2; int ival0 = 2;
auto expr01 = gcall(ascending, _x1, std::ref(ival0), _x2); auto expr0_b = gcall(ascending, _x1, std::ref(ival0), _x2);
CPPA_CHECK(ge_invoke(expr01, 1, 3)); CPPA_CHECK(ge_invoke(expr0_b, 1, 3));
++ival0; ++ival0;
CPPA_CHECK(!ge_invoke(expr01, 1, 3)); CPPA_CHECK(!ge_invoke(expr0_b, 1, 3));
auto expr0_c = gcall(ascending, 10, _x1, 30);
CPPA_CHECK(!ge_invoke(expr0_c, 10));
CPPA_CHECK(ge_invoke(expr0_c, 11));
auto expr1 = _x1 + _x2; auto expr1 = _x1 + _x2;
auto expr2 = _x1 + _x2 < _x3; auto expr2 = _x1 + _x2 < _x3;
...@@ -106,6 +111,34 @@ size_t test__match() ...@@ -106,6 +111,34 @@ size_t test__match()
"wrong return type"); "wrong return type");
CPPA_CHECK_EQUAL(42, (ge_invoke(expr15, 7, 10, 25))); CPPA_CHECK_EQUAL(42, (ge_invoke(expr15, 7, 10, 25)));
std::string expr16_str;// = "expr16";
auto expr16_a = _x1.size();
auto expr16_b = _x1.front() == 'e';
CPPA_CHECK_EQUAL(false, ge_invoke(expr16_b, expr16_str));
CPPA_CHECK_EQUAL(0, ge_invoke(expr16_a, expr16_str));
expr16_str = "expr16";
CPPA_CHECK_EQUAL(true, ge_invoke(expr16_b, expr16_str));
CPPA_CHECK_EQUAL(expr16_str.size(), ge_invoke(expr16_a, expr16_str));
expr16_str.front() = '_';
CPPA_CHECK_EQUAL(false, ge_invoke(expr16_b, expr16_str));
int expr17_value = 42;
auto expr17 = gref(expr17_value) == 42;
CPPA_CHECK_EQUAL(true, ge_invoke(expr17));
expr17_value = 0;
CPPA_CHECK_EQUAL(false, ge_invoke(expr17));
int expr18_value = 42;
auto expr18_a = gref(expr18_value) == 42;
CPPA_CHECK_EQUAL(true, ge_invoke(expr18_a));
expr18_value = 0;
CPPA_CHECK_EQUAL(false, ge_invoke(expr18_a));
auto expr18_b = gref(expr18_value) == _x1;
auto expr18_c = std::ref(expr18_value) == _x1;
CPPA_CHECK_EQUAL(true, ge_invoke(expr18_b, 0));
CPPA_CHECK_EQUAL(true, ge_invoke(expr18_c, 0));
bool invoked = false; bool invoked = false;
match("abc") match("abc")
( (
...@@ -117,6 +150,46 @@ size_t test__match() ...@@ -117,6 +150,46 @@ size_t test__match()
if (!invoked) { CPPA_ERROR("match(\"abc\") failed"); } if (!invoked) { CPPA_ERROR("match(\"abc\") failed"); }
invoked = false; invoked = false;
bool disable_case1 = true;
bool case1_invoked = false;
bool case2_invoked = false;
auto expr19 =
(
on<anything>().when(gref(disable_case1) == false) >> [&]()
{
case1_invoked = true;
},
on<anything>() >> [&]()
{
case2_invoked = true;
}
);
any_tuple expr19_tup = make_cow_tuple("hello guard!");
expr19(expr19_tup);
CPPA_CHECK(!case1_invoked);
CPPA_CHECK(case2_invoked);
partial_function expr20 = expr19;
case1_invoked = false;
case2_invoked = false;
disable_case1 = false;
expr20(expr19_tup);
CPPA_CHECK(case1_invoked);
CPPA_CHECK(!case2_invoked);
std::vector<int> expr21_vec_a{1, 2, 3};
std::vector<int> expr21_vec_b{1, 0, 2};
auto vec_sorted = [](std::vector<int> const& vec)
{
return std::is_sorted(vec.begin(), vec.end());
};
auto expr21 = gcall(vec_sorted, _x1);
CPPA_CHECK(ge_invoke(expr21, expr21_vec_a));
CPPA_CHECK(!ge_invoke(expr21, expr21_vec_b));
auto expr22 = _x1.empty() && _x2.not_empty();
CPPA_CHECK(ge_invoke(expr22, std::string(""), std::string("abc")));
match(std::vector<int>{1, 2, 3}) match(std::vector<int>{1, 2, 3})
( (
on<int, int, int>().when( _x1 + _x2 + _x3 == 6 on<int, int, int>().when( _x1 + _x2 + _x3 == 6
......
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