Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Turn class templates into a member classes of class templates to further narrow scope of ADL #83

Merged
merged 56 commits into from Mar 13, 2020
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
087e34c
Modify just() to use better ADL insulation techniquesPut sender/opera…
Mar 4, 2020
74e5ced
Modify transform() to use better ADL insulation techniques.
Mar 4, 2020
ce583d2
Modify finally() to use better ADL insuation techniques.
Mar 4, 2020
e1bd2ba
rework transform adl-isolation
Mar 4, 2020
a972eca
turn transform into a customizeable algorithm
Mar 4, 2020
efc6311
adl-isolate allocate_[sender|operation]
Mar 5, 2020
ad2f0db
ADL-isolate awaitable_sender
Mar 5, 2020
e56186a
ADL-insulate dematerialize
Mar 5, 2020
2d602fc
consistency fixes
Mar 5, 2020
47bdaea
ADL-isolate unifex::let
Mar 5, 2020
0eb2f74
ADL-insulate unifex::materialize
Mar 5, 2020
d530192
ADL insulate unifex::sequence
Mar 6, 2020
e2a042e
ADL-insulate unifex::via
Mar 6, 2020
50fab33
ADL-insulate when_all
Mar 6, 2020
bb40421
ADL-insulate unifex::retry_when
Mar 6, 2020
4293adb
avoid gcc warning
Mar 6, 2020
d26067c
Merge remote-tracking branch 'origin/master' into adl_isolation
Mar 6, 2020
e343681
ADL-isolate unifex::reduce_stream
Mar 6, 2020
b394049
ADL-isolate indexed_for
Mar 6, 2020
dd1f492
ADL-isolate scheduler CPOs
Mar 6, 2020
af45c87
ADL-isolate types in thread_unsafe_event_loop
Mar 6, 2020
31ea3a2
ADL-isolate types in timed_single_thread_context
Mar 6, 2020
a7f4280
ADL-isolate unifex::with_query_value
Mar 6, 2020
6ae5224
review feedback
Mar 6, 2020
0535c26
Merge remote-tracking branch 'origin/master' into adl_isolation
Mar 6, 2020
917ffd2
ADL-isolate adapt_stream
Mar 6, 2020
fc40572
ADL-isolate any_unique; don't deduce noexcept from function type, it'…
Mar 7, 2020
d45a7e9
ADL-isolate async_mutex and async_trace
Mar 7, 2020
8f8f66c
ADL-isolate unifex::blocking
Mar 8, 2020
d74ed04
Merge remote-tracking branch 'origin/master' into adl_isolation
Mar 8, 2020
59e55f8
Simplify vtable_entry definition in any_unique.hpp
Mar 8, 2020
a437a66
Fix constexpr build issue with continuation_info.
Feb 8, 2020
c14bbca
Work around msvc eagerness to instantiate tag_invoke overloads in fin…
Mar 9, 2020
8d9e22d
Work around msvc eagerness to instantiate tag_invoke overloads in seq…
Mar 9, 2020
99d3b16
ADL-isolate receiver types used by sync_wait() algorithm.
Mar 9, 2020
0ee8593
Work around msvc eagerness to instantiate tag_invoke overloads in tra…
Mar 9, 2020
ec18cb2
Work around msvc eagerness to instantiate tag_invoke overloads in whe…
Mar 9, 2020
178c5a2
Further constrain forwarding tag_invoke() to require is_invocable<CPO…
Mar 9, 2020
8cdff10
Add msvc workarounds for materialize/dematerialize algorithms.
Mar 9, 2020
3358fd3
Add macros to centalise MSVC-specific workarounds for over eager temp…
Mar 10, 2020
5a5a029
Fix typo in when_all().
Mar 10, 2020
968a089
various tweaks to make clang happy and to disable ADL
Mar 10, 2020
6352bcf
more ADL-isolation
ericniebler Mar 10, 2020
fdbf2f1
complex-ify the receiver CPOs to not add default tag_invoke overloads
ericniebler Mar 10, 2020
45e1bea
complex-ify the sender and subschedule CPOs to not add default tag_in…
ericniebler Mar 11, 2020
35cfdd3
more ADL isolation
Mar 11, 2020
b307eff
more ADL isolation
Mar 11, 2020
0efaa86
more ADL isolation
Mar 12, 2020
e615084
more ADL isolation
Mar 12, 2020
34dbdda
bring type_erased_stream back from the dead. Add an example.
Mar 12, 2020
c773f75
ADL-isolate type-erased stream
Mar 12, 2020
8d6006e
Update manual_event_loop.cpp in sync with changes to header.
Mar 13, 2020
6b87ce9
Fix syntax error in UNIFEX_DECLARE_NON_DEDUCED_TYPE macro on msvc.
Mar 13, 2020
f789f85
Rename_inline namespace to _inline_sched.
Mar 13, 2020
1536f07
Fix is_stop_never_possible_v under msvc.
Mar 13, 2020
195a864
review feedback
Mar 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/async_mutex.cpp
Expand Up @@ -49,8 +49,8 @@ int main() {
single_thread_context ctx1;
single_thread_context ctx2;

sync_wait(when_all(awaitable_sender{makeTask(ctx1.get_scheduler())},
awaitable_sender{makeTask(ctx2.get_scheduler())}));
sync_wait(when_all(awaitable_sender(makeTask(ctx1.get_scheduler())),
awaitable_sender(makeTask(ctx2.get_scheduler()))));

if (sharedState != 200'000) {
std::printf("error: incorrect result %i, expected 2000000\n", sharedState);
Expand Down
4 changes: 2 additions & 2 deletions examples/async_trace.cpp
Expand Up @@ -95,12 +95,12 @@ int main() {
return time;
}),
#if !UNIFEX_NO_COROUTINES
awaitable_sender {
awaitable_sender(
[]() -> task<int> {
co_await dump_async_trace("coroutine");
co_return 42;
}()
}
)
#else
just(42)
#endif // UNIFEX_NO_COROUTINES
Expand Down
2 changes: 1 addition & 1 deletion examples/coroutine_stream_consumer.cpp
Expand Up @@ -66,7 +66,7 @@ int main() {
co_return sum;
};

sync_wait(awaitable_sender{makeTask()});
sync_wait(awaitable_sender(makeTask()));

return 0;
}
Expand Down
88 changes: 50 additions & 38 deletions include/unifex/adapt_stream.hpp
Expand Up @@ -21,66 +21,78 @@
#include <type_traits>

