diff --git a/src/voy/basic/sink.h b/src/voy/basic/sink.h index f0ffc51..10ce0e0 100644 --- a/src/voy/basic/sink.h +++ b/src/voy/basic/sink.h @@ -1,69 +1,69 @@ /* * Copyright (C) 2018 Ivan Čukić * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. * If not, see . */ #ifndef VOY_BASIC_SINK_H #define VOY_BASIC_SINK_H // STL #include // Self #include "../utils.h" #include "../dsl/node_tags.h" +namespace voy { + using voy::utils::non_copyable; using voy::dsl::sink_node_tag; -namespace voy { - template class sink: non_copyable { public: using node_category = sink_node_tag; explicit sink(F function) : m_function{std::move(function)} { } template void operator() (T&& value) const { voy_fwd_invoke(m_function, value); } void init() { } void notify_ended() const { } private: F m_function; }; } // namespace voy #endif // include guard diff --git a/src/voy/basic/values.h b/src/voy/basic/values.h index fc7c411..11e8471 100644 --- a/src/voy/basic/values.h +++ b/src/voy/basic/values.h @@ -1,109 +1,109 @@ /* * Copyright (C) 2018 Ivan Čukić * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. * If not, see . */ #ifndef VOY_BASIC_VALUES_H #define VOY_BASIC_VALUES_H // STL #include #include #include // Self #include "../utils.h" #include "../traits.h" #include "../dsl/node_tags.h" namespace voy { using voy::utils::non_copyable; using voy::dsl::source_node_tag; using voy::dsl::continuator_base; // `values` creates a reactive stream that emits the predefined // values as soon as the pipeline is initialized template class values: non_copyable { voy_assert_value_type(T); public: using node_category = source_node_tag; explicit values(std::initializer_list values) : m_values{values} { } template explicit values(C&& values) : m_values{voy_fwd(values)} { } template class node: public continuator_base, non_copyable { using base = continuator_base; public: node(std::vector&& values, Cont&& cont) : continuator_base{std::move(cont)} , m_values{std::move(values)} { } void init() { base::init(); for (auto&& value: m_values) { base::emit(std::move(value)); } m_values.clear(); base::notify_ended(); } private: std::vector m_values; }; template auto with_continuation(Cont&& cont) && { - return node(std::move(m_values), voy_fwd(cont)); + return node(std::move(m_values), voy_fwd(cont)); } private: std::vector m_values; }; } // namespace voy #endif // include guard diff --git a/src/voy/dsl.h b/src/voy/dsl.h index d7a3d1c..e3ee9a0 100644 --- a/src/voy/dsl.h +++ b/src/voy/dsl.h @@ -1,130 +1,279 @@ /* * Copyright (C) 2018 Ivan Čukić * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. * If not, see . */ #ifndef VOY_DSL_H #define VOY_DSL_H // STL #include #include // Self #include "utils.h" #include "traits.h" #include "dsl/node_tags.h" #include "dsl/node_traits.h" namespace voy::dsl { using namespace voy::traits; -using namespace node_traits; +using node_traits::is_connection_expr, + node_traits::is_node, + node_traits::is_source, + node_traits::is_sink, + node_traits::node_category, + node_traits::with_continuation_memfn; // We need to be able to store the connected graph paths (pipelines) // inside of ordinary classes, so we will return a type-erased pipeline // to the caller once the path is complete class pipeline { public: - using ptr = std::unique_ptr; - virtual ~pipeline() {} virtual void init() = 0; + + using ptr = std::unique_ptr; }; namespace detail { // The actual type of a pipeline template - class pipeline_impl: public pipeline { - public: + struct pipeline_impl: pipeline { explicit pipeline_impl(T&& content) noexcept : node{std::move(content.node)} { } void init() override { node.init(); } T node; }; // In order to use the fold expressions, we need to provide an operator // on which to fold. We can create a wrapper type with operator<< // defined for it template - class node_wrapper: utils::non_copyable { + struct node_wrapper: utils::non_copyable { voy_assert_value_type(Node); - public: - node_wrapper(Node&& node) + node_wrapper(Node node) : node{std::move(node)} { } - node_wrapper(node_wrapper&&) = delete; - node_wrapper& operator=(node_wrapper&&) = delete; + node_wrapper(node_wrapper&& other) = delete; + void operator=(node_wrapper&&) = delete; void init() { node.init(); } Node node; }; + template auto make_node_wrapper(Node&& node) { - return node_wrapper(voy_fwd(node)); + voy_assert_value_type(Node); + return node_wrapper{std::move(node)}; } + template decltype(auto) operator<< (node_wrapper&& receiver, node_wrapper&& sender) { voy_assert_value_type(Left); voy_assert_value_type(Right); return make_node_wrapper( std::move(sender.node).with_continuation(std::move(receiver.node)) ); } + // Goes through all the items in a tuple, and connects one by one template - pipeline::ptr connect_all(std::tuple&& items, + pipeline::ptr connect_all(std::tuple items, std::index_sequence) { return std::make_unique< decltype(pipeline_impl((... << make_node_wrapper(std::get(std::move(items)))))) > ((... << make_node_wrapper(std::get(std::move(items))))); } + + + // The connection_expr class represents one pipe operation + // in the AST where the left and right arguments can be either + // compound expressions themselves, or single nodes + template + struct connection_expr { + voy_assert_value_type(LeftGraph); + voy_assert_value_type(RightGraph); + + LeftGraph left; + RightGraph right; + + // An expression is also a graph node + using node_category = NodeCategory; + // ... but we still need to be able to differentiate it from normal nodes + using connection_expr_tag = node_category; + + // Generates a tuple of all nodes in an expression from right to left, + // that is, from the sink to the source + auto collect_graph_nodes() + { + auto collect_left_graph_nodes = [this] + { + if constexpr (is_connection_expr) { + return left.collect_graph_nodes(); + } else { + return std::make_tuple(std::move(left)); + } + }; + + auto collect_right_graph_nodes = [this] + { + if constexpr (is_connection_expr) { + return right.collect_graph_nodes(); + } else { + return std::make_tuple(std::move(right)); + } + }; + + return std::tuple_cat(collect_right_graph_nodes(), + collect_left_graph_nodes()); + } + + connection_expr(LeftGraph left, RightGraph right) + : left{std::move(left)} + , right{std::move(right)} + { + } + + auto evaluate() + { + if constexpr (std::is_same_v) { + auto sink_to_source_items = collect_graph_nodes(); + + auto result = connect_all(std::move(sink_to_source_items), + std::make_index_sequence< + std::tuple_size_v< + std::decay_t + > + >()); + + // async call result->init() + + return result; + + } else { + voy_fail(node_category, "'evaluate' can only be called on a complete path"); + } + } + }; + + + template < typename NodeCategory + , typename LeftGraph + , typename RightGraph + > + auto make_connection_expr(LeftGraph&& left, RightGraph&& right) + { + return connection_expr < NodeCategory + , traits::remove_cvref_t + , traits::remove_cvref_t + > { voy_fwd(left), voy_fwd(right) }; + } + + + template < typename Left + , typename Right + , typename LeftVal = traits::remove_cvref_t + , typename RightVal = traits::remove_cvref_t + > + decltype(auto) connect_streams(Left&& left, Right&& right) + { + static_assert(is_node, "The left needs to be a node"); + static_assert(is_node, "The right needs to be a node"); + static_assert( + is_connection_expr || + is_detected_v, + "The left node needs to be a connection expression, or to have with_continuation member function"); + + #define MAKE(Type) \ + make_connection_expr(std::move(left), \ + std::move(right)) + + if constexpr (!is_source && !is_sink) { + + return MAKE(transformation_node_tag); + + } else if constexpr (is_source && !is_sink) { + + return MAKE(node_category); + + } else if constexpr (!is_source && is_sink) { + + return MAKE(node_category); + + } else { + + // If we have both a sink and a source, we can connect the + // nodes in this path of the graph + return MAKE(void).evaluate(); + + } + + #undef MAKE + } } // namespace detail +template < typename Left + , typename Right + , typename LeftVal = traits::remove_cvref_t + , typename RightVal = traits::remove_cvref_t + , voy_require(is_node && is_node) + , voy_require(( + is_connection_expr || + is_detected_v + )) + > +decltype(auto) operator| (Left&& left, Right&& right) +{ + return detail::connect_streams(voy_fwd(left), voy_fwd(right)); +} + + } // namespace voy::dsl #endif // include guard diff --git a/src/voy/dsl/node_traits.h b/src/voy/dsl/node_traits.h index 34f346a..b04dc2f 100644 --- a/src/voy/dsl/node_traits.h +++ b/src/voy/dsl/node_traits.h @@ -1,97 +1,106 @@ /* * Copyright (C) 2018 Ivan Čukić * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. * If not, see . */ #ifndef VOY_NODE_TRAITS_H #define VOY_NODE_TRAITS_H // STL #include #include "node_tags.h" #include "../traits.h" #include "../utils.h" namespace voy::dsl::node_traits { using namespace voy::traits; // Returns the category of the node. It can also be used to detect whether // a class contains the node category specification template using node_category = typename remove_cvref_t::node_category; // Used to detect whther the class contains a with_continuation function // for rvalues template using with_continuation_memfn = decltype( - std::declval().with_continuation(voy::utils::do_nothing)); + std::declval().with_continuation(voy::utils::do_nothing{})); // Returns whether the node has the specified tag or not template voy_concept is_tagged_with = std::is_same_v>; // The meta-function to check whether a class is a node is easier to implement // using the constexpr-if because of the branching. namespace detail { template < typename Node , typename DetectedCategory = detected_t > voy_concept is_node() { if constexpr (!is_detected_v) { voy_fail(Node, "No category specified for the node"); return false; } else if constexpr (std::is_same_v) { voy_fail(Node, "Category is void, which means we already have a complete graph path"); return false; } else { return true; } } } // Wrapper for detail::is_node to make it look like a normal meta-function template voy_concept is_node = detail::is_node(); // Checks whether the node is a source node template voy_concept is_source = is_tagged_with; // Checks whether the node is a source node template voy_concept is_sink = is_tagged_with; +// To be used with the detection idiom to check whether a type is +// a connection_expr or a normal node +template +using connection_expr_tag_definition = typename Graph::connection_expr_tag; + +template +voy_concept is_connection_expr = + is_detected_v; + } // namespace voy::dsl::node_traits #endif // include guard diff --git a/src/voy/main.cpp b/src/voy/main.cpp index 3604f48..0c70381 100644 --- a/src/voy/main.cpp +++ b/src/voy/main.cpp @@ -1,47 +1,44 @@ /* * Copyright (C) 2018 Ivan Čukić * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. * If not, see . */ #include #include "basic/values.h" #include "basic/sink.h" #include "dsl.h" int main(int argc, char *argv[]) { auto cout = [] (auto&& value) { std::cout << "Out: " << voy_fwd(value) << std::endl; }; - auto nodes = std::make_tuple( - voy::sink { cout }, - voy::values { 42, 6 } - ); + using voy::dsl::operator|; - auto pipeline = voy::dsl::detail::connect_all( - std::move(nodes), std::make_index_sequence<2>()); + auto pipeline = + voy::values{42, 6} | voy::sink{cout}; pipeline->init(); return 0; } diff --git a/src/voy/utils.h b/src/voy/utils.h index 7eafbbc..f3114fe 100644 --- a/src/voy/utils.h +++ b/src/voy/utils.h @@ -1,104 +1,107 @@ /* * Copyright (C) 2018 Ivan Čukić * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. * If not, see . */ #ifndef VOY_UTILS_H #define VOY_UTILS_H #include // This file contains some things that would be cool to have in // the standard library. There might be a chance some of these will // end up in C++20. And a few additional utility functions and macros. #define voy_fwd(Var) std::forward(Var) #define voy_fwd_invoke(Fn, Var) std::invoke(Fn, voy_fwd(Var)) namespace voy::utils { // Private copy constructor and copy assignment ensure classes derived // from class noncopyable cannot be copied. namespace disable_adl_ { class non_copyable { protected: constexpr non_copyable() = default; ~non_copyable() = default; non_copyable(non_copyable&& other) = default; non_copyable& operator=(non_copyable&& other) = default; private: non_copyable(const non_copyable&) = delete; non_copyable& operator=(const non_copyable&) = delete; }; } using non_copyable = disable_adl_::non_copyable; // Function object that takes a value, and does nothing, // really useful -inline auto do_nothing = [] (auto&& value) -{ +struct do_nothing { + template + void operator() (T&& value) const + { + } }; // Identity function inline auto identity = [] (auto&& value) { return voy_forward(value); }; // Overloaded lambdas template struct overloaded: Fs... { using Fs::operator()...; }; template overloaded(Fs...) -> overloaded; // Execute code when exiting scope template struct on_scope_exit { on_scope_exit(F f) : m_deinit{std::move(f)} { } ~on_scope_exit() { m_deinit(); } F m_deinit; }; } // namespace voy::utils #endif // include guard