From 7c4bcf878160392c1e2826c46c95c17582be7fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=C4=8Cuki=C4=87?= Date: Tue, 6 Jun 2023 22:53:22 +0200 Subject: [PATCH] Added a plugin system for defining nodes --- CMakeLists.txt | 3 + include/graph.hpp | 250 +++++++++++++++++++++++------- include/node.hpp | 10 ++ include/node_registry.hpp | 103 ++++++++++++ include/plugin.hpp | 114 ++++++++++++++ include/plugin_loader.hpp | 195 +++++++++++++++++++++++ include/reflection.hpp | 7 + include/scheduler.hpp | 50 +++--- include/typelist.hpp | 53 +++---- test/CMakeLists.txt | 32 +++- test/app_plugins_test.cpp | 150 ++++++++++++++++++ test/plugins/CMakeLists.txt | 20 +++ test/plugins/bad_plugin.cpp | 45 ++++++ test/plugins/good_base_plugin.cpp | 61 ++++++++ test/plugins/good_math_plugin.cpp | 64 ++++++++ test/qa_plugins_test.cpp | 180 +++++++++++++++++++++ 16 files changed, 1220 insertions(+), 117 deletions(-) create mode 100644 include/node_registry.hpp create mode 100644 include/plugin.hpp create mode 100644 include/plugin_loader.hpp create mode 100644 test/app_plugins_test.cpp create mode 100644 test/plugins/CMakeLists.txt create mode 100644 test/plugins/bad_plugin.cpp create mode 100644 test/plugins/good_base_plugin.cpp create mode 100644 test/plugins/good_math_plugin.cpp create mode 100644 test/qa_plugins_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 46507c3f8..ff0d3af17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,9 @@ cmake_minimum_required(VERSION 3.19) project(graph-prototype CXX) set(CMAKE_CXX_STANDARD 20) +# Mainly for FMT +set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) + add_library(graph-prototype-options INTERFACE) include(cmake/CompilerWarnings.cmake) set_project_warnings(graph-prototype-options) diff --git a/include/graph.hpp b/include/graph.hpp index 92daaf25d..8794283e6 100644 --- a/include/graph.hpp +++ b/include/graph.hpp @@ -187,13 +187,21 @@ class dynamic_port { } public: - using value_type = void; // a sterile port + using value_type = void; // a sterile port - constexpr dynamic_port() = delete; + constexpr dynamic_port() = delete; - template - constexpr dynamic_port(const T &arg) = delete; + dynamic_port(const dynamic_port &arg) = delete; + dynamic_port & + operator=(const dynamic_port &arg) + = delete; + + dynamic_port(dynamic_port &&arg) = default; + dynamic_port & + operator=(dynamic_port &&arg) + = default; + // TODO: Make owning versus non-owning API more explicit template explicit constexpr dynamic_port(T &arg) noexcept : _accessor{ std::make_unique>(arg) } {} @@ -335,76 +343,148 @@ class dynamic_node { #endif -class graph { +class node_model { +protected: + using dynamic_ports = std::vector; + bool _dynamic_ports_loaded = false; + dynamic_ports _dynamic_input_ports; + dynamic_ports _dynamic_output_ports; + + node_model(){}; + public: - class node_model { - public: - virtual ~node_model() = default; + node_model(const node_model &) = delete; + node_model & + operator=(const node_model &) + = delete; + node_model(node_model &&other) = delete; + node_model & + operator=(node_model &&other) + = delete; + + fair::graph::dynamic_port & + dynamic_input_port(std::size_t index) { + assert(_dynamic_ports_loaded); + return _dynamic_input_ports[index]; + } - virtual std::string_view - name() const - = 0; + fair::graph::dynamic_port & + dynamic_output_port(std::size_t index) { + assert(_dynamic_ports_loaded); + return _dynamic_output_ports[index]; + } - virtual work_return_t - work() = 0; + auto + dynamic_input_ports_size() const { + assert(_dynamic_ports_loaded); + return _dynamic_input_ports.size(); + } - virtual void * - raw() = 0; - }; + auto + dynamic_output_ports_size() const { + assert(_dynamic_ports_loaded); + return _dynamic_output_ports.size(); + } - std::vector> _connection_definitions; - std::vector> _nodes; -private: + virtual ~node_model() = default; - template - class node_wrapper final : public node_model { - private: - static_assert(std::is_same_v>); - T _node; + virtual std::string_view + name() const + = 0; - public: - node_wrapper(const node_wrapper &other) = delete; + virtual work_return_t + work() = 0; - node_wrapper & - operator=(const node_wrapper &other) - = delete; + virtual void * + raw() = 0; +}; - node_wrapper(node_wrapper &&other) : _node(std::exchange(other._node, nullptr)) {} +template +class node_wrapper : public node_model { +private: + static_assert(std::is_same_v>); + T _node; + + [[nodiscard]] constexpr const auto & + node_ref() const noexcept { + if constexpr (requires { *_node; }) { + return *_node; + } else { + return _node; + } + } - node_wrapper & - operator=(node_wrapper &&other) { - auto tmp = std::move(other); - std::swap(_node, tmp._node); - return *this; + [[nodiscard]] constexpr auto & + node_ref() noexcept { + if constexpr (requires { *_node; }) { + return *_node; + } else { + return _node; } + } - ~node_wrapper() override = default; + void + init_dynamic_ports() { + using Node = std::remove_cvref_t; - node_wrapper() {} + constexpr std::size_t input_port_count = fair::graph::traits::node::template input_port_types::size; + [this](std::index_sequence) { (this->_dynamic_input_ports.emplace_back(fair::graph::input_port(&node_ref())), ...); } + (std::make_index_sequence()); - template - requires(!std::is_same_v, T>) - node_wrapper(Arg &&arg) : _node(std::forward(arg)) {} + constexpr std::size_t output_port_count = fair::graph::traits::node::template output_port_types::size; + [this](std::index_sequence) { (this->_dynamic_output_ports.push_back(fair::graph::dynamic_port(fair::graph::output_port(&node_ref()))), ...); } + (std::make_index_sequence()); - template - requires(sizeof...(Args) > 1) - node_wrapper(Args &&...args) : _node{ std::forward(args)... } {} + static_assert(input_port_count + output_port_count > 0); + _dynamic_ports_loaded = true; + } - constexpr work_return_t - work() override { - return _node.work(); - } +public: + node_wrapper(const node_wrapper &other) = delete; + node_wrapper(node_wrapper &&other) = delete; + node_wrapper & + operator=(const node_wrapper &other) + = delete; + node_wrapper & + operator=(node_wrapper &&other) + = delete; + + ~node_wrapper() override = default; + + node_wrapper() { init_dynamic_ports(); } + + template + requires(!std::is_same_v, T>) + node_wrapper(Arg &&arg) : _node(std::forward(arg)) { + init_dynamic_ports(); + } - std::string_view - name() const override { - return _node.name(); - } + template + requires(sizeof...(Args) > 1) + node_wrapper(Args &&...args) : _node{ std::forward(args)... } { + init_dynamic_ports(); + } - void * - raw() override { - return std::addressof(_node); - } - }; + constexpr work_return_t + work() override { + return node_ref().work(); + } + + std::string_view + name() const override { + return node_ref().name(); + } + + void * + raw() override { + return std::addressof(node_ref()); + } +}; + +class graph { +private: + std::vector> _connection_definitions; + std::vector> _nodes; class edge { public: @@ -462,7 +542,34 @@ class graph { } }; - std::vector _edges; + std::vector _edges; + + template + std::unique_ptr & + find_node(Node &what) { + auto it = [&, this] { + if constexpr (std::is_same_v) { + return std::find_if(_nodes.begin(), _nodes.end(), [&](const auto &node) { return node.get() == &what; }); + } else { + return std::find_if(_nodes.begin(), _nodes.end(), [&](const auto &node) { return node->raw() == &what; }); + } + }(); + + if (it == _nodes.end()) throw fmt::format("No such node in this graph"); + return *it; + } + + template + [[nodiscard]] dynamic_port & + dynamic_output_port(Node &node, std::size_t index) { + return find_node(node)->dynamic_output_port(index); + } + + template + [[nodiscard]] dynamic_port & + dynamic_input_port(Node &node, std::size_t index) { + return find_node(node)->dynamic_input_port(index); + } template [[nodiscard]] connection_result_t @@ -575,6 +682,12 @@ class graph { return _edges.size(); } + node_model & + add_node(std::unique_ptr node) { + auto &new_node_ref = _nodes.emplace_back(std::move(node)); + return *new_node_ref.get(); + } + template auto & make_node(Args &&...args) { // TODO for review: do we still need this factory method or allow only pmt-map-type constructors (see below) @@ -622,10 +735,31 @@ class graph { return graph::source_connector(*this, source, std::invoke(member_ptr, source)); } - [[nodiscard]] const std::vector& + [[nodiscard]] const std::vector & get_edges() const { return _edges; } + + template + connection_result_t + dynamic_connect(Source &source, std::size_t source_index, Sink &sink, std::size_t sink_index) { + return dynamic_output_port(source, source_index).connect(dynamic_input_port(sink, sink_index)); + } + + const std::vector> & + connection_definitions() { + return _connection_definitions; + } + + void + clear_connection_definitions() { + _connection_definitions.clear(); + } + + auto & + nodes() { + return _nodes; + } }; // TODO: add nicer enum formatter diff --git a/include/node.hpp b/include/node.hpp index ddcfd7b78..8d759e3ae 100644 --- a/include/node.hpp +++ b/include/node.hpp @@ -943,6 +943,16 @@ static_assert(traits::node::can_process_simd(copy( } // namespace test #endif +namespace detail { +template typename NodeTemplate, typename... AllowedTypes> +struct register_node { + template + register_node(RegisterInstance *plugin_instance, std::string node_type) { + plugin_instance->template add_node_type(node_type); + } +}; +} // namespace detail + } // namespace fair::graph #endif // include guard diff --git a/include/node_registry.hpp b/include/node_registry.hpp new file mode 100644 index 000000000..424224236 --- /dev/null +++ b/include/node_registry.hpp @@ -0,0 +1,103 @@ +#ifndef GRAPH_PROTOTYPE_NODE_REGISTRY_H +#define GRAPH_PROTOTYPE_NODE_REGISTRY_H + +#include +#include +#include + +#include +#include + +namespace fair::graph { + +using namespace std::string_literals; +using namespace std::string_view_literals; + +struct node_construction_param { + std::string_view key; + std::string_view value; +}; + +struct node_construction_params { + std::span params; + + template + requires(!std::is_same_v, node_construction_params>) + node_construction_params(Collection &collection) : params(collection) {} + + node_construction_params(const node_construction_params &other) : params(other.params) {} + + node_construction_params & + operator=(node_construction_params &) + = delete; + + node_construction_params() {} + + std::string_view + value(std::string_view key) const { + auto it = std::find_if(params.begin(), params.end(), [&](const auto ¶m) { return key == param.key; }); + return it != params.end() ? it->value : std::string_view{}; + } +}; + +class node_registry { +private: + using node_type_handler = std::function &, node_construction_params)>; + std::vector _node_types; + std::unordered_map> _node_type_handlers; + + template typename NodeTemplate, typename ValueType> + static auto + create_handler() { + return [](std::unique_ptr &result, fair::graph::node_construction_params params) { + using NodeType = NodeTemplate; + + if constexpr (std::is_constructible_v) { + result = std::make_unique>(params); // gp_plugin_node::wrap(std::make_shared(params)); + } else if constexpr (std::is_constructible_v) { + result = std::make_unique>(); + } else { + fair::meta::print_types, NodeType>{}; + } + return true; + }; + } + +public: + template typename NodeTemplate, typename... AllowedTypes> + void + add_node_type(std::string node_type) { + _node_types.push_back(node_type); + auto &node_handlers = _node_type_handlers[node_type]; + + ((node_handlers[std::string(meta::type_name())] = create_handler()), ...); + } + + std::span + provided_nodes() const { + return _node_types; + } + + std::unique_ptr + create_node(std::string_view name, std::string_view type, node_construction_params params) { + std::unique_ptr result; + auto node_it = _node_type_handlers.find(std::string(name)); + if (node_it == _node_type_handlers.end()) return nullptr; + + auto &node_handlers = node_it->second; + auto handler_it = node_handlers.find(std::string(type)); + if (handler_it == node_handlers.end()) return nullptr; + + handler_it->second(result, params); + return result; + } + + auto + known_nodes() const { + return _node_types; + } +}; + +} // namespace fair::graph + +#endif // include guard diff --git a/include/plugin.hpp b/include/plugin.hpp new file mode 100644 index 000000000..b3be9fe08 --- /dev/null +++ b/include/plugin.hpp @@ -0,0 +1,114 @@ +#ifndef GRAPH_PROTOTYPE_PLUGIN_H +#define GRAPH_PROTOTYPE_PLUGIN_H + +#include +#include + +#include + +#include +#include + +using namespace std::string_literals; +using namespace std::string_view_literals; + +#define GP_PLUGIN_CURRENT_ABI_VERSION 1 + +struct gp_plugin_metadata { + std::string_view plugin_name; + std::string_view plugin_author; + std::string_view plugin_license; + std::string_view plugin_version; +}; + +class gp_plugin_base { +public: + virtual ~gp_plugin_base() {} + + virtual std::uint8_t + abi_version() const + = 0; + virtual const gp_plugin_metadata & + metadata() const + = 0; + + virtual std::span + provided_nodes() const = 0; + virtual std::unique_ptr + create_node(std::string_view name, std::string_view type, fair::graph::node_construction_params params) = 0; +}; + +namespace fair::graph { +class plugin : public gp_plugin_base { +private: + gp_plugin_metadata *_metadata = nullptr; + fair::graph::node_registry registry; + +public: + plugin() {} + + void + set_metadata(gp_plugin_metadata &metadata) { + _metadata = &metadata; + } + + std::uint8_t + abi_version() const override { + return GP_PLUGIN_CURRENT_ABI_VERSION; + } + + virtual const gp_plugin_metadata & + metadata() const override { + return *_metadata; + } + + std::span + provided_nodes() const override { + return registry.provided_nodes(); + } + + std::unique_ptr + create_node(std::string_view name, std::string_view type, fair::graph::node_construction_params params) override { + return registry.create_node(name, type, params); + } + + template typename NodeTemplate, typename... Args> + void + add_node_type(std::string node_type) { + registry.add_node_type(std::move(node_type)); + } +}; + +} // namespace fair::graph + +#define GP_PLUGIN(Name, Author, License, Version) \ + inline namespace GP_PLUGIN_DEFINITION_NAMESPACE { \ + fair::graph::plugin * \ + gp_plugin_instance() { \ + static fair::graph::plugin *instance = [] { \ + auto *result = new fair::graph::plugin(); \ + static gp_plugin_metadata plugin_metadata{ Name, Author, License, Version }; \ + result->set_metadata(plugin_metadata); \ + return result; \ + }(); \ + return instance; \ + } \ + } \ + extern "C" { \ + gp_plugin_base * \ + gp_plugin_make() { \ + return gp_plugin_instance(); \ + } \ + void \ + gp_plugin_free(gp_plugin_base *plugin) { \ + if (plugin != gp_plugin_instance()) { \ + assert(false && "Requested to delete something that is not us"); \ + return; \ + } \ + delete plugin; \ + } \ + } + +#define GP_PLUGIN_REGISTER_NODE(...) GP_REGISTER_NODE(gp_plugin_instance(), __VA_ARGS__); + +#endif // include guard diff --git a/include/plugin_loader.hpp b/include/plugin_loader.hpp new file mode 100644 index 000000000..27f0f401d --- /dev/null +++ b/include/plugin_loader.hpp @@ -0,0 +1,195 @@ +#ifndef GRAPH_PROTOTYPE_PLUGIN_LOADER_H +#define GRAPH_PROTOTYPE_PLUGIN_LOADER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using plugin_create_function_t = gp_plugin_base *(*) (); +using plugin_destroy_function_t = void (*)(gp_plugin_base *); + +namespace fair::graph { + +using namespace std::string_literals; +using namespace std::string_view_literals; + +#ifndef __EMSCRIPTEN__ +// Plugins are not supported on WASM + +class plugin_handler { +private: + void *_dl_handle = nullptr; + plugin_create_function_t _create_fn = nullptr; + plugin_destroy_function_t _destroy_fn = nullptr; + gp_plugin_base *_instance = nullptr; + + std::string _status; + + void + release() { + if (_instance) { + _destroy_fn(_instance); + _instance = nullptr; + } + + if (_dl_handle) { + dlclose(_dl_handle); + _dl_handle = nullptr; + } + } + +public: + plugin_handler() {} + + plugin_handler(std::string plugin_file) { + _dl_handle = dlopen(plugin_file.c_str(), RTLD_LAZY); + if (!_dl_handle) { + _status = "Failed to load the plugin file"; + return; + } + + _create_fn = (plugin_create_function_t) (dlsym(_dl_handle, "gp_plugin_make")); + if (!_create_fn) { + _status = "Failed to load symbol gp_plugin_make"; + release(); + return; + } + + _destroy_fn = (plugin_destroy_function_t) (dlsym(_dl_handle, "gp_plugin_free")); + if (!_destroy_fn) { + _status = "Failed to load symbol gp_plugin_free"; + release(); + return; + } + + _instance = _create_fn(); + if (!_instance) { + _status = "Failed to create an instance of the plugin"; + release(); + return; + } + + if (_instance->abi_version() != GP_PLUGIN_CURRENT_ABI_VERSION) { + _status = "Wrong ABI version"; + release(); + return; + } + } + + plugin_handler(const plugin_handler &other) = delete; + plugin_handler & + operator=(const plugin_handler &other) + = delete; + + plugin_handler(plugin_handler &&other) noexcept + : _dl_handle(std::exchange(other._dl_handle, nullptr)) + , _create_fn(std::exchange(other._create_fn, nullptr)) + , _destroy_fn(std::exchange(other._destroy_fn, nullptr)) + , _instance(std::exchange(other._instance, nullptr)) {} + + plugin_handler & + operator=(plugin_handler &&other) noexcept { + auto tmp = std::move(other); + std::swap(_dl_handle, tmp._dl_handle); + std::swap(_create_fn, tmp._create_fn); + std::swap(_destroy_fn, tmp._destroy_fn); + std::swap(_instance, tmp._instance); + return *this; + } + + ~plugin_handler() { release(); } + + explicit operator bool() const { return _instance; } + + const std::string & + status() const { + return _status; + } + + auto * + operator->() const { + return _instance; + } +}; + +class plugin_loader { +private: + std::vector _handlers; + std::unordered_map _handler_for_name; + std::unordered_map _failed_plugins; + + node_registry *_global_registry; + std::vector _known_nodes; + +public: + plugin_loader(node_registry *global_registry, std::span plugin_directories) : _global_registry(global_registry) { + for (const auto &directory : plugin_directories) { + std::cerr << std::filesystem::current_path() << std::endl; + + if (!std::filesystem::is_directory(directory)) continue; + + for (const auto &file : std::filesystem::directory_iterator{ directory }) { + if (file.is_regular_file() && file.path().extension() == ".so") { + if (plugin_handler handler(file.path().string()); handler) { + for (const auto &node_name : handler->provided_nodes()) { + _handler_for_name.emplace(std::string(node_name), handler.operator->()); + _known_nodes.emplace_back(node_name); + } + + _handlers.push_back(std::move(handler)); + + } else { + _failed_plugins[file.path()] = handler.status(); + } + } + } + } + } + + const auto & + plugins() const { + return _handlers; + } + + const auto & + failed_plugins() const { + return _failed_plugins; + } + + auto + known_nodes() const { + auto result = _known_nodes; + const auto &builtin = _global_registry->known_nodes(); + result.insert(result.end(), builtin.begin(), builtin.end()); + return result; + } + + std::unique_ptr + instantiate(std::string name, std::string_view type, node_construction_params params = {}) { + // Try to create a node from the global registry + if (auto result = _global_registry->create_node(name, type, params)) { + return result; + } + + auto it = _handler_for_name.find(name); + if (it == _handler_for_name.end()) return {}; + + auto &handler = it->second; + + return handler->create_node(std::move(name), type, params); + } +}; +#endif + +} // namespace fair::graph + +#endif // include guard diff --git a/include/reflection.hpp b/include/reflection.hpp index 3e6bc12bd..341eff272 100644 --- a/include/reflection.hpp +++ b/include/reflection.hpp @@ -128,4 +128,11 @@ ENABLE_REFLECTION_FOR_TEMPLATE_FULL((typename ...Ts), (Type), __VA_ARGS__) +#define GP_CONCAT_IMPL(x, y) x##y +#define GP_MACRO_CONCAT(x, y) GP_CONCAT_IMPL(x, y) + +#define GP_REGISTER_NODE(Register, Name, ...) fair::graph::detail::register_node GP_MACRO_CONCAT(GP_REGISTER_NODE_, __COUNTER__)(Register, #Name); + + + #endif //GRAPH_PROTOTYPE_REFLECTION_HPP diff --git a/include/scheduler.hpp b/include/scheduler.hpp index cee24efd2..35f62dc81 100644 --- a/include/scheduler.hpp +++ b/include/scheduler.hpp @@ -8,39 +8,47 @@ namespace fair::graph::scheduler { struct init_proof { init_proof(bool _success) : success(_success) {} - init_proof(init_proof && init): init_proof(init.success) {} + + init_proof(init_proof &&init) : init_proof(init.success) {} + bool success = true; - init_proof& operator=(init_proof &&init) noexcept {this->success = init; return *this;} + init_proof & + operator=(init_proof &&init) noexcept { + this->success = init; + return *this; + } + operator bool() const { return success; } }; -init_proof init(fair::graph::graph &graph) { - auto result = init_proof( - std::all_of(graph._connection_definitions.begin(), graph._connection_definitions.end(), [] (auto& connection_definition) { - return connection_definition() == connection_result_t::SUCCESS; - })); - graph._connection_definitions.clear(); +init_proof +init(fair::graph::graph &graph) { + auto result = init_proof(std::all_of(graph.connection_definitions().begin(), graph.connection_definitions().end(), + [](auto &connection_definition) { return connection_definition() == connection_result_t::SUCCESS; })); + graph.clear_connection_definitions(); return result; } /** * Trivial loop based scheduler, which iterates over all nodes in definition order in the graph until no node did any processing */ -class simple : public node{ - init_proof _init; +class simple : public node { + init_proof _init; fair::graph::graph _graph; + public: - explicit simple(fair::graph::graph &&graph) : _init{fair::graph::scheduler::init(graph)}, _graph(std::move(graph)) { } + explicit simple(fair::graph::graph &&graph) : _init{ fair::graph::scheduler::init(graph) }, _graph(std::move(graph)) {} - work_return_t work() { + work_return_t + work() { if (!_init) { return work_return_t::ERROR; } bool run = true; while (run) { bool something_happened = false; - for (auto &node : _graph._nodes) { + for (auto &node : _graph.nodes()) { auto result = node->work(); if (result == work_return_t::ERROR) { return work_return_t::ERROR; @@ -66,14 +74,15 @@ class simple : public node{ * detecting cycles and nodes which can be reached from several source nodes. */ class breadth_first : public node { - using node_t = fair::graph::graph::node_model*; - init_proof _init; - fair::graph::graph _graph; + using node_t = fair::graph::node_model *; + init_proof _init; + fair::graph::graph _graph; std::vector _nodelist; + public: - explicit breadth_first(fair::graph::graph &&graph) : _init{fair::graph::scheduler::init(graph)}, _graph(std::move(graph)) { + explicit breadth_first(fair::graph::graph &&graph) : _init{ fair::graph::scheduler::init(graph) }, _graph(std::move(graph)) { std::map> _adjacency_list{}; - std::vector _source_nodes{}; + std::vector _source_nodes{}; // compute the adjacency list std::set node_reached; for (auto &e : _graph.get_edges()) { @@ -106,7 +115,8 @@ class breadth_first : public node { } } - work_return_t work() { + work_return_t + work() { if (!_init) { return work_return_t::ERROR; } @@ -122,6 +132,6 @@ class breadth_first : public node { } } }; -} +} // namespace fair::graph::scheduler #endif // GRAPH_PROTOTYPE_SCHEDULER_HPP diff --git a/include/typelist.hpp b/include/typelist.hpp index 93191db98..a9da78265 100644 --- a/include/typelist.hpp +++ b/include/typelist.hpp @@ -3,10 +3,10 @@ #include #include +#include +#include #include #include -#include -#include namespace fair::meta { @@ -60,9 +60,7 @@ struct concat_impl { template struct concat_impl { - using type = - typename concat_impl::type, typename concat_impl::type, - typename concat_impl::type>::type; + using type = typename concat_impl::type, typename concat_impl::type, typename concat_impl::type>::type; }; } // namespace detail @@ -108,24 +106,20 @@ struct splitter<4> { template<> struct splitter<8> { - template + template using first = typelist; - template + template using second = typelist; }; template<> struct splitter<16> { - template + template using first = typelist; - template + template using second = typelist; }; @@ -136,8 +130,7 @@ struct splitter { using B = splitter; template - using first = concat, - typename B::template first>>; + using first = concat, typename B::template first>>; template using second = typename B::template second>; @@ -206,8 +199,7 @@ struct reduce_impl> { }; template class Method, typename T0, typename T1, typename... Ts> -struct reduce_impl> - : public reduce_impl::type, Ts...>> {}; +struct reduce_impl> : public reduce_impl::type, Ts...>> {}; template class Method, typename T0, typename T1, typename T2, typename T3, typename... Ts> struct reduce_impl> : public reduce_impl::type, typename Method::type, Ts...>> {}; @@ -285,13 +277,8 @@ struct typelist { template requires(sizeof...(Ts) == std::tuple_size_v>) - static constexpr auto - construct(Tup &&args_tuple) { - return std::apply( - [](Args &&...args) { - return std::make_tuple(F::template apply(std::forward(args))...); - }, - std::forward(args_tuple)); + static constexpr auto construct(Tup &&args_tuple) { + return std::apply([](Args &&...args) { return std::make_tuple(F::template apply(std::forward(args))...); }, std::forward(args_tuple)); } template typename Trafo> @@ -300,6 +287,9 @@ struct typelist { template typename Pred> constexpr static bool all_of = (Pred::value && ...); + template typename Pred> + constexpr static bool any_of = (Pred::value || ...); + template typename Pred> constexpr static bool none_of = (!Pred::value && ...); @@ -344,18 +334,17 @@ struct typelist { } else { return static_cast(nullptr); } - }())>; - + }())>; }; - namespace detail { - template