namespace unifex {
namespace _adapt_stream {
template <typename Stream, typename NextAdaptFunc, typename CleanupAdaptFunc>
struct _adapted {
struct type;
};
template <typename Stream, typename NextAdaptFunc, typename CleanupAdaptFunc = void>
using adapted = typename _adapted<
std::remove_cvref_t<Stream>,
std::decay_t<NextAdaptFunc>,
std::decay_t<CleanupAdaptFunc>>::type;

template <
typename Stream,
typename NextAdaptFunc,
typename CleanupAdaptFunc>
struct adapted_stream {
template <typename Stream, typename NextAdaptFunc, typename CleanupAdaptFunc>
struct _adapted<Stream, NextAdaptFunc, CleanupAdaptFunc>::type {
Stream innerStream_;
NextAdaptFunc nextAdapter_;
CleanupAdaptFunc cleanupAdapter_;

friend auto tag_invoke(tag_t<next>, adapted_stream& s)
-> std::invoke_result_t<NextAdaptFunc&, next_sender_t<Stream>> {
friend auto tag_invoke(tag_t<next>, type& s)
-> std::invoke_result_t<NextAdaptFunc&, next_sender_t<Stream>> {
return std::invoke(s.nextAdapter_, next(s.innerStream_));
}

friend auto tag_invoke(tag_t<cleanup>, adapted_stream& s)
-> std::invoke_result_t<CleanupAdaptFunc&, cleanup_sender_t<Stream>> {
friend auto tag_invoke(tag_t<cleanup>, type& s)
-> std::invoke_result_t<CleanupAdaptFunc&, cleanup_sender_t<Stream>> {
return std::invoke(s.cleanupAdapter_, cleanup(s.innerStream_));
}
};

template <typename Stream, typename AdaptFunc>
struct both_adapted_stream {
struct _adapted<Stream, AdaptFunc, void> {
struct type;
};
template <typename Stream, typename AdaptFunc>
struct _adapted<Stream, AdaptFunc, void>::type {
Stream innerStream_;
AdaptFunc adapter_;

friend auto tag_invoke(tag_t<next>, both_adapted_stream& s)
-> std::invoke_result_t<AdaptFunc&, next_sender_t<Stream>> {
friend auto tag_invoke(tag_t<next>, type& s)
-> std::invoke_result_t<AdaptFunc&, next_sender_t<Stream>> {
return std::invoke(s.adapter_, next(s.innerStream_));
}

friend auto tag_invoke(tag_t<cleanup>, both_adapted_stream& s)
-> std::invoke_result_t<AdaptFunc&, cleanup_sender_t<Stream>> {
friend auto tag_invoke(tag_t<cleanup>, type& s)
-> std::invoke_result_t<AdaptFunc&, cleanup_sender_t<Stream>> {
return std::invoke(s.adapter_, cleanup(s.innerStream_));
}
};
} // namespace _adapt_stream

template <typename Stream, typename AdapterFunc>
auto adapt_stream(Stream&& stream, AdapterFunc&& adapt) {
return both_adapted_stream<
std::remove_cvref_t<Stream>,
std::remove_cvref_t<AdapterFunc>>{(Stream &&) stream,
(AdapterFunc &&) adapt};
}

template <
typename Stream,
typename NextAdapterFunc,
typename CleanupAdapterFunc>
auto adapt_stream(
Stream&& stream,
NextAdapterFunc&& adaptNext,
CleanupAdapterFunc&& adaptCleanup) {
return adapted_stream<
std::remove_cvref_t<Stream>,
std::remove_cvref_t<NextAdapterFunc>,
std::remove_cvref_t<CleanupAdapterFunc>>{(Stream &&) stream,
(NextAdapterFunc &&) adaptNext,
(CleanupAdapterFunc &&)
adaptCleanup};
}
namespace _adapt_stream_cpo {
inline constexpr struct _fn {
template <typename Stream, typename AdapterFunc>
auto operator()(Stream&& stream, AdapterFunc&& adapt) const
-> _adapt_stream::adapted<Stream, AdapterFunc> {
return {(Stream &&) stream, (AdapterFunc &&) adapt};
}

template <
typename Stream,
typename NextAdapterFunc,
typename CleanupAdapterFunc>
auto operator()(
Stream&& stream,
NextAdapterFunc&& adaptNext,
CleanupAdapterFunc&& adaptCleanup) const
-> _adapt_stream::adapted<Stream, NextAdapterFunc, CleanupAdapterFunc> {
return {
(Stream &&) stream,
(NextAdapterFunc &&) adaptNext,
(CleanupAdapterFunc &&) adaptCleanup};
}
} adapt_stream {};
} // namespace _adapt_stream_cpo
using _adapt_stream_cpo::adapt_stream;
} // namespace unifex
92 changes: 67 additions & 25 deletions include/unifex/allocate.hpp
Expand Up @@ -24,16 +24,24 @@
#include <memory>
#include <type_traits>

namespace unifex
{
namespace unifex {
namespace _alloc {
template <typename Operation, typename Allocator>
class allocated_operation {
struct _op {
class type;
};
template <typename Operation, typename Allocator>
using operation = typename _op<Operation, Allocator>::type;

template <typename Operation, typename Allocator>
class _op<Operation, Allocator>::type {
using operation = type;
using allocator_t = typename std::allocator_traits<
Allocator>::template rebind_alloc<Operation>;

public:
template <typename Sender, typename Receiver>
explicit allocated_operation(Sender&& s, Receiver&& r)
explicit type(Sender&& s, Receiver&& r)
: allocator_(get_allocator(r)) {
using allocator_traits = std::allocator_traits<allocator_t>;
Operation* op = allocator_traits::allocate(allocator_, 1);
Expand All @@ -48,12 +56,12 @@ namespace unifex
constructorSucceeded = true;
}

~allocated_operation() {
~type() {
op_->~Operation();
std::allocator_traits<allocator_t>::deallocate(allocator_, op_, 1);
}

friend void tag_invoke(tag_t<start>, allocated_operation& op) noexcept {
friend void tag_invoke(tag_t<start>, operation& op) noexcept {
start(*op.op_);
}

Expand All @@ -63,58 +71,92 @@ namespace unifex
};

template <typename Sender>
class allocate_sender {
struct _sender {
class type;
};
template <typename Sender>
using sender = typename _sender<std::remove_cvref_t<Sender>>::type;

template <typename Sender>
class _sender<Sender>::type {
using sender = type;
public:
template <
template <typename...>
class Variant,
template <typename...>
class Tuple>
template <typename...> class Variant,
template <typename...> class Tuple>
using value_types = typename Sender::template value_types<Variant, Tuple>;

template <template <typename...> class Variant>
using error_types = typename Sender::template error_types<Variant>;

template <typename Receiver>
friend auto tag_invoke(tag_t<connect>, allocate_sender&& s, Receiver&& r)
-> allocated_operation<
friend auto tag_invoke(tag_t<connect>, sender&& s, Receiver&& r)
-> operation<
operation_t<Sender, Receiver>,
std::remove_cvref_t<get_allocator_t<Receiver>>> {
return allocated_operation<
return operation<
operation_t<Sender, Receiver>,
std::remove_cvref_t<get_allocator_t<Receiver>>>{
(Sender &&) s.sender_, (Receiver &&) r};
}

template <typename Receiver>
friend auto tag_invoke(tag_t<connect>, allocate_sender& s, Receiver&& r)
-> allocated_operation<
friend auto tag_invoke(tag_t<connect>, sender& s, Receiver&& r)
-> operation<
operation_t<Sender&, Receiver>,
std::remove_cvref_t<get_allocator_t<Receiver>>> {
return allocated_operation<
return operation<
operation_t<Sender&, Receiver>,
std::remove_cvref_t<get_allocator_t<Receiver>>>{
s.sender_, (Receiver &&) r};
}

template <typename Receiver>
friend auto
tag_invoke(tag_t<connect>, const allocate_sender& s, Receiver&& r)
-> allocated_operation<
tag_invoke(tag_t<connect>, const sender& s, Receiver&& r)
-> operation<
operation_t<const Sender&, Receiver>,
std::remove_cvref_t<get_allocator_t<Receiver>>> {
return allocated_operation<
return operation<
operation_t<const Sender&, Receiver>,
std::remove_cvref_t<get_allocator_t<Receiver>>>{
std::as_const(s.sender_), (Receiver &&) r};
}

Sender sender_;
};
} // namespace _alloc

template <typename Sender>
auto allocate(Sender&& sender) -> allocate_sender<std::decay_t<Sender>> {
return allocate_sender<std::decay_t<Sender>>{(Sender &&) sender};
}
namespace _alloc_cpo {
inline constexpr struct _fn {
private:
template<bool>
struct _impl {
template <typename Sender>
auto operator()(Sender&& predecessor) const
noexcept(is_nothrow_tag_invocable_v<_fn, Sender>) {
return unifex::tag_invoke(_fn{}, (Sender&&) predecessor);
}
};
public:
template <typename Sender>
auto operator()(Sender&& predecessor) const
noexcept(std::is_nothrow_invocable_v<
_impl<is_tag_invocable_v<_fn, Sender>>, Sender>) {
return _impl<is_tag_invocable_v<_fn, Sender>>{}((Sender&&) predecessor);
}
} allocate{};

template<>
struct _fn::_impl<false> {
template <typename Sender>
auto operator()(Sender&& predecessor) const
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We seem to be inconsistent with the naming of the input sender.
We use 'sender', 'source' and 'predecessor' in various places.
Ideally we'd be consistent. Any preferences?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No strong feelings.

noexcept(std::is_nothrow_constructible_v<_alloc::sender<Sender>, Sender>)
-> _alloc::sender<Sender> {
return _alloc::sender<Sender>{(Sender &&) predecessor};
}
};
} // namespace _alloc_cpo
using _alloc_cpo::allocate;
ericniebler marked this conversation as resolved.
Show resolved Hide resolved

} // namespace unifex
} // namespace unifex