diff --git a/CMakeLists.txt b/CMakeLists.txt index 04c3c8a..d564e3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,13 +73,13 @@ target_compile_options(ustdex INTERFACE $<$:/Zc:__cplusplus /Zc:hiddenFriend /Zc:preprocessor /Zc:externConstexpr>) # download CPM.cmake -file( - DOWNLOAD - https://github.com/cpm-cmake/CPM.cmake/releases/download/v0.39.0/CPM.cmake - ${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake - EXPECTED_HASH SHA256=66639bcac9dd2907b2918de466783554c1334446b9874e90d38e3778d404c2ef -) -include(${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake) +#file( +# DOWNLOAD +# https://github.com/cpm-cmake/CPM.cmake/releases/download/v0.39.0/CPM.cmake +# ${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake +# EXPECTED_HASH SHA256=66639bcac9dd2907b2918de466783554c1334446b9874e90d38e3778d404c2ef +#) +#include(${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake) if (USTDEX_ENABLE_CUDA) target_compile_features(ustdex INTERFACE cuda_std_17) diff --git a/CMakePresets.json b/CMakePresets.json index 9704f3b..a3662f2 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,36 +1,59 @@ { - "version": 3, - "cmakeMinimumRequired": { - "major": 3, - "minor": 21, - "patch": 3 + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 3 + }, + "configurePresets": [ + { + "name": "Debug", + "binaryDir": "${sourceDir}/build/Debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_STANDARD": "17", + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG}/scripts/buildsystems/vcpkg.cmake" + } }, - "configurePresets": [ - { - "name": "Debug", - "binaryDir": "${sourceDir}/build/Debug", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_CXX_STANDARD": "17" - } + { + "name": "Release", + "binaryDir": "${sourceDir}/build/Release", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CXX_STANDARD": "17", + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG}/scripts/buildsystems/vcpkg.cmake" + } + }, + { + "name": "clang-x64-debug", + "displayName": "Clang x64 Debug", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "architecture": { + "value": "x64", + "strategy": "external" }, - { - "name": "Release", - "binaryDir": "${sourceDir}/build/Release", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release", - "CMAKE_CXX_STANDARD": "17" + "cacheVariables": { + "CMAKE_C_COMPILER": "clang.exe", + "CMAKE_CXX_COMPILER": "clang++.exe", + "CMAKE_BUILD_TYPE": "Debug" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "intelliSenseMode": "windows-clang-x64" } } - ], - "buildPresets": [ - { - "name": "Debug", - "configurePreset": "Debug" - }, - { - "name": "Release", - "configurePreset": "Release" - } - ] - } + } + ], + "buildPresets": [ + { + "name": "Debug", + "configurePreset": "Debug" + }, + { + "name": "Release", + "configurePreset": "Release" + }, + + ] +} \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7ea0d6b..5555ff2 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -12,11 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. + +#find_package(asio CONFIG REQUIRED) + add_executable(_clangd_helper_file detail/_clangd_helper_file.cpp) target_link_libraries(_clangd_helper_file PUBLIC ustdex) add_executable(scratch scratch.cpp) target_link_libraries(scratch PUBLIC ustdex) +#target_link_libraries(scratch PUBLIC ustdex asio::asio) if (USTDEX_ENABLE_CUDA) add_executable(cuscratch scratch.cu) diff --git a/examples/asio_thread_pool.hpp b/examples/asio_thread_pool.hpp new file mode 100644 index 0000000..faa8508 --- /dev/null +++ b/examples/asio_thread_pool.hpp @@ -0,0 +1,135 @@ +#pragma once + +#include +#include + +namespace atp +{ + template + struct _operation + { + asio::any_io_executor _executor_; + USTDEX_NO_UNIQUE_ADDRESS Rcvr _rcvr_; + + USTDEX_API static void _execute_impl(_operation* _p) noexcept + { + auto& _rcvr = _p->_rcvr_; + try + { + if (ustdex::get_stop_token(ustdex::get_env(_rcvr)).stop_requested()) + { + ustdex::set_stopped(static_cast(_rcvr)); + } + else + { + ustdex::set_value(static_cast(_rcvr)); + } + } + catch (...) + { + ustdex::set_error(static_cast(_rcvr), ::std::current_exception()); + } + + } + + USTDEX_API _operation(asio::any_io_executor executor, Rcvr _rcvr) : + _executor_{ executor } + , _rcvr_{ static_cast(_rcvr) } + {} + + USTDEX_API void start() & noexcept; + }; + + class asio_scheduler + { + struct _schedule_task + { + using sender_concept = ustdex::sender_t; + + template + USTDEX_API auto connect(Rcvr _rcvr) const noexcept -> _operation + { + return { _executor_, static_cast(_rcvr) }; + } + + template + USTDEX_API static constexpr auto get_completion_signatures() noexcept + { + return ustdex::completion_signatures(); + } + + private: + friend asio_scheduler; + + struct _env + { + asio::any_io_executor _executor_; + + template + USTDEX_API auto query(ustdex::get_completion_scheduler_t) const noexcept -> asio_scheduler + { + return asio_scheduler{ _executor_ }; + } + }; + + USTDEX_API auto get_env() const noexcept -> _env + { + return _env{ _executor_ }; + } + + USTDEX_API explicit _schedule_task(asio::any_io_executor executor) noexcept + : _executor_(executor) + {} + + asio::any_io_executor const _executor_; + }; + + USTDEX_API auto query(ustdex::get_forward_progress_guarantee_t) const noexcept -> ustdex::forward_progress_guarantee + { + return ustdex::forward_progress_guarantee::parallel; + } + + asio::any_io_executor _executor_; + + public: + using scheduler_concept = ustdex::scheduler_t; + + USTDEX_API explicit asio_scheduler(asio::any_io_executor executor) noexcept + : _executor_(executor) + {} + + [[nodiscard]] USTDEX_API auto schedule() const noexcept -> _schedule_task + { + return _schedule_task{ _executor_ }; + } + + USTDEX_API friend bool operator==(const asio_scheduler& _a, const asio_scheduler& _b) noexcept + { + return _a._executor_ == _b._executor_; + } + + USTDEX_API friend bool operator!=(const asio_scheduler& _a, const asio_scheduler& _b) noexcept + { + return _a._executor_ != _b._executor_; + } + }; + + template + USTDEX_API inline void _operation::start() & noexcept + { + try + { + asio::post(_executor_, + [this]() + { + _execute_impl(this); + }); + } + catch(...) + { + ustdex::set_error(static_cast(_rcvr_), ::std::current_exception()); + } + + } + +} \ No newline at end of file diff --git a/examples/parallel_sort.hpp b/examples/parallel_sort.hpp new file mode 100644 index 0000000..784469d --- /dev/null +++ b/examples/parallel_sort.hpp @@ -0,0 +1,220 @@ +#pragma once + +#include + +#define use_basic_sender + +#ifdef use_basic_sender +namespace { + +struct parallel_sort_t +{ + template + auto operator()(Sch sch, RandomAccessIterator begin, RandomAccessIterator end, SortFun sort_fun) const noexcept + { + return ustdex::_make_sexpr(ustdex::_mk_tuple(begin, end, sort_fun, sch), ustdex::schedule(sch)); + } +}; + +struct __impl : ustdex::__sexpr_defaults +{ +private: + //Ïû³ýÄ£°åµÝ¹é + struct exec_then + { + template + void operator()(Args&&... args) + { + _fun_(); + } + std::function _fun_; + }; + +public: + static constexpr struct + { + template + auto operator()(_Sender&&, Env&&...) const noexcept + { + using RAI = typename ustdex::data_of<_Sender>::template at<0>; + return ustdex::completion_signatures(); + }; + } get_completion_signatures; + + static constexpr struct + { + template + auto operator()( + ustdex::_ignore, + _State _state, + _Receiver& _rcvr, + _SetTag, + _Args&&... _args) const noexcept + { + auto _begin_ = ustdex::_cget<0>(_state); + auto _end_ = ustdex::_cget<1>(_state); + auto _sort_fun_ = ustdex::_cget<2>(_state); + + if constexpr (std::is_same_v<_SetTag, ustdex::set_value_t>) + { + auto _begin_ = ustdex::_cget<0>(_state); + auto _end_ = ustdex::_cget<1>(_state); + auto _sort_fun_ = ustdex::_cget<2>(_state); + auto _sch_ = ustdex::_cget<3>(_state); + auto n = std::distance(_begin_, _end_); + if (n <= 16384) + { + std::sort(_begin_, _end_, _sort_fun_); + ustdex::set_value(std::move(_rcvr), std::move(_begin_), std::move(_end_)); + } + else + { + auto mid = _begin_ + n / 2; + auto left = parallel_sort_t{}(_sch_, _begin_, mid, _sort_fun_); + auto right = parallel_sort_t{}(_sch_, mid, _end_, _sort_fun_); + + std::function fun = [begin = _begin_, mid, end = _end_, rcvr = std::move(_rcvr)]() mutable + { + std::inplace_merge(begin, mid, end); + ustdex::set_value(std::move(rcvr), begin, end); + }; + auto task = ustdex::when_all(std::move(left), std::move(right)) | ustdex::then(exec_then{ std::move(fun) }); + + ustdex::start_detached(std::move(task)); + } + } + else + { + _SetTag()(static_cast<_Receiver&&>(_rcvr), static_cast<_Args&&>(_args)...); + } + }; + } complete; +}; + +} + +namespace ustdex +{ + template <> + struct __sexpr_impl : __impl {}; +} + +inline constexpr parallel_sort_t parallel_sort; +#else +struct parallel_sort_t +{ +private: + struct exec_then + { + template + void operator()(Args&&... args) + { + _fun_(); + } + std::function _fun_; + }; + + template + struct _opstate_t + { + using operation_state_concept = ustdex::operation_state_t; + using _env_t = ustdex::env_of_t; + + _opstate_t(Rcvr&& rcvr, Sch sch, RandomAccessIterator begin, RandomAccessIterator end, SortFun sort_fun) + : _rcvr_{ std::move(rcvr) }, _sch_{ sch }, _begin_{ begin }, _end_{ end }, _sort_fun_{ sort_fun } + , _opstate_(ustdex::connect(ustdex::schedule(_sch_), ustdex::_rcvr_ref{ *this })) + {} + void start() noexcept + { + ustdex::start(_opstate_); + } + void set_value() noexcept; + + void set_error(std::exception_ptr) noexcept + { + ustdex::set_error(std::move(_rcvr_), std::current_exception()); + } + void set_stopped() noexcept + { + ustdex::set_stopped(std::move(_rcvr_)); + } + auto get_env() const noexcept -> _env_t + { + return ustdex::get_env(_rcvr_); + } + + Rcvr _rcvr_; + Sch _sch_; + RandomAccessIterator _begin_; + RandomAccessIterator _end_; + SortFun _sort_fun_; + ustdex::connect_result_t, ustdex::_rcvr_ref<_opstate_t, _env_t>> _opstate_; + }; + + template + struct _sndr_t + { + using sender_concept = ustdex::sender_t; + + template + static constexpr auto get_completion_signatures() noexcept + { + return ustdex::completion_signatures(); + } + template + auto connect(Rcvr&& rcvr) && noexcept(ustdex::_nothrow_decay_copyable) + -> _opstate_t + { + return _opstate_t( + std::move(rcvr), std::move(_sch_), _begin_, _end_, std::move(_sort_fun_)); + } + + template + auto connect(Rcvr&& rcvr) const& noexcept(ustdex::_nothrow_decay_copyable) + -> _opstate_t + { + return _opstate_t( + std::move(rcvr), _sch_, _begin_, _end_, _sort_fun_); + } + + Sch _sch_; + RandomAccessIterator _begin_; + RandomAccessIterator _end_; + SortFun _sort_fun_; + }; +public: + template + auto operator()(Sch sch, RandomAccessIterator begin, RandomAccessIterator end, SortFun sort_fun) const noexcept -> _sndr_t + { + return _sndr_t{std::move(sch), begin, end, std::move(sort_fun)}; + } +}; + +inline constexpr parallel_sort_t parallel_sort{}; + +template +inline void parallel_sort_t::_opstate_t::set_value() noexcept +{ + auto n = _end_ - _begin_; + if (n <= 16384) + { + std::sort(_begin_, _end_, _sort_fun_); + ustdex::set_value(std::move(_rcvr_), std::move(_begin_), std::move(_end_)); + } + else + { + auto mid = _begin_ + n / 2; + auto left = parallel_sort(_sch_, _begin_, mid, _sort_fun_); + auto right = parallel_sort(_sch_, mid, _end_, _sort_fun_); + + std::function fun = [begin = _begin_, mid, end = _end_, rcvr = std::move(_rcvr_)]() mutable + { + std::inplace_merge(begin, mid, end); + ustdex::set_value(std::move(rcvr), begin, end); + }; + auto task = ustdex::when_all(std::move(left), std::move(right)) | ustdex::then(exec_then{ std::move(fun) }); + + ustdex::start_detached(std::move(task)); + } +} +#endif // use_basic_sender diff --git a/examples/scratch.cpp b/examples/scratch.cpp index 46d5da4..1aecb34 100644 --- a/examples/scratch.cpp +++ b/examples/scratch.cpp @@ -19,87 +19,273 @@ #include #include "ustdex/ustdex.hpp" +#include +#include +#include +#include + #include "parallel_sort.hpp" + //#include "asio_thread_pool.hpp" +#include "ustdex/detail/basic_sender.hpp" -using namespace ustdex; struct sink { - using receiver_concept = receiver_t; + using receiver_concept = ustdex::receiver_t; + + void set_value() noexcept {} - void set_value() noexcept {} + void set_value(int a) noexcept + { + std::printf("%d\n", a); + } - void set_value(int a) noexcept - { - std::printf("%d\n", a); - } + template + void set_value(As&&...) noexcept + { + std::puts("In sink::set_value(auto&&...)"); + } - template - void set_value(As&&...) noexcept - { - std::puts("In sink::set_value(auto&&...)"); - } + void set_error(std::exception_ptr) noexcept {} - void set_error(std::exception_ptr) noexcept {} + void set_stopped() noexcept {} - void set_stopped() noexcept {} + [[nodiscard]] + ustdex::prop get_env() const noexcept + { + static ustdex::inplace_stop_source _stop_source_; + return ustdex::prop{ ustdex::get_stop_token, _stop_source_.get_token() }; + } }; template [[deprecated]] void print() {} -static_assert(dependent_sender); +using namespace ustdex; int main() { - thread_context ctx; - auto sch = ctx.get_scheduler(); - - auto work = just(1, 2, 3) // - | then([](int a, int b, int c) { - std::printf("%d %d %d\n", a, b, c); - return a + b + c; - }); - auto s = starts_on(sch, std::move(work)); - static_assert(!dependent_sender); - std::puts("Hello, world!"); - sync_wait(s); - - auto s3 = just(42) | let_value([](int a) { - std::puts("here"); - return just(a + 1); - }); - sync_wait(s3); - - auto [sch2] = sync_wait(read_env(get_scheduler)).value(); - - auto [i1, i2] = sync_wait(when_all(just(42), just(43))).value(); - std::cout << i1 << ' ' << i2 << '\n'; - - auto s4 = just(42) | then([](int) {}) | upon_error([](auto) { /*return 42;*/ }); - auto s5 = when_all(std::move(s4), just(42, 43), just(+"hello")); - auto [i, j, k] = sync_wait(std::move(s5)).value(); - std::cout << i << ' ' << j << ' ' << k << '\n'; - - auto s6 = sequence(just(42) | then([](int) { - std::cout << "sequence sender 1\n"; - }), - just(42) | then([](int) { - std::cout << "sequence sender 2\n"; - })); - sync_wait(std::move(s6)); - - auto s7 = - just(42) - | conditional( - [](int i) { - return i % 2 == 0; - }, - then([](int) { - std::cout << "even\n"; - }), - then([](int) { - std::cout << "odd\n"; - })); - sync_wait(std::move(s7)); + ustdex::thread_context tp; + using MT = decltype(tp.get_scheduler()); + auto task = schedule(tp.get_scheduler()) | let_value([] { + /*return read_env(get_scheduler) | then([](auto&& sched) { + static_assert(std::is_same_v < std::decay_t, std::decay_t>); + });*/ + + auto sched_sender = read_env(get_scheduler); + auto value_sender = just(42); + return when_all(std::move(sched_sender), std::move(value_sender)) // <-- use a when_all here + | then([](auto&& sched, int value) { /*io_uring_socket.async_send(value, on=sched)...*/ }); + }); + +#if 0 + std::cout << "main: " << std::this_thread::get_id() << std::endl; + + + ustdex::static_thread_pool thread_pool_b{}; + auto v1 = test_sender(thread_pool_b.get_scheduler(), 2, 3, 4); + + auto op = ustdex::connect(v1, sink{}); + + ustdex::start(op); + + while (true); + +#endif + /*auto pp1 = ustdex::_make_sexpr(3, 4, 5); + _whatis();*/ + + int a = 0; + +#if 0 + //asio::static_thread_pool thread_pool_a{}; + ustdex::static_thread_pool thread_pool_b{}; + std::vector values(100'000'000);// + std::random_device random_device; + std::mt19937 rng(random_device()); + std::uniform_int_distribution dist(1, 1'000'000); + std::generate(values.begin(), values.end(), [&] { return dist(rng); }); + + std::cout << "starting sort\n"; + + auto begin = std::chrono::high_resolution_clock::now(); + //thread_pool_b.get_scheduler() + /*auto ps = parallel_sort(atp::asio_scheduler{thread_pool_a.get_executor()}, values.begin(), values.end(), [](const int& a, const int& b) + { + return a < b; + });*/ + + auto ps = parallel_sort(thread_pool_b.get_scheduler(), values.begin(), values.end(), [](const int& a, const int& b) + { + return a < b; + }); + auto op = ustdex::connect(ps, sink{}); + ustdex::sync_wait(std::move(ps)); + + + auto end = std::chrono::high_resolution_clock::now(); + + auto duration = end - begin; + std::cout << "sort took " + << std::chrono::duration_cast(duration).count() + << " microseconds\n"; + + return 0; + +#endif + +#if 0 + auto task = read_env(get_stop_token) + | then([](auto stop_token) + { + std::cout << "Stop token: " << stop_token.stop_requested() << '\n'; + return 42; + }) + | then([](int i) + { + std::cout << "Value: " << i << '\n'; + return i + 1; + }); + + auto task2 = when_all(task); + + //using TT = completion_signatures_of_t; + + //_whatis(); + + auto op = connect(task2, sink{}); + + start(op); + + //auto [sch2] = sync_wait(read_env(get_scheduler)).value(); +#endif + +#if 0 + thread_context ctx; + auto sch = ctx.get_scheduler(); + + auto work = just(1, 2, 3) // + | then([](int a, int b, int c) + { + std::printf("%d %d %d\n", a, b, c); + return a + b + c; + }); + auto s = starts_on(sch, std::move(work)); + static_assert(!dependent_sender); + std::puts("Hello, world!"); + sync_wait(s); + + auto s3 = just(42) | let_value([](int a) + { + std::puts("here"); + return just(a + 1); + }); + sync_wait(s3); + + auto [sch2] = sync_wait(read_env(get_scheduler)).value(); + + auto [i1, i2] = sync_wait(when_all(just(42), just(43))).value(); + std::cout << i1 << ' ' << i2 << '\n'; + + auto s4 = just(42) | then([](int) {}) | upon_error([](auto) { /*return 42;*/ }); + auto s5 = when_all(std::move(s4), just(42, 43), just(+"hello")); + auto [i, j, k] = sync_wait(std::move(s5)).value(); + std::cout << i << ' ' << j << ' ' << k << '\n'; + + auto s6 = sequence(just(42) | then([](int) + { + std::cout << "sequence sender 1\n"; + }), + just(42) | then([](int) + { + std::cout << "sequence sender 2\n"; + })); + sync_wait(std::move(s6)); + + auto s7 = + just(42) + | conditional( + [](int i) + { + return i % 2 == 0; + }, + then([](int) + { + std::cout << "even\n"; + }), + then([](int) + { + std::cout << "odd\n"; + })); + sync_wait(std::move(s7)); + + + { + inplace_stop_source stop_source; + + auto task = just(100) + | then([&stop_source](int i) + { + stop_source.request_stop(); + std::cout << "111_Value: " << i << '\n'; + return i; + }) + //| stop_when(stop_source.get_token()) + | stop_with([](const int& i) + { + if (i == 100) + { + return false; + } + else + { + return false; + } + }) + | then([](int i) + { + std::cout << "222_Value: " << i << '\n'; + return i; + }) + | upon_stopped([]() + { + std::cout << "Stopped!\n"; + }) + | upon_error([](std::exception_ptr e) + { + try + { + std::rethrow_exception(e); + } + catch (const std::exception& ex) + { + std::cout << "Error: " << ex.what() << '\n'; + } + }); + + auto op = connect(std::move(task), sink{}); + start(op); + //sync_wait(std::move(task)); + + } + + { + auto task = when_any(just(5), just('a'), just(3.14)) | + then([](std::any v) + { + int aa = 0; + }); + + auto op = connect(std::move(task), sink{}); + start(op); + + //using TA = completion_signatures_of_t; + //_m_self_or<_nil>::call + //using TB = _value_types; + + //_whatis(); + } + + +#endif } diff --git a/include/ustdex/detail/basic_sender.hpp b/include/ustdex/detail/basic_sender.hpp new file mode 100644 index 0000000..61212fd --- /dev/null +++ b/include/ustdex/detail/basic_sender.hpp @@ -0,0 +1,531 @@ +#pragma once + +#include "completion_signatures.hpp" +#include "config.hpp" +#include "cpos.hpp" +#include "tuple.hpp" +#include "utility.hpp" + +#include "prologue.hpp" + +namespace ustdex +{ + template + struct __sexpr_impl; + + template + struct __op_state; + + template + struct __rcvr; + + namespace detail + { + struct __ {}; + + template + using __t = typename _Tp::__t; + + template + inline constexpr bool __mnever = false; + + //! Metafunction selects the first of two type arguments. + template + using __mfirst = _Tp; + + //! Metafunction selects the second of two type arguments. + template + using __msecond = _Up; + + // A type that describes a sender's metadata + template + struct __desc + { + using __tag = _Tag; + using __data = _Data; + using __children = _m_list<_Child...>; + + template + using call = _m_call<_Fn, _Tag, _Data, _Child...>; + }; + + template + using __impl_of = decltype((std::declval<_Sender>().__impl_)); + + // Accessor for the "data" field of a sender + struct __get_data + { + template + //USTDEX_ATTR_ALWAYS_INLINE + auto operator()(_ignore, _Data&& __data, _Child&&...) const noexcept -> _Data&& + { + return static_cast<_Data&&>(__data); + } + }; + + // A function object that is to senders what std::apply is to tuples: + struct __sexpr_apply_t + { + template + USTDEX_ATTR_ALWAYS_INLINE + auto operator()(_Sender&& __sndr, _ApplyFn&& __fun) const + noexcept(noexcept(__sndr + .apply(static_cast<_Sender&&>(__sndr), static_cast<_ApplyFn&&>(__fun)))) + -> decltype(__sndr + .apply(static_cast<_Sender&&>(__sndr), static_cast<_ApplyFn&&>(__fun))) + { + return __sndr.apply(static_cast<_Sender&&>(__sndr), static_cast<_ApplyFn&&>(__fun)); + } + }; + + template + struct __sexpr_uncurry_fn + { + template + constexpr auto operator()(_Tag, _Data&&, _Child&&...) const noexcept + -> _m_call<_Fn, _Tag, _Data, _Child...>; + }; + + template + using __sexpr_uncurry = _call_result_t<__sexpr_apply_t, _CvrefSender, __sexpr_uncurry_fn<_Fn>>; + + template + using __desc_of = __sexpr_uncurry<_Sender, _m_quote<__desc>>; + + template + struct __connect_fn + { + template + using __receiver_t = __t<__rcvr<_Receiver, _Sexpr, _Idx>>; + + __op_state<_Sexpr, _Receiver>* __op_; + + struct __impl + { + __op_state<_Sexpr, _Receiver>* __op_; + + template + auto operator()(std::index_sequence<_Is...>, _Child&&... __child) const + noexcept((_nothrow_callable> && ...)) + -> _tupl_for>...> + { + return _tupl{ ustdex::connect(static_cast<_Child&&>(__child), __receiver_t<_Is>{__op_})... }; + } + }; + + template + auto operator()(_ignore, _ignore, _Child&&... __child) const + noexcept(_nothrow_callable<__impl, std::index_sequence_for<_Child...>, _Child...>) + -> _call_result_t<__impl, std::index_sequence_for<_Child...>, _Child...> + { + return __impl{ __op_ }(std::index_sequence_for<_Child...>(), static_cast<_Child&&>(__child)...); + } + + auto operator()(_ignore, _ignore) const noexcept -> _tuple<> + { + return {}; + } + }; + + template + using __state_type_t = + std::decay_t::get_state(std::declval<_Sexpr>(), std::declval<_Receiver&>()))>; + + template + using __env_type_t = decltype( + __sexpr_impl<_Tag>::get_env(std::declval<_Index>(), std::declval<__state_type_t<_Tag, _Sexpr, _Receiver>&>(), std::declval<_Receiver&>()) + ); + + template + struct __drop_front_impl + { + template + auto operator()(_ignore, _Rest&&... __rest) const noexcept( + _nothrow_callable) -> _call_result_t + { + return __fn(static_cast<_Rest&&>(__rest)...); + }; + + _Fn __fn; + }; + + template + inline constexpr __drop_front_impl<_Fn> __drop_front(_Fn __fn) noexcept + { + return __drop_front_impl<_Fn>{std::move(__fn)}; + }; + + template + struct __captures_impl + { + template + auto operator()(_Cvref a, _Fun&& __fun) noexcept(_nothrow_callable<_Fun, _Tag, _m_call<_Cvref, _Captures>...>) + -> _call_result_t<_Fun, _Tag, _m_call<_Cvref, _Captures>...> + { + return __captures3_tup.apply([__fun = std::move(__fun)](_Captures&... __captures3) mutable + -> _call_result_t<_Fun, _Tag, _m_call<_Cvref, _Captures>...> + { + return static_cast<_Fun&&>(__fun)(_Tag(), const_cast<_m_call<_Cvref, _Captures>&&>(__captures3)...); + }, __captures3_tup); + } + _tuple<_Captures...> __captures3_tup; + }; + + template + constexpr auto __captures(_Tag, _Captures&&... __captures2) + { + return __captures_impl<_Tag, _Captures...>{_tuple<_Captures...>{std::move(__captures2)...}}; + } + + template + using __captures_t = decltype(__captures(_Tag(), std::declval<_Data>(), std::declval<_Child>()...)); + + template + struct __op_base : _immovable + { + using __tag_t = typename std::decay_t<_Sexpr>::__tag_t; + using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>; + + USTDEX_NO_UNIQUE_ADDRESS + _Receiver __rcvr_; + USTDEX_NO_UNIQUE_ADDRESS + __state_t __state_; + + __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) noexcept( + _nothrow_decay_copyable<_Receiver> + && _nothrow_callable::get_state), _Sexpr, _Receiver&>) + : __rcvr_(static_cast<_Receiver&&>(__rcvr)) + , __state_(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr), __rcvr_)) + { + } + + USTDEX_ATTR_ALWAYS_INLINE auto __state() & noexcept -> __state_t& + { + return __state_; + } + + USTDEX_ATTR_ALWAYS_INLINE auto __state() const& noexcept -> const __state_t& + { + return __state_; + } + + USTDEX_ATTR_ALWAYS_INLINE auto __rcvr() & noexcept -> _Receiver& + { + return __rcvr_; + } + + USTDEX_ATTR_ALWAYS_INLINE auto __rcvr() const& noexcept -> const _Receiver& + { + return __rcvr_; + } + }; + + struct __defaults + { + static constexpr struct + { + template + auto operator()(_ignore, const _Child&... __child) const noexcept -> decltype(auto) + { + if constexpr (sizeof...(__child) == 1) + { + return ustdex::get_env(__child...); + } + else + { + return env<>(); + } + } + } get_attrs; + + static constexpr struct + { + template + auto operator()(_ignore, _ignore, const _Receiver& __rcvr) const noexcept + -> env_of_t + { + return ustdex::get_env(__rcvr); + }; + + } get_env; + + static constexpr struct + { + template + auto operator()(_Sender&& __sndr, _ignore) const noexcept -> decltype(auto) + { + return __sndr.apply(static_cast<_Sender&&>(__sndr), __get_data()); + }; + + } get_state; + + static constexpr struct + { + //requires __connectable<_Sender, _Receiver> + template + auto operator()(_Sender&& __sndr, _Receiver __rcvr) const noexcept( + _nothrow_constructible<__op_state<_Sender, _Receiver>, _Sender, _Receiver>) + -> __op_state<_Sender, _Receiver> + { + return __op_state<_Sender, _Receiver>{ + static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr)}; + }; + } connect; + + static constexpr struct + { + template + auto operator()( + _ignore, + _ignore, + _ChildOps&... __ops) const noexcept + { + (_StartTag()(__ops), ...); + }; + } start; + + static constexpr struct + { + template + auto operator()( + _Index, + _ignore, + _Receiver& __rcvr, + _SetTag, + _Args&&... __args) const noexcept + { + static_assert(_Index::value == 0, "I don't know how to complete this operation."); + _SetTag()(std::move(__rcvr), static_cast<_Args&&>(__args)...); + }; + } complete; + + static constexpr struct + { + template + void operator()(_Sender&&, _Args&&...) const noexcept + { + static_assert( + __mnever < tag_of_t<_Sender>> + "No customization of get_completion_signatures for this sender tag type."); + }; + } get_completion_signatures; + }; + } + + using detail::__sexpr_apply_t; + inline constexpr __sexpr_apply_t __sexpr_apply{}; + + template + using tag_of_t = typename detail::__desc_of<_Sender>::__tag; + + template + using data_of = typename detail::__desc_of<_Sender>::__data; + + template > + using children_of = _m_apply<_Continuation, typename detail::__desc_of<_Sender>::__children>; + + template + using __descriptor_fn_t = _fn_t>*; + + using __sexpr_defaults = detail::__defaults; + + template + struct __op_state : detail::__op_base<_Sexpr, _Receiver> + { + using __desc_t = typename std::decay_t<_Sexpr>::__desc_t; + using __tag_t = typename __desc_t::__tag; + using __data_t = typename __desc_t::__data; + using __state_t = typename __op_state::__state_t; + using __inner_ops_t = + _call_result_t>; + + __inner_ops_t __inner_ops_; + + __op_state(_Sexpr&& __sexpr, _Receiver __rcvr) noexcept( + _nothrow_constructible, _Sexpr, _Receiver> + && _nothrow_callable>) + : __op_state::__op_base{ static_cast<_Sexpr&&>(__sexpr), static_cast<_Receiver&&>(__rcvr) } + , __inner_ops_(__sexpr_apply( + static_cast<_Sexpr&&>(__sexpr), + detail::__connect_fn<_Sexpr, _Receiver>{this})) + {} + + USTDEX_ATTR_ALWAYS_INLINE void start() & noexcept + { + auto&& __rcvr = this->__rcvr(); + __inner_ops_.apply( + [&](auto&... __ops) noexcept + { + __sexpr_impl<__tag_t>::start(this->__state(), __rcvr, __ops...); + }, + __inner_ops_); + } + + template + USTDEX_ATTR_ALWAYS_INLINE + void __complete(_Index, _Tag2, _Args&&... __args) noexcept + { + using __tag_t = typename __op_state::__tag_t; + auto&& __rcvr = this->__rcvr(); + __sexpr_impl<__tag_t>::complete( + _Index(), this->__state(), __rcvr, _Tag2(), static_cast<_Args&&>(__args)...); + } + + template + USTDEX_ATTR_ALWAYS_INLINE + auto __get_env(_Index) const noexcept + -> detail::__env_type_t<_Index, __tag_t, _Index, _Sexpr, _Receiver> + { + const auto& __rcvr = this->__rcvr(); + return __sexpr_impl<__tag_t>::get_env(_Index(), this->__state(), __rcvr); + } + }; + + template + struct __rcvr + { + struct __t + { + using receiver_concept = receiver_t; + using __id = __rcvr; + + using __index = _m_size_t<_Idx>; + using __parent_op_t = __op_state<_Sexpr, _Receiver>; + using __tag_t = tag_of_t<_Sexpr>; + + // A pointer to the parent operation state, which contains the one created with + // this receiver. + __parent_op_t* __op_; + + template + USTDEX_ATTR_ALWAYS_INLINE + void set_value(_Args&&... __args) noexcept + { + __op_->__complete(__index(), ustdex::set_value, static_cast<_Args&&>(__args)...); + } + + template + USTDEX_ATTR_ALWAYS_INLINE + void set_error(_Error&& __err) noexcept + { + __op_->__complete(__index(), ustdex::set_error, static_cast<_Error&&>(__err)); + } + + USTDEX_ATTR_ALWAYS_INLINE void set_stopped() noexcept + { + __op_->__complete(__index(), ustdex::set_stopped); + } + + template >> + USTDEX_ATTR_ALWAYS_INLINE + auto get_env() const noexcept + -> detail::__env_type_t<_Self, __tag_t, __index, _Sexpr, _Receiver> + { + return __op_->__get_env(__index()); + } + }; + }; + + template + struct __sexpr_impl : detail::__defaults + { + using not_specialized = void; + }; + + template + using __get_attrs_fn = + decltype(detail::__drop_front(__sexpr_impl<_Tag>::get_attrs)); + + namespace + { + //! A struct template to aid in creating senders. + //! This struct closely resembles P2300's [_`basic-sender`_](https://eel.is/c++draft/exec#snd.expos-24), + //! but is not an exact implementation. + //! Note: The struct named `__basic_sender` is just a dummy type and is also not _`basic-sender`_. + template + struct __sexpr + { + using sender_concept = sender_t; + + using __desc_t = decltype(std::declval<_DescriptorFn>()()); + using __tag_t = typename __desc_t::__tag; + using __captures_t = _m_call<__desc_t, _m_quote>; + + mutable __captures_t __impl_; + + template + explicit __sexpr(_Tag, _Data&& __data, _Child&&... __child) + : __impl_( + detail::__captures( + _Tag(), + static_cast<_Data&&>(__data), + static_cast<_Child&&>(__child)...)) + {} + + using __impl = __sexpr_impl<__tag_t>; + using _Self_t = __sexpr; + + template + USTDEX_ATTR_ALWAYS_INLINE + auto get_env() const noexcept + -> __result_of<__sexpr_apply, const _Self&, __get_attrs_fn<__tag_t>> + { + return __sexpr_apply(*this, detail::__drop_front(__impl::get_attrs)); + } + + template + USTDEX_ATTR_ALWAYS_INLINE + static constexpr auto get_completion_signatures() noexcept -> + __result_of<__impl::get_completion_signatures, _Self, _Env...> + { + return {}; + } + + // BUGBUG fix receiver constraint here: + template + USTDEX_ATTR_ALWAYS_INLINE + auto connect(_Receiver&& __rcvr) + noexcept(_nothrow_callable) -> + __result_of<__impl::connect, _Self_t, _Receiver> + { + return __impl::connect( + static_cast<_Self_t&&>(*this), static_cast<_Receiver&&>(__rcvr)); + } + + template + USTDEX_ATTR_ALWAYS_INLINE + static auto apply(_Sender&& __sndr, _ApplyFn&& __fun) noexcept( + _nothrow_callable, _copy_cvref_fn<_Sender>, _ApplyFn>) + -> _call_result_t, _copy_cvref_fn<_Sender>, _ApplyFn> + { + return static_cast<_Sender&&>(__sndr) + .__impl_(_copy_cvref_fn<_Sender>(), static_cast<_ApplyFn&&>(__fun)); + } + }; + + template + __sexpr(_Tag, _Data, _Child...) -> __sexpr<__descriptor_fn_t<_Tag, _Data, _Child...>>; + } // namespace + + template + using _sexpr_t = __sexpr<__descriptor_fn_t<_Tag, _Data, _Child...>>; + + namespace detail + { + template + struct _make_sexpr_t + { + template + constexpr auto operator()(_Data _data = {}, _Child... _child) const + { + return _sexpr_t<_Tag, _Data, _Child...>{ + _Tag(), static_cast<_Data&&>(_data), static_cast<_Child&&>(_child)...}; + } + }; + } // namespace __detail + + template + inline constexpr detail::_make_sexpr_t<_Tag> _make_sexpr{}; +} + +#include "epilogue.hpp" \ No newline at end of file diff --git a/include/ustdex/detail/binder_closure.hpp b/include/ustdex/detail/binder_closure.hpp new file mode 100644 index 0000000..e921923 --- /dev/null +++ b/include/ustdex/detail/binder_closure.hpp @@ -0,0 +1,43 @@ +#ifndef USTDEX_ASYNC_DETAIL_BINDER_CLOSURE +#define USTDEX_ASYNC_DETAIL_BINDER_CLOSURE + +#include "config.hpp" +#include "tuple.hpp" + +#include "prologue.hpp" + +namespace ustdex +{ + + template + struct binder_closure + { + _tuple _args_; + + binder_closure(Args... args) noexcept : _args_({ static_cast(args)... }) + {} + + template + static auto make(Sndr&& sndr, _tuple _args, std::index_sequence) noexcept ->_call_result_t + { + return bind_t()(static_cast(sndr), static_cast(const_cast(_cget(_args)))...); + } + + template + USTDEX_TRIVIAL_API auto operator()(Sndr _sndr) noexcept ->_call_result_t + { + return this->make(static_cast(_sndr), static_cast<_tuple&&>(_args_), std::make_index_sequence()); + } + + template + USTDEX_TRIVIAL_API friend auto operator|(Sndr sndr, binder_closure self) noexcept ->_call_result_t + { + return self.make(static_cast(sndr), static_cast<_tuple&&>(self._args_), std::make_index_sequence()); + } + }; + +} + +#include "epilogue.hpp" + +#endif // USTDEX_ASYNC_DETAIL_BINDER_CLOSURE \ No newline at end of file diff --git a/include/ustdex/detail/completion_signatures.hpp b/include/ustdex/detail/completion_signatures.hpp index eeaf109..abb1543 100644 --- a/include/ustdex/detail/completion_signatures.hpp +++ b/include/ustdex/detail/completion_signatures.hpp @@ -169,15 +169,15 @@ USTDEX_API constexpr auto completion_signatures::select(Tag) const noex { if constexpr (Tag() == set_value) { - return _partitioned::template _value_types<_m_quote_f::call, completion_signatures>(); + return typename _partitioned::template _value_types<_m_quote_f::call, completion_signatures>(); } else if constexpr (Tag() == set_error) { - return _partitioned::template _error_types::call>(); + return typename _partitioned::template _error_types::call>(); } else { - return _partitioned::template _stopped_types(); + return typename _partitioned::template _stopped_types(); } } diff --git a/include/ustdex/detail/conditional.hpp b/include/ustdex/detail/conditional.hpp index c7ece94..7d878a8 100644 --- a/include/ustdex/detail/conditional.hpp +++ b/include/ustdex/detail/conditional.hpp @@ -211,6 +211,8 @@ struct _cond_t template struct USTDEX_TYPE_VISIBILITY_DEFAULT _cond_t::_sndr_t { + using sender_concept = sender_t; + using _data_t = _cond_t::_data; _cond_t _tag_; _data_t _data_; @@ -236,7 +238,7 @@ struct USTDEX_TYPE_VISIBILITY_DEFAULT _cond_t::_sndr_t template USTDEX_API auto connect(Rcvr _rcvr) const& -> _opstate { - return {_sndr_, static_cast(_rcvr), static_cast<_data_t&&>(_data_)}; + return {_sndr_, static_cast(_rcvr), _data_}; } USTDEX_API env_of_t get_env() const noexcept diff --git a/include/ustdex/detail/config.hpp b/include/ustdex/detail/config.hpp index 65c060f..c7ccfe2 100644 --- a/include/ustdex/detail/config.hpp +++ b/include/ustdex/detail/config.hpp @@ -366,3 +366,6 @@ using _remove_reference_t = __remove_reference_t(Ty); namespace ustdex { } + +template +struct _whatis; diff --git a/include/ustdex/detail/exclusive_scan.hpp b/include/ustdex/detail/exclusive_scan.hpp index fbdb26b..48632d9 100644 --- a/include/ustdex/detail/exclusive_scan.hpp +++ b/include/ustdex/detail/exclusive_scan.hpp @@ -24,7 +24,7 @@ namespace ustdex { -#if __cplusplus >= 202002L +#if __cplusplus >= 202302L // exclusive_scan is constexpr in C++20 using std::exclusive_scan; #else diff --git a/include/ustdex/detail/lazy.hpp b/include/ustdex/detail/lazy.hpp index 7c11c80..eb254b0 100644 --- a/include/ustdex/detail/lazy.hpp +++ b/include/ustdex/detail/lazy.hpp @@ -38,16 +38,16 @@ struct _lazy template USTDEX_API Ty& construct(Ts&&... _ts) noexcept(_nothrow_constructible) { - Ty* _value_ = ::new (static_cast(std::addressof(_value_))) Ty{static_cast(_ts)...}; - return *std::launder(_value_); + Ty* rst = ::new (static_cast(std::addressof(_value_))) Ty{static_cast(_ts)...}; + return *std::launder(rst); } template USTDEX_API Ty& construct_from(Fn&& _fn, Ts&&... _ts) noexcept(_nothrow_callable) { - Ty* _value_ = ::new (static_cast(std::addressof(_value_))) + Ty* rst = ::new (static_cast(std::addressof(_value_))) Ty{static_cast(_fn)(static_cast(_ts)...)}; - return *std::launder(_value_); + return *std::launder(rst); } USTDEX_API void destroy() noexcept diff --git a/include/ustdex/detail/let_value.hpp b/include/ustdex/detail/let_value.hpp index 4dbfb8b..8d7d9c0 100644 --- a/include/ustdex/detail/let_value.hpp +++ b/include/ustdex/detail/let_value.hpp @@ -230,19 +230,21 @@ struct _let template USTDEX_API static constexpr auto get_completion_signatures() { + using SndrEnv = env_of_t; + using Env_t = env; USTDEX_LET_COMPLETIONS(auto(_child_completions) = get_child_completion_signatures()) { if constexpr (Disposition == _disposition_t::_value) { - return transform_completion_signatures(_child_completions, _transform_args_fn{}); + return transform_completion_signatures(_child_completions, _transform_args_fn{}); } else if constexpr (Disposition == _disposition_t::_error) { - return transform_completion_signatures(_child_completions, {}, _transform_args_fn{}); + return transform_completion_signatures(_child_completions, {}, _transform_args_fn{}); } else { - return transform_completion_signatures(_child_completions, {}, {}, _transform_args_fn{}); + return transform_completion_signatures(_child_completions, {}, {}, _transform_args_fn{}); } } } diff --git a/include/ustdex/detail/meta.hpp b/include/ustdex/detail/meta.hpp index 456be3f..35b704f 100644 --- a/include/ustdex/detail/meta.hpp +++ b/include/ustdex/detail/meta.hpp @@ -68,20 +68,6 @@ struct _m_size using call USTDEX_ATTR_NODEBUG_ALIAS = _m_size_t; }; -template