diff --git a/stlab/concurrency/future.hpp b/stlab/concurrency/future.hpp index baa0148be..227f40ddd 100644 --- a/stlab/concurrency/future.hpp +++ b/stlab/concurrency/future.hpp @@ -23,6 +23,9 @@ #include #include +#include +#include + /**************************************************************************************************/ namespace stlab { @@ -1245,39 +1248,42 @@ auto when_all(E executor, F f, const std::pair& range) { /**************************************************************************************************/ -template // models ForwardIterator that reference to a range of futures of the same type +template < + typename E, // models task executor + typename F, // models functional object + typename I> // models ForwardIterator that reference to a range of futures of the same type auto when_any(E executor, F f, const std::pair& range) { using param_t = typename std::iterator_traits::value_type::result_type; using result_t = typename detail::result_of_when_any_t::result_type; using context_result_t = std::conditional_t::value, void, param_t>; - using context_t = detail::common_context, - F, - detail::single_trigger, - detail::all_trigger>; + using context_t = detail::common_context, F, + detail::single_trigger, detail::all_trigger>; if (range.first == range.second) { - auto p = package_with_broken_promise(std::move(executor), - detail::context_result(std::move(f), 0)); + auto p = package_with_broken_promise( + std::move(executor), + detail::context_result(std::move(f), 0)); return std::move(p.second); } - return detail::create_range_of_futures::do_it(std::move(executor), - std::move(f), - range.first, range.second); + return detail::create_range_of_futures::do_it( + std::move(executor), std::move(f), range.first, range.second); } /**************************************************************************************************/ -template +template auto async(E executor, F&& f, Args&&... args) - -> future> -{ - auto p = package()>(executor, - std::bind([_f = std::forward(f)](Args&... args) mutable { - return _f(std::move(args)...); - }, std::forward(args)...)); + -> future(std::decay_t...)>> { + using result_type = std::result_of_t(std::decay_t...)>; + + auto p = package( + executor, std::bind( + [_f = std::forward(f)](unwrap_reference_t> & + ... args) mutable->result_type { + return _f(move_if>>(args)...); + }, + std::forward(args)...)); executor(std::move(p.first)); diff --git a/stlab/functional.hpp b/stlab/functional.hpp new file mode 100644 index 000000000..75a9124bb --- /dev/null +++ b/stlab/functional.hpp @@ -0,0 +1,63 @@ +/* + Copyright 2017 Adobe + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************************/ + +#ifndef STLAB_FUNCTIONAL_HPP +#define STLAB_FUNCTIONAL_HPP + +/**************************************************************************************************/ + +#include +#include + +/**************************************************************************************************/ + +namespace stlab { + +/**************************************************************************************************/ + +inline namespace v1 { + +/**************************************************************************************************/ + +template +struct unwrap_reference { + using type = T; +}; + +template +struct unwrap_reference> { + using type = T; +}; + +template +using unwrap_reference_t = typename unwrap_reference::type; + +/**************************************************************************************************/ + +template +struct is_reference_wrapper : std::false_type {}; +template +struct is_reference_wrapper> : std::true_type {}; + +template +constexpr bool is_reference_wrapper_v = is_reference_wrapper::value; + +/**************************************************************************************************/ + +} // namespace v1 + +/**************************************************************************************************/ + +} // namespace stlab + +/**************************************************************************************************/ + +#endif + +/**************************************************************************************************/ + diff --git a/stlab/test/model.hpp b/stlab/test/model.hpp index 897132b91..fa2d35691 100644 --- a/stlab/test/model.hpp +++ b/stlab/test/model.hpp @@ -11,7 +11,10 @@ /**************************************************************************************************/ +#include +#include #include +#include /**************************************************************************************************/ @@ -23,25 +26,80 @@ inline namespace v1 { /**************************************************************************************************/ +struct annotate_counters { + std::atomic_size_t _dtor{0}; + std::atomic_size_t _copy_ctor{0}; + std::atomic_size_t _move_ctor{0}; + std::atomic_size_t _copy_assign_lhs{0}; + std::atomic_size_t _copy_assign_rhs{0}; + std::atomic_size_t _move_assign_lhs{0}; + std::atomic_size_t _move_assign_rhs{0}; + std::atomic_size_t _swap{0}; + std::atomic_size_t _equality{0}; + std::mutex _mutex; + std::condition_variable _condition; + + std::size_t remaining() const { + return _copy_ctor + _move_ctor - _dtor + 1; + } + + void wait(std::size_t count) { + std::unique_lock lock(_mutex); + while (count != remaining()) + _condition.wait(lock); + } + + friend inline std::ostream& operator<<(std::ostream& out, const annotate_counters& x) { + out << " dtor: " << x._dtor << "\n"; + out << " copy_ctor: " << x._copy_ctor << "\n"; + out << " move_ctor: " << x._move_ctor << "\n"; + out << "copy_assign_lhs: " << x._copy_assign_lhs << "\n"; + out << "copy_assign_rhs: " << x._copy_assign_rhs << "\n"; + out << "move_assign_lhs: " << x._move_assign_lhs << "\n"; + out << "move_assign_rhs: " << x._move_assign_rhs << "\n"; + out << " swap: " << x._swap << "\n"; + out << " equality: " << x._equality << "\n"; + + return out; + } +}; + struct annotate { - annotate() { std::cout << "annotate ctor" << std::endl; } - ~annotate() { std::cout << "annotate dtor" << std::endl; } + annotate_counters* _counters; + explicit annotate(annotate_counters& counters) : _counters(&counters) {} + + ~annotate() { + { + ++_counters->_dtor; + _counters->_condition.notify_one(); + } + } - annotate(const annotate&) { std::cout << "annotate copy-ctor" << std::endl; } - annotate(annotate&&) noexcept { std::cout << "annotate move-ctor" << std::endl; } + annotate(const annotate& x) : _counters(x._counters) { ++_counters->_copy_ctor; } + annotate(annotate&& x) noexcept : _counters(x._counters) { ++_counters->_move_ctor; } - annotate& operator=(const annotate&) { - std::cout << "annotate assign" << std::endl; + annotate& operator=(const annotate& x) { + ++x._counters->_copy_assign_rhs; + ++_counters->_copy_assign_lhs; return *this; } - annotate& operator=(annotate&&) noexcept { - std::cout << "annotate move-assign" << std::endl; + annotate& operator=(annotate&& x) noexcept { + ++x._counters->_move_assign_rhs; + ++_counters->_move_assign_lhs; return *this; } - friend inline void swap(annotate&, annotate&) { std::cout << "annotate swap" << std::endl; } - friend inline bool operator==(const annotate&, const annotate&) { return true; } - friend inline bool operator!=(const annotate&, const annotate&) { return false; } + friend inline void swap(annotate& x, annotate& y) { + ++x._counters->_swap; + ++y._counters->_swap; + } + + friend inline bool operator==(const annotate& x, const annotate& y) { + ++x._counters->_equality; + ++y._counters->_equality; + return true; + } + friend inline bool operator!=(const annotate& x, const annotate& y) { return !(x == y); } }; /**************************************************************************************************/ diff --git a/stlab/utility.hpp b/stlab/utility.hpp new file mode 100644 index 000000000..c9ab4d6a5 --- /dev/null +++ b/stlab/utility.hpp @@ -0,0 +1,66 @@ +/* + Copyright 2017 Adobe + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************************/ + +#ifndef STLAB_UTILITY_HPP +#define STLAB_UTILITY_HPP + +/**************************************************************************************************/ + +#include + +/**************************************************************************************************/ + +namespace stlab { + +/**************************************************************************************************/ + +inline namespace v1 { + +/**************************************************************************************************/ + +namespace detail { + +template +struct move_if_helper; + +template +struct move_if_helper { + using type = std::remove_reference_t&&; +}; + +template +struct move_if_helper { + using type = std::remove_reference_t&; +}; + +template +using move_if_helper_t = typename move_if_helper::type; + +} // namespace detail + +/**************************************************************************************************/ + +template +constexpr detail::move_if_helper_t move_if(T&& t) noexcept { + return static_cast>(t); +} + +/**************************************************************************************************/ + +} // namespace v1 + +/**************************************************************************************************/ + +} // namespace stlab + +/**************************************************************************************************/ + +#endif + +/**************************************************************************************************/ + diff --git a/test/future_tests.cpp b/test/future_tests.cpp index 537e826e6..65ea9d645 100644 --- a/test/future_tests.cpp +++ b/test/future_tests.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include "future_test_helper.hpp" @@ -17,6 +18,177 @@ using namespace stlab; using namespace future_test_helper; +/**************************************************************************************************/ + +BOOST_AUTO_TEST_CASE(async_lambda_arguments) { + { + BOOST_TEST_MESSAGE("running async lambda argument of type rvalue -> value"); + + annotate_counters counters; + async(immediate_executor, [](annotate x){ }, annotate(counters)); + BOOST_REQUIRE(counters.remaining() == 0); + BOOST_REQUIRE(counters._copy_ctor == 0); + } + + { + BOOST_TEST_MESSAGE("running async lambda argument of type lvalue -> value"); + + annotate_counters counters; + annotate x(counters); + async(immediate_executor, [](annotate x){ }, x); + BOOST_REQUIRE(counters.remaining() == 1); + BOOST_REQUIRE(counters._copy_ctor == 1); + } + + { + BOOST_TEST_MESSAGE("running async lambda argument of type ref -> value"); + + annotate_counters counters; + annotate x(counters); + async(immediate_executor, [](annotate x){ }, std::ref(x)); + BOOST_REQUIRE(counters.remaining() == 1); + BOOST_REQUIRE(counters._copy_ctor == 1); + } + + { + BOOST_TEST_MESSAGE("running async lambda argument of type cref -> value"); + + annotate_counters counters; + annotate x(counters); + async(immediate_executor, [](annotate x){ }, std::cref(x)); + BOOST_REQUIRE(counters.remaining() == 1); + BOOST_REQUIRE(counters._copy_ctor == 1); + } +//------- +#if 0 + { + // EXPECTED WILL NOT COMPILE + BOOST_TEST_MESSAGE("running async lambda argument of type rvalue -> &"); + + annotate_counters counters; + async(immediate_executor, [](annotate& x){ }, annotate(counters)); + BOOST_REQUIRE(counters.remaining() == 0); + BOOST_REQUIRE(counters._copy_ctor == 0); + } + + { + BOOST_TEST_MESSAGE("running async lambda argument of type lvalue -> &"); + + annotate_counters counters; + annotate x(counters); + async(immediate_executor, [](annotate& x){ }, x); + BOOST_REQUIRE(counters.remaining() == 1); + BOOST_REQUIRE(counters._copy_ctor == 1); + } +#endif + + { + BOOST_TEST_MESSAGE("running async lambda argument of type ref -> &"); + + annotate_counters counters; + annotate x(counters); + async(immediate_executor, [](annotate& x){ }, std::ref(x)); + BOOST_REQUIRE(counters.remaining() == 1); + BOOST_REQUIRE(counters._copy_ctor == 0); + } + +#if 0 + // EXPECTED WILL NOT COMPILE + { + BOOST_TEST_MESSAGE("running async lambda argument of type cref -> &"); + + annotate_counters counters; + annotate x(counters); + async(immediate_executor, [](annotate& x){ }, std::cref(x)); + BOOST_REQUIRE(counters.remaining() == 1); + BOOST_REQUIRE(counters._copy_ctor == 1); + } +#endif +//------- + { + BOOST_TEST_MESSAGE("running async lambda argument of type rvalue -> const&"); + + annotate_counters counters; + async(immediate_executor, [](const annotate& x){ }, annotate(counters)); + BOOST_REQUIRE(counters.remaining() == 0); + BOOST_REQUIRE(counters._copy_ctor == 0); + } + + { + BOOST_TEST_MESSAGE("running async lambda argument of type lvalue -> const&"); + + annotate_counters counters; + annotate x(counters); + async(immediate_executor, [](const annotate& x){ }, x); + BOOST_REQUIRE(counters.remaining() == 1); + BOOST_REQUIRE(counters._copy_ctor == 1); + } + + { + BOOST_TEST_MESSAGE("running async lambda argument of type ref -> const&"); + + annotate_counters counters; + annotate x(counters); + async(immediate_executor, [](const annotate& x){ }, std::ref(x)); + BOOST_REQUIRE(counters.remaining() == 1); + BOOST_REQUIRE(counters._copy_ctor == 0); + } + + { + BOOST_TEST_MESSAGE("running async lambda argument of type cref -> const&"); + + annotate_counters counters; + annotate x(counters); + async(immediate_executor, [](const annotate& x){ }, std::cref(x)); + BOOST_REQUIRE(counters.remaining() == 1); + BOOST_REQUIRE(counters._copy_ctor == 0); + } +//------- + { + BOOST_TEST_MESSAGE("running async lambda argument of type rvalue -> &&"); + + annotate_counters counters; + async(immediate_executor, [](annotate&& x){ }, annotate(counters)); + BOOST_REQUIRE(counters.remaining() == 0); + BOOST_REQUIRE(counters._copy_ctor == 0); + } + + { + BOOST_TEST_MESSAGE("running async lambda argument of type lvalue -> &&"); + + annotate_counters counters; + annotate x(counters); + async(immediate_executor, [](annotate&& x){ }, x); + BOOST_REQUIRE(counters.remaining() == 1); + BOOST_REQUIRE(counters._copy_ctor == 1); + } + +#if 0 + // EXPECTED WILL NOT COMPILE + { + BOOST_TEST_MESSAGE("running async lambda argument of type ref -> &&"); + + annotate_counters counters; + annotate x(counters); + async(immediate_executor, [](annotate&& x){ }, std::ref(x)); + BOOST_REQUIRE(counters.remaining() == 1); + BOOST_REQUIRE(counters._copy_ctor == 0); + } + + { + BOOST_TEST_MESSAGE("running async lambda argument of type cref -> &&"); + + annotate_counters counters; + annotate x(counters); + async(immediate_executor, [](annotate&& x){ }, std::cref(x)); + BOOST_REQUIRE(counters.remaining() == 1); + BOOST_REQUIRE(counters._copy_ctor == 0); + } +#endif +} + +/**************************************************************************************************/ + using all_test_types = boost::mpl::list>; BOOST_AUTO_TEST_CASE_TEMPLATE(future_default_constructed, T, all_test_types)