Created
August 2, 2018 16:07
-
-
Save jaredhoberock/9eb90aa5f9e4755a1ea4b206a8e837c1 to your computer and use it in GitHub Desktop.
Experimental polymorphic executor which exposes a single .execute() function
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// $ clang-5.0 -std=c++17 -Iexecutors-impl/include -lstdc++ neo_executor.cpp | |
#include <experimental/execution> | |
#include <type_traits> | |
#include <iostream> | |
template<class T> | |
struct __is_bulk_task | |
{ | |
template<class U = T, | |
class = decltype(std::declval<U>().function()), | |
class = decltype(std::declval<U>().shape()), | |
class = decltype(std::declval<U>().shared_factory()) | |
> | |
static constexpr bool test(int) { return true; } | |
static constexpr bool test(...) { return false; } | |
static constexpr bool value = test(0); | |
}; | |
template<class T> | |
static constexpr bool __is_bulk_task_v = __is_bulk_task<T>::value; | |
template<class Function, class SharedFactory> | |
struct __bulk_task_t | |
{ | |
Function& function() | |
{ | |
return f_; | |
} | |
const Function& function() const | |
{ | |
return f_; | |
} | |
size_t shape() const | |
{ | |
return shape_; | |
} | |
SharedFactory& shared_factory() | |
{ | |
return shared_factory_; | |
} | |
const SharedFactory& shared_factory() const | |
{ | |
return shared_factory_; | |
} | |
Function f_; | |
size_t shape_; | |
SharedFactory shared_factory_; | |
}; | |
template<class Function, class SharedFactory> | |
__bulk_task_t<std::decay_t<Function>, std::decay_t<SharedFactory>> __bulk_task(Function&& f, size_t n, SharedFactory&& shared_factory) | |
{ | |
return {std::forward<Function>(f), n, std::forward<SharedFactory>(shared_factory)}; | |
} | |
template<class NeoExecutor> | |
struct __old_executor_adaptor | |
{ | |
friend bool operator==(const __old_executor_adaptor& a, const __old_executor_adaptor& b) noexcept | |
{ | |
return a.adapted_ex_ == b.adapted_ex_; | |
} | |
friend bool operator!=(const __old_executor_adaptor& a, const __old_executor_adaptor& b) noexcept | |
{ | |
return !(a == b); | |
} | |
template<class Function, class SharedFactory> | |
void bulk_execute(Function&& f, size_t n, SharedFactory&& shared_factory) const noexcept | |
{ | |
adapted_ex_.execute(__bulk_task(std::forward<Function>(f), n, std::forward<SharedFactory>(shared_factory))); | |
} | |
template<class Function, | |
class = std::enable_if_t< | |
std::is_invocable_v<std::decay_t<Function>> | |
> | |
> | |
void execute(Function&& f) const noexcept | |
{ | |
adapted_ex_.execute(std::forward<Function>(f)); | |
} | |
NeoExecutor adapted_ex_; | |
}; | |
template<class... SupportableProperties> | |
class neo_executor : private std::experimental::execution::executor<SupportableProperties...> | |
{ | |
private: | |
using super_t = std::experimental::execution::executor<SupportableProperties...>; | |
public: | |
neo_executor() = default; | |
neo_executor(const neo_executor&) = default; | |
template<class Executor> | |
neo_executor(const Executor& ex) : super_t(__old_executor_adaptor<Executor>{ex}) {} | |
template<class Function> | |
void execute(Function&& f) const | |
{ | |
// XXX this if should also check that single is in the SupportableProperties | |
if constexpr (std::is_invocable_v<Function>) | |
{ | |
super_t::execute(std::forward<Function>(f)); | |
} | |
// XXX this if should also check that bulk is in the SupportableProperties | |
else if constexpr (__is_bulk_task_v<Function>) | |
{ | |
super_t::bulk_execute(f.function(), f.shape(), f.shared_factory()); | |
} | |
else | |
{ | |
static_assert("Unrecognized callable."); | |
} | |
} | |
}; | |
class inline_executor | |
{ | |
public: | |
friend bool operator==(const inline_executor&, const inline_executor&) noexcept | |
{ | |
return true; | |
} | |
friend bool operator!=(const inline_executor&, const inline_executor&) noexcept | |
{ | |
return false; | |
} | |
template <class Function> | |
void execute(Function f) const noexcept | |
{ | |
f(); | |
} | |
}; | |
static_assert(std::experimental::execution::is_oneway_executor_v<inline_executor>, "one way executor requirements not met"); | |
class neo_bulk_executor | |
{ | |
public: | |
friend bool operator==(const neo_bulk_executor&, const neo_bulk_executor&) noexcept | |
{ | |
return true; | |
} | |
friend bool operator!=(const neo_bulk_executor&, const neo_bulk_executor&) noexcept | |
{ | |
return false; | |
} | |
template<class BulkTask> | |
void execute(BulkTask task) const noexcept | |
{ | |
auto f = std::move(task.function()); | |
auto shape = task.shape(); | |
auto shared = std::move(task.shared_factory())(); | |
for(auto i = 0; i < shape; ++i) | |
{ | |
f(i, shared); | |
} | |
} | |
}; | |
int main() | |
{ | |
using namespace std::experimental::execution; | |
inline_executor inline_ex; | |
neo_bulk_executor bulk_ex; | |
neo_executor<oneway_t, single_t> wrapped_inline_ex(inline_ex); | |
wrapped_inline_ex.execute([] | |
{ | |
std::cout << "Hello world from inline agent" << std::endl; | |
}); | |
neo_executor<oneway_t, bulk_t> wrapped_bulk_ex(bulk_ex); | |
wrapped_bulk_ex.execute(__bulk_task( | |
[](size_t i, auto& shared) | |
{ | |
std::cout << "Hello world from bulk agent " << i << std::endl; | |
}, | |
10, | |
[]{ return 13; } | |
)); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment