diff --git a/phlex/core/CMakeLists.txt b/phlex/core/CMakeLists.txt index dbdb3667d..19cc01f05 100644 --- a/phlex/core/CMakeLists.txt +++ b/phlex/core/CMakeLists.txt @@ -24,7 +24,7 @@ add_library( products_consumer.cpp registrar.cpp registration_api.cpp - specified_label.cpp + product_query.cpp store_counters.cpp ) target_include_directories(phlex_core PRIVATE ${PROJECT_SOURCE_DIR}) diff --git a/phlex/core/declared_fold.cpp b/phlex/core/declared_fold.cpp index 85d1fb1cd..54cd24018 100644 --- a/phlex/core/declared_fold.cpp +++ b/phlex/core/declared_fold.cpp @@ -3,7 +3,7 @@ namespace phlex::experimental { declared_fold::declared_fold(algorithm_name name, std::vector predicates, - specified_labels input_products) : + product_queries input_products) : products_consumer{std::move(name), std::move(predicates), std::move(input_products)} { } diff --git a/phlex/core/declared_fold.hpp b/phlex/core/declared_fold.hpp index 2295ea71e..9461f2ca2 100644 --- a/phlex/core/declared_fold.hpp +++ b/phlex/core/declared_fold.hpp @@ -7,14 +7,14 @@ #include "phlex/core/fwd.hpp" #include "phlex/core/input_arguments.hpp" #include "phlex/core/message.hpp" +#include "phlex/core/product_query.hpp" #include "phlex/core/products_consumer.hpp" -#include "phlex/core/specified_label.hpp" #include "phlex/core/store_counters.hpp" #include "phlex/model/algorithm_name.hpp" #include "phlex/model/handle.hpp" #include "phlex/model/level_id.hpp" +#include "phlex/model/product_specification.hpp" #include "phlex/model/product_store.hpp" -#include "phlex/model/qualified_name.hpp" #include "phlex/utilities/simple_ptr_map.hpp" #include "oneapi/tbb/concurrent_unordered_map.h" @@ -37,12 +37,12 @@ namespace phlex::experimental { public: declared_fold(algorithm_name name, std::vector predicates, - specified_labels input_products); + product_queries input_products); virtual ~declared_fold(); virtual tbb::flow::sender& sender() = 0; virtual tbb::flow::sender& to_output() = 0; - virtual qualified_names const& output() const = 0; + virtual product_specifications const& output() const = 0; virtual std::size_t product_count() const = 0; }; @@ -68,12 +68,12 @@ namespace phlex::experimental { tbb::flow::graph& g, AlgorithmBits alg, InitTuple initializer, - specified_labels product_labels, + product_queries product_labels, std::vector output, std::string partition) : declared_fold{std::move(name), std::move(predicates), std::move(product_labels)}, initializer_{std::move(initializer)}, - output_{to_qualified_names(full_name(), std::move(output))}, + output_{to_product_specifications(full_name(), std::move(output), make_type_ids())}, partition_{std::move(partition)}, join_{make_join_or_none(g, std::make_index_sequence{})}, fold_{g, @@ -121,7 +121,7 @@ namespace phlex::experimental { } private: - tbb::flow::receiver& port_for(specified_label const& product_label) override + tbb::flow::receiver& port_for(product_query const& product_label) override { return receiver_for(join_, input(), product_label); } @@ -130,7 +130,7 @@ namespace phlex::experimental { tbb::flow::sender& sender() override { return output_port<0ull>(fold_); } tbb::flow::sender& to_output() override { return sender(); } - qualified_names const& output() const override { return output_; } + product_specifications const& output() const override { return output_; } template void call(function_t const& ft, messages_t const& messages, std::index_sequence) @@ -175,7 +175,7 @@ namespace phlex::experimental { InitTuple initializer_; input_retriever_types input_{input_arguments()}; - qualified_names output_; + product_specifications output_; std::string partition_; join_or_none_t join_; tbb::flow::multifunction_node, messages_t<1>> fold_; diff --git a/phlex/core/declared_observer.cpp b/phlex/core/declared_observer.cpp index a9c6886fd..47076705b 100644 --- a/phlex/core/declared_observer.cpp +++ b/phlex/core/declared_observer.cpp @@ -6,7 +6,7 @@ namespace phlex::experimental { declared_observer::declared_observer(algorithm_name name, std::vector predicates, - specified_labels input_products) : + product_queries input_products) : products_consumer{std::move(name), std::move(predicates), std::move(input_products)} { } diff --git a/phlex/core/declared_observer.hpp b/phlex/core/declared_observer.hpp index 5488507ca..ad6581267 100644 --- a/phlex/core/declared_observer.hpp +++ b/phlex/core/declared_observer.hpp @@ -5,15 +5,15 @@ #include "phlex/core/fwd.hpp" #include "phlex/core/input_arguments.hpp" #include "phlex/core/message.hpp" +#include "phlex/core/product_query.hpp" #include "phlex/core/products_consumer.hpp" -#include "phlex/core/specified_label.hpp" #include "phlex/core/store_counters.hpp" #include "phlex/metaprogramming/type_deduction.hpp" #include "phlex/model/algorithm_name.hpp" #include "phlex/model/handle.hpp" #include "phlex/model/level_id.hpp" +#include "phlex/model/product_specification.hpp" #include "phlex/model/product_store.hpp" -#include "phlex/model/qualified_name.hpp" #include "phlex/utilities/simple_ptr_map.hpp" #include "oneapi/tbb/concurrent_hash_map.h" @@ -34,7 +34,7 @@ namespace phlex::experimental { public: declared_observer(algorithm_name name, std::vector predicates, - specified_labels input_products); + product_queries input_products); virtual ~declared_observer(); protected: @@ -64,7 +64,7 @@ namespace phlex::experimental { std::vector predicates, tbb::flow::graph& g, AlgorithmBits alg, - specified_labels input_products) : + product_queries input_products) : declared_observer{std::move(name), std::move(predicates), std::move(input_products)}, join_{make_join_or_none(g, std::make_index_sequence{})}, observer_{g, @@ -93,7 +93,7 @@ namespace phlex::experimental { ~observer_node() { report_cached_hashes(cached_hashes_); } private: - tbb::flow::receiver& port_for(specified_label const& product_label) override + tbb::flow::receiver& port_for(product_query const& product_label) override { return receiver_for(join_, input(), product_label); } diff --git a/phlex/core/declared_predicate.cpp b/phlex/core/declared_predicate.cpp index fa3841cbe..e3761cd40 100644 --- a/phlex/core/declared_predicate.cpp +++ b/phlex/core/declared_predicate.cpp @@ -6,7 +6,7 @@ namespace phlex::experimental { declared_predicate::declared_predicate(algorithm_name name, std::vector predicates, - specified_labels input_products) : + product_queries input_products) : products_consumer{std::move(name), std::move(predicates), std::move(input_products)} { } diff --git a/phlex/core/declared_predicate.hpp b/phlex/core/declared_predicate.hpp index d8e6a0c0d..ce43277c6 100644 --- a/phlex/core/declared_predicate.hpp +++ b/phlex/core/declared_predicate.hpp @@ -6,8 +6,8 @@ #include "phlex/core/fwd.hpp" #include "phlex/core/input_arguments.hpp" #include "phlex/core/message.hpp" +#include "phlex/core/product_query.hpp" #include "phlex/core/products_consumer.hpp" -#include "phlex/core/specified_label.hpp" #include "phlex/core/store_counters.hpp" #include "phlex/metaprogramming/type_deduction.hpp" #include "phlex/model/algorithm_name.hpp" @@ -37,7 +37,7 @@ namespace phlex::experimental { public: declared_predicate(algorithm_name name, std::vector predicates, - specified_labels input_products); + product_queries input_products); virtual ~declared_predicate(); virtual tbb::flow::sender& sender() = 0; @@ -70,7 +70,7 @@ namespace phlex::experimental { std::vector predicates, tbb::flow::graph& g, AlgorithmBits alg, - specified_labels input_products) : + product_queries input_products) : declared_predicate{std::move(name), std::move(predicates), std::move(input_products)}, join_{make_join_or_none(g, std::make_index_sequence{})}, predicate_{ @@ -102,7 +102,7 @@ namespace phlex::experimental { ~predicate_node() { report_cached_results(results_); } private: - tbb::flow::receiver& port_for(specified_label const& product_label) override + tbb::flow::receiver& port_for(product_query const& product_label) override { return receiver_for(join_, input(), product_label); } diff --git a/phlex/core/declared_transform.cpp b/phlex/core/declared_transform.cpp index 3a2b23eb9..afb07c3b7 100644 --- a/phlex/core/declared_transform.cpp +++ b/phlex/core/declared_transform.cpp @@ -6,7 +6,7 @@ namespace phlex::experimental { declared_transform::declared_transform(algorithm_name name, std::vector predicates, - specified_labels input_products) : + product_queries input_products) : products_consumer{std::move(name), std::move(predicates), std::move(input_products)} { } @@ -19,6 +19,10 @@ namespace phlex::experimental { spdlog::warn("Transform {} has {} cached stores.", full_name(), stores.size()); } for (auto const& [hash, store] : stores) { + if (not store) { + spdlog::warn("Store with hash {} is null!", hash); + continue; + } spdlog::debug(" => ID: {} (hash: {})", store->id()->to_string(), hash); } } diff --git a/phlex/core/declared_transform.hpp b/phlex/core/declared_transform.hpp index daa654de9..2f3ed8b53 100644 --- a/phlex/core/declared_transform.hpp +++ b/phlex/core/declared_transform.hpp @@ -8,15 +8,15 @@ #include "phlex/core/fwd.hpp" #include "phlex/core/input_arguments.hpp" #include "phlex/core/message.hpp" +#include "phlex/core/product_query.hpp" #include "phlex/core/products_consumer.hpp" -#include "phlex/core/specified_label.hpp" #include "phlex/core/store_counters.hpp" #include "phlex/metaprogramming/type_deduction.hpp" #include "phlex/model/algorithm_name.hpp" #include "phlex/model/handle.hpp" #include "phlex/model/level_id.hpp" +#include "phlex/model/product_specification.hpp" #include "phlex/model/product_store.hpp" -#include "phlex/model/qualified_name.hpp" #include "phlex/utilities/simple_ptr_map.hpp" #include "oneapi/tbb/concurrent_hash_map.h" @@ -41,12 +41,12 @@ namespace phlex::experimental { public: declared_transform(algorithm_name name, std::vector predicates, - specified_labels input_products); + product_queries input_products); virtual ~declared_transform(); virtual tbb::flow::sender& sender() = 0; virtual tbb::flow::sender& to_output() = 0; - virtual qualified_names const& output() const = 0; + virtual product_specifications const& output() const = 0; virtual std::size_t product_count() const = 0; protected: @@ -79,10 +79,11 @@ namespace phlex::experimental { std::vector predicates, tbb::flow::graph& g, AlgorithmBits alg, - specified_labels input_products, + product_queries input_products, std::vector output) : declared_transform{std::move(name), std::move(predicates), std::move(input_products)}, - output_{to_qualified_names(full_name(), std::move(output))}, + output_{to_product_specifications( + full_name(), std::move(output), make_output_type_ids())}, join_{make_join_or_none(g, std::make_index_sequence{})}, transform_{g, concurrency, @@ -126,7 +127,7 @@ namespace phlex::experimental { ~transform_node() { report_cached_stores(stores_); } private: - tbb::flow::receiver& port_for(specified_label const& product_label) override + tbb::flow::receiver& port_for(product_query const& product_label) override { return receiver_for(join_, input(), product_label); } @@ -135,7 +136,7 @@ namespace phlex::experimental { tbb::flow::sender& sender() override { return output_port<0>(transform_); } tbb::flow::sender& to_output() override { return output_port<1>(transform_); } - qualified_names const& output() const override { return output_; } + product_specifications const& output() const override { return output_; } template auto call(function_t const& ft, messages_t const& messages, std::index_sequence) @@ -154,7 +155,7 @@ namespace phlex::experimental { } input_retriever_types input_{input_arguments()}; - qualified_names output_; + product_specifications output_; join_or_none_t join_; tbb::flow::multifunction_node, messages_t<2u>> transform_; stores_t stores_; diff --git a/phlex/core/declared_unfold.cpp b/phlex/core/declared_unfold.cpp index 615ace95c..05ce2579c 100644 --- a/phlex/core/declared_unfold.cpp +++ b/phlex/core/declared_unfold.cpp @@ -35,7 +35,7 @@ namespace phlex::experimental { declared_unfold::declared_unfold(algorithm_name name, std::vector predicates, - specified_labels input_products) : + product_queries input_products) : products_consumer{std::move(name), std::move(predicates), std::move(input_products)} { } @@ -48,6 +48,10 @@ namespace phlex::experimental { spdlog::warn("Unfold {} has {} cached stores.", full_name(), stores.size()); } for (auto const& [hash, store] : stores) { + if (not store) { + spdlog::warn("Store with hash {} is null!", hash); + continue; + } spdlog::debug(" => ID: {} (hash: {})", store->id()->to_string(), hash); } } diff --git a/phlex/core/declared_unfold.hpp b/phlex/core/declared_unfold.hpp index 7b0477baf..4d40b4ec6 100644 --- a/phlex/core/declared_unfold.hpp +++ b/phlex/core/declared_unfold.hpp @@ -11,8 +11,8 @@ #include "phlex/model/algorithm_name.hpp" #include "phlex/model/handle.hpp" #include "phlex/model/level_id.hpp" +#include "phlex/model/product_specification.hpp" #include "phlex/model/product_store.hpp" -#include "phlex/model/qualified_name.hpp" #include "phlex/utilities/simple_ptr_map.hpp" #include "oneapi/tbb/concurrent_hash_map.h" @@ -57,12 +57,12 @@ namespace phlex::experimental { public: declared_unfold(algorithm_name name, std::vector predicates, - specified_labels input_products); + product_queries input_products); virtual ~declared_unfold(); virtual tbb::flow::sender& sender() = 0; virtual tbb::flow::sender& to_output() = 0; - virtual qualified_names const& output() const = 0; + virtual product_specifications const& output() const = 0; virtual std::size_t product_count() const = 0; protected: @@ -91,11 +91,13 @@ namespace phlex::experimental { tbb::flow::graph& g, Predicate&& predicate, Unfold&& unfold, - specified_labels product_labels, + product_queries product_labels, std::vector output_products, std::string new_level_name) : declared_unfold{std::move(name), std::move(predicates), std::move(product_labels)}, - output_{to_qualified_names(full_name(), std::move(output_products))}, + output_{to_product_specifications(full_name(), + std::move(output_products), + make_type_ids>>())}, new_level_name_{std::move(new_level_name)}, join_{make_join_or_none(g, std::make_index_sequence{})}, unfold_{ @@ -129,7 +131,7 @@ namespace phlex::experimental { ~unfold_node() { report_cached_stores(stores_); } private: - tbb::flow::receiver& port_for(specified_label const& product_label) override + tbb::flow::receiver& port_for(product_query const& product_label) override { return receiver_for(join_, input(), product_label); } @@ -137,7 +139,7 @@ namespace phlex::experimental { tbb::flow::sender& sender() override { return output_port<0>(unfold_); } tbb::flow::sender& to_output() override { return sender(); } - qualified_names const& output() const override { return output_; } + product_specifications const& output() const override { return output_; } template void call(Predicate const& predicate, @@ -175,7 +177,7 @@ namespace phlex::experimental { std::size_t product_count() const final { return product_count_.load(); } input_retriever_types input_{input_arguments()}; - qualified_names output_; + product_specifications output_; std::string new_level_name_; join_or_none_t join_; tbb::flow::multifunction_node, messages_t<1u>> unfold_; diff --git a/phlex/core/detail/filter_impl.cpp b/phlex/core/detail/filter_impl.cpp index 4c576e23d..ebf8c6a8e 100644 --- a/phlex/core/detail/filter_impl.cpp +++ b/phlex/core/detail/filter_impl.cpp @@ -4,9 +4,11 @@ #include namespace { - phlex::experimental::specified_label const output_dummy{phlex::experimental::qualified_name{ - phlex::experimental::algorithm_name{"for_output_only", ""}, "for_output_only"}}; - std::vector const for_output_only{output_dummy}; + phlex::experimental::product_query const output_dummy{phlex::experimental::product_specification{ + phlex::experimental::algorithm_name{"for_output_only", ""}, + "for_output_only", + phlex::experimental::type_id{}}}; + std::vector const for_output_only{output_dummy}; } namespace phlex::experimental { @@ -50,7 +52,7 @@ namespace phlex::experimental { void decision_map::erase(accessor& a) { results_.erase(a); } - data_map::data_map(specified_labels const& product_names) : + data_map::data_map(product_queries const& product_names) : product_names_{&product_names}, nargs_{product_names.size()} { assert(nargs_ > 0); diff --git a/phlex/core/detail/filter_impl.hpp b/phlex/core/detail/filter_impl.hpp index 2749a938a..d13884662 100644 --- a/phlex/core/detail/filter_impl.hpp +++ b/phlex/core/detail/filter_impl.hpp @@ -2,7 +2,7 @@ #define PHLEX_CORE_DETAIL_FILTER_IMPL_HPP #include "phlex/core/fwd.hpp" -#include "phlex/core/specified_label.hpp" +#include "phlex/core/product_query.hpp" #include "phlex/model/product_store.hpp" #include "oneapi/tbb/concurrent_hash_map.h" @@ -56,7 +56,7 @@ namespace phlex::experimental { struct for_output_t {}; static constexpr for_output_t for_output{}; explicit data_map(for_output_t); - explicit data_map(specified_labels const& product_names); + explicit data_map(product_queries const& product_names); bool is_complete(std::size_t const msg_id) const; @@ -65,7 +65,7 @@ namespace phlex::experimental { private: stores_t stores_; - std::vector const* product_names_; + std::vector const* product_names_; std::size_t nargs_; }; } diff --git a/phlex/core/edge_creation_policy.cpp b/phlex/core/edge_creation_policy.cpp index 50d6df876..39b9f7f79 100644 --- a/phlex/core/edge_creation_policy.cpp +++ b/phlex/core/edge_creation_policy.cpp @@ -1,37 +1,62 @@ #include "phlex/core/edge_creation_policy.hpp" +#include "fmt/format.h" +#include "fmt/ranges.h" +#include "spdlog/spdlog.h" #include -#include namespace phlex::experimental { edge_creation_policy::named_output_port const* edge_creation_policy::find_producer( - qualified_name const& specified_product_name) const + product_query const& query) const { - auto [b, e] = producers_.equal_range(specified_product_name.name()); + auto const& spec = query.name; + auto [b, e] = producers_.equal_range(spec.name()); if (b == e) { + spdlog::debug("Failed to find {}. Assuming it is provided by the driver", spec.name()); return nullptr; } std::map candidates; for (auto const& [key, producer] : std::ranges::subrange{b, e}) { - if (producer.node.match(specified_product_name.qualifier())) { - candidates.emplace(producer.node.full(), &producer); + if (producer.node.match(spec.qualifier())) { + if (spec.type() != producer.type) { + spdlog::debug("Matched {} ({}) from {} but types don't match (`{}` vs `{}`). Excluding " + "from candidate list.", + spec.full(), + query.to_string(), + producer.node.full(), + spec.type(), + producer.type); + } else { + if (spec.type().exact_compare(producer.type)) { + spdlog::debug("Matched {} ({}) from {} and types match. Keeping in candidate list.", + spec.full(), + query.to_string(), + producer.node.full()); + } else { + spdlog::warn("Matched {} ({}) from {} and types match, but not exactly (produce {} and " + "consume {}). Keeping in candidate list!", + spec.full(), + query.to_string(), + producer.node.full(), + spec.type().exact_name(), + producer.type.exact_name()); + } + candidates.emplace(producer.node.full(), &producer); + } } } if (candidates.empty()) { throw std::runtime_error("Cannot identify product matching the specified label " + - specified_product_name.full()); + spec.full()); } if (candidates.size() > 1ull) { - std::ostringstream msg; - msg << "More than one candidate matches the specified label " << specified_product_name.full() - << ":"; - for (auto const& key : candidates | std::views::keys) { - msg << "\n - " << key; - } - msg << '\n'; - throw std::runtime_error(msg.str()); + std::string msg = + fmt::format("More than one candidate matches the specification {}: \n - {}\n", + spec.full(), + fmt::join(std::views::keys(candidates), "\n - ")); + throw std::runtime_error(msg); } return candidates.begin()->second; diff --git a/phlex/core/edge_creation_policy.hpp b/phlex/core/edge_creation_policy.hpp index d36fccfce..b149e3b6e 100644 --- a/phlex/core/edge_creation_policy.hpp +++ b/phlex/core/edge_creation_policy.hpp @@ -2,7 +2,8 @@ #define PHLEX_CORE_EDGE_CREATION_POLICY_HPP #include "phlex/core/message.hpp" -#include "phlex/model/qualified_name.hpp" +#include "phlex/model/product_specification.hpp" +#include "phlex/model/type_id.hpp" #include "oneapi/tbb/flow_graph.h" @@ -22,9 +23,10 @@ namespace phlex::experimental { algorithm_name node; tbb::flow::sender* port; tbb::flow::sender* to_output; + type_id type; }; - named_output_port const* find_producer(qualified_name const& product_name) const; + named_output_port const* find_producer(product_query const& query) const; auto values() const { return producers_ | std::views::values; } private: @@ -45,8 +47,9 @@ namespace phlex::experimental { for (auto const& product_name : node->output()) { if (empty(product_name.name())) continue; - result.emplace(product_name.name(), - named_output_port{node_name, &node->sender(), &node->to_output()}); + result.emplace( + product_name.name(), + named_output_port{node_name, &node->sender(), &node->to_output(), product_name.type()}); } } return result; diff --git a/phlex/core/edge_maker.hpp b/phlex/core/edge_maker.hpp index 800b53b5d..1f2e6acfd 100644 --- a/phlex/core/edge_maker.hpp +++ b/phlex/core/edge_maker.hpp @@ -57,12 +57,12 @@ namespace phlex::experimental { collector = &coll_it->second.data_port(); } - for (auto const& product_label : node->input()) { - auto* receiver_port = collector ? collector : &node->port(product_label); - auto producer = producers_.find_producer(product_label.name); + for (auto const& query : node->input()) { + auto* receiver_port = collector ? collector : &node->port(query); + auto producer = producers_.find_producer(query); if (not producer) { // Is there a way to detect mis-specified product dependencies? - result[node_name].push_back({product_label, receiver_port}); + result[node_name].push_back({query, receiver_port}); continue; } diff --git a/phlex/core/input_arguments.cpp b/phlex/core/input_arguments.cpp index 9cd21feda..d8d860116 100644 --- a/phlex/core/input_arguments.cpp +++ b/phlex/core/input_arguments.cpp @@ -9,11 +9,11 @@ namespace phlex::experimental::detail { void verify_no_duplicate_input_products(std::string const& algorithm_name, - specified_labels to_sort) + product_queries to_sort) { std::sort(begin(to_sort), end(to_sort)); std::set unique_and_sorted(begin(to_sort), end(to_sort)); - specified_labels duplicates; + product_queries duplicates; std::set_difference(begin(to_sort), end(to_sort), begin(unique_and_sorted), diff --git a/phlex/core/input_arguments.hpp b/phlex/core/input_arguments.hpp index 864364a7b..9e4b7038b 100644 --- a/phlex/core/input_arguments.hpp +++ b/phlex/core/input_arguments.hpp @@ -2,7 +2,7 @@ #define PHLEX_CORE_INPUT_ARGUMENTS_HPP #include "phlex/core/message.hpp" -#include "phlex/core/specified_label.hpp" +#include "phlex/core/product_query.hpp" #include "phlex/model/handle.hpp" #include @@ -15,7 +15,7 @@ namespace phlex::experimental { template struct retriever { using handle_arg_t = detail::handle_value_type; - specified_label label; + product_query label; auto retrieve(auto const& messages) const { return std::get(messages).store->template get_handle( @@ -36,7 +36,7 @@ namespace phlex::experimental { using input_retriever_types = typename input_retriever_types_impl::type; template - auto form_input_arguments_impl(specified_labels const& args, std::index_sequence) + auto form_input_arguments_impl(product_queries const& args, std::index_sequence) { return std::make_tuple( retriever, Is>{std::move(args[Is])}...); @@ -44,11 +44,11 @@ namespace phlex::experimental { namespace detail { void verify_no_duplicate_input_products(std::string const& algorithm_name, - specified_labels to_sort); + product_queries to_sort); } template - auto form_input_arguments(std::string const& algorithm_name, specified_labels const& args) + auto form_input_arguments(std::string const& algorithm_name, product_queries const& args) { constexpr auto N = std::tuple_size_v; detail::verify_no_duplicate_input_products(algorithm_name, args); diff --git a/phlex/core/message.cpp b/phlex/core/message.cpp index a2718c665..456b147de 100644 --- a/phlex/core/message.cpp +++ b/phlex/core/message.cpp @@ -18,8 +18,8 @@ namespace phlex::experimental { return b; } - std::size_t port_index_for(specified_labels const& product_labels, - specified_label const& product_label) + std::size_t port_index_for(product_queries const& product_labels, + product_query const& product_label) { auto const [b, e] = std::tuple{cbegin(product_labels), cend(product_labels)}; auto it = std::find(b, e, product_label); diff --git a/phlex/core/message.hpp b/phlex/core/message.hpp index 6e7d3a3c5..88824b44f 100644 --- a/phlex/core/message.hpp +++ b/phlex/core/message.hpp @@ -2,7 +2,7 @@ #define PHLEX_CORE_MESSAGE_HPP #include "phlex/core/fwd.hpp" -#include "phlex/core/specified_label.hpp" +#include "phlex/core/product_query.hpp" #include "phlex/model/handle.hpp" #include "phlex/model/product_store.hpp" #include "phlex/utilities/sized_tuple.hpp" @@ -67,8 +67,8 @@ namespace phlex::experimental { } } - std::size_t port_index_for(specified_labels const& product_labels, - specified_label const& product_label); + std::size_t port_index_for(product_queries const& product_labels, + product_query const& product_label); template tbb::flow::receiver& receiver_for(detail::join_messages_t& join, @@ -85,8 +85,8 @@ namespace phlex::experimental { template tbb::flow::receiver& receiver_for(join_or_none_t& join, - specified_labels const& product_labels, - specified_label const& product_label) + product_queries const& product_labels, + product_query const& product_label) { if constexpr (N > 1ull) { auto const index = port_index_for(product_labels, product_label); diff --git a/phlex/core/multiplexer.cpp b/phlex/core/multiplexer.cpp index 419f8110f..2ac6f57d9 100644 --- a/phlex/core/multiplexer.cpp +++ b/phlex/core/multiplexer.cpp @@ -14,7 +14,7 @@ using namespace std::chrono; namespace { phlex::experimental::product_store_const_ptr store_for( phlex::experimental::product_store_const_ptr store, - phlex::experimental::specified_label const& label) + phlex::experimental::product_query const& label) { auto const& [product_name, family] = label; if (family.empty()) { diff --git a/phlex/core/multiplexer.hpp b/phlex/core/multiplexer.hpp index 2ba355b0d..c72fd5f38 100644 --- a/phlex/core/multiplexer.hpp +++ b/phlex/core/multiplexer.hpp @@ -23,7 +23,7 @@ namespace phlex::experimental { ~multiplexer(); struct named_input_port { - specified_label product_label; + product_query product_label; tbb::flow::receiver* port; }; using named_input_ports_t = std::vector; diff --git a/phlex/core/product_query.cpp b/phlex/core/product_query.cpp new file mode 100644 index 000000000..48421b88f --- /dev/null +++ b/phlex/core/product_query.cpp @@ -0,0 +1,57 @@ +#include "phlex/core/product_query.hpp" + +#include "fmt/format.h" + +#include +#include +#include + +namespace phlex::experimental { + product_query product_query::operator()(std::string family) && + { + return {std::move(name), std::move(family)}; + } + + std::string product_query::to_string() const + { + if (family.empty()) { + return name.full(); + } + return fmt::format("{} ϵ {}", name.full(), family); + } + + product_query operator""_in(char const* name, std::size_t length) + { + if (length == 0ull) { + throw std::runtime_error("Cannot specify product with empty name."); + } + return product_query::create(name); + } + + bool operator==(product_query const& a, product_query const& b) + { + return std::tie(a.name, a.family) == std::tie(b.name, b.family); + } + + bool operator!=(product_query const& a, product_query const& b) { return !(a == b); } + + bool operator<(product_query const& a, product_query const& b) + { + return std::tie(a.name, a.family) < std::tie(b.name, b.family); + } + + std::ostream& operator<<(std::ostream& os, product_query const& label) + { + os << label.to_string(); + return os; + } + + product_query product_query::create(char const* c) { return create(std::string{c}); } + + product_query product_query::create(std::string const& s) + { + return {product_specification::create(s)}; + } + + product_query product_query::create(product_query l) { return l; } +} diff --git a/phlex/core/product_query.hpp b/phlex/core/product_query.hpp new file mode 100644 index 000000000..026d51a93 --- /dev/null +++ b/phlex/core/product_query.hpp @@ -0,0 +1,86 @@ +#ifndef PHLEX_CORE_SPECIFIED_LABEL_HPP +#define PHLEX_CORE_SPECIFIED_LABEL_HPP + +#include "phlex/model/product_specification.hpp" + +#include +#include +#include +#include +#include + +namespace phlex::experimental { + struct product_query { + product_specification name; + std::string family; + product_query operator()(std::string family) &&; + std::string to_string() const; + + static product_query create(char const* c); + static product_query create(std::string const& s); + static product_query create(product_query l); + }; + + using product_queries = std::vector; + + inline auto& to_name(product_query const& label) { return label.name.name(); } + inline auto& to_family(product_query& label) { return label.family; } + + product_query operator""_in(char const* str, std::size_t); + bool operator==(product_query const& a, product_query const& b); + bool operator!=(product_query const& a, product_query const& b); + bool operator<(product_query const& a, product_query const& b); + std::ostream& operator<<(std::ostream& os, product_query const& label); + + template + concept label_compatible = requires(T t) { + { product_query::create(t) }; + }; + + template + auto to_labels(std::array const& like_labels) + { + std::array labels; + std::ranges::transform( + like_labels, labels.begin(), [](T const& t) { return product_query::create(t); }); + return labels; + } + + namespace detail { + // C is a container of product_queries + template + requires std::is_same_v::value_type, product_query> && + is_tuple::value + class product_queries_type_setter {}; + template + class product_queries_type_setter> { + private: + std::size_t index_ = 0; + + template + void set_type(C& container) + { + container.at(index_).name.set_type(make_type_id()); + ++index_; + } + + public: + void operator()(C& container) + { + assert(container.size() == sizeof...(Ts)); + (set_type(container), ...); + } + }; + } + + template + requires std::is_same_v::value_type, product_query> && + is_tuple::value + void populate_types(C& container) + { + detail::product_queries_type_setter populate_types{}; + populate_types(container); + } +} + +#endif // PHLEX_CORE_SPECIFIED_LABEL_HPP diff --git a/phlex/core/products_consumer.cpp b/phlex/core/products_consumer.cpp index c0ed2b91b..f8dca1854 100644 --- a/phlex/core/products_consumer.cpp +++ b/phlex/core/products_consumer.cpp @@ -4,7 +4,7 @@ namespace phlex::experimental { products_consumer::products_consumer(algorithm_name name, std::vector predicates, - specified_labels input_products) : + product_queries input_products) : consumer{std::move(name), std::move(predicates)}, input_products_{std::move(input_products)} { } @@ -13,10 +13,10 @@ namespace phlex::experimental { std::size_t products_consumer::num_inputs() const { return input().size(); } - tbb::flow::receiver& products_consumer::port(specified_label const& product_label) + tbb::flow::receiver& products_consumer::port(product_query const& product_label) { return port_for(product_label); } - specified_labels const& products_consumer::input() const noexcept { return input_products_; } + product_queries const& products_consumer::input() const noexcept { return input_products_; } } diff --git a/phlex/core/products_consumer.hpp b/phlex/core/products_consumer.hpp index 7c17a4f00..ee7a12532 100644 --- a/phlex/core/products_consumer.hpp +++ b/phlex/core/products_consumer.hpp @@ -5,7 +5,7 @@ #include "phlex/core/fwd.hpp" #include "phlex/core/input_arguments.hpp" #include "phlex/core/message.hpp" -#include "phlex/core/specified_label.hpp" +#include "phlex/core/product_query.hpp" #include "phlex/model/algorithm_name.hpp" #include "oneapi/tbb/flow_graph.h" @@ -18,14 +18,14 @@ namespace phlex::experimental { public: products_consumer(algorithm_name name, std::vector predicates, - specified_labels input_products); + product_queries input_products); virtual ~products_consumer(); std::size_t num_inputs() const; - specified_labels const& input() const noexcept; - tbb::flow::receiver& port(specified_label const& product_label); + product_queries const& input() const noexcept; + tbb::flow::receiver& port(product_query const& product_label); virtual std::vector*> ports() = 0; virtual std::size_t num_calls() const = 0; @@ -38,9 +38,9 @@ namespace phlex::experimental { } private: - virtual tbb::flow::receiver& port_for(specified_label const& product_label) = 0; + virtual tbb::flow::receiver& port_for(product_query const& product_label) = 0; - specified_labels input_products_; + product_queries input_products_; }; } diff --git a/phlex/core/registration_api.hpp b/phlex/core/registration_api.hpp index 86a180f12..2b36a7ad3 100644 --- a/phlex/core/registration_api.hpp +++ b/phlex/core/registration_api.hpp @@ -25,6 +25,7 @@ namespace phlex::experimental { class registration_api { using hof_type = HOF; using NodePtr = typename hof_type::node_ptr_type; + using input_parameter_types = typename AlgorithmBits::input_parameter_types; static constexpr auto N = AlgorithmBits::number_inputs; static constexpr auto M = hof_type::number_output_products; @@ -46,8 +47,9 @@ namespace phlex::experimental { { } - auto input_family(std::array input_args) + auto input_family(std::array input_args) { + populate_types(input_args); if constexpr (M == 0ull) { registrar_.set_creator( [this, inputs = std::move(input_args)](auto predicates, auto /* output_products */) { @@ -85,7 +87,7 @@ namespace phlex::experimental { "The number of function parameters is not the same as the number of specified " "input arguments."); return input_family( - {specified_label::create(std::forward(input_args))...}); + {product_query::create(std::forward(input_args))...}); } private: @@ -116,6 +118,7 @@ namespace phlex::experimental { template class fold_api { using InitTuple = std::tuple; + using input_parameter_types = skip_first_type; static constexpr auto N = AlgorithmBits::number_inputs; static constexpr auto M = 1; // For now @@ -141,8 +144,9 @@ namespace phlex::experimental { { } - auto input_family(std::array input_args) + auto input_family(std::array input_args) { + populate_types(input_args); registrar_.set_creator( [this, inputs = std::move(input_args)](auto predicates, auto output_products) { return std::make_unique>( @@ -171,7 +175,7 @@ namespace phlex::experimental { "The number of function parameters is not the same as the number of specified " "input arguments."); return input_family( - {specified_label::create(std::forward(input_args))...}); + {product_query::create(std::forward(input_args))...}); } private: @@ -222,8 +226,9 @@ namespace phlex::experimental { { } - auto input_family(std::array input_args) + auto input_family(std::array input_args) { + populate_types(input_args); registrar_.set_creator( [this, inputs = std::move(input_args)](auto upstream_predicates, auto output_products) { return std::make_unique>( @@ -245,7 +250,7 @@ namespace phlex::experimental { static_assert(N == sizeof...(input_args), "The number of function parameters is not the same as the number of specified " "input arguments."); - return input_family({specified_label{std::forward(input_args)}...}); + return input_family({product_query{std::forward(input_args)}...}); } private: diff --git a/phlex/core/specified_label.cpp b/phlex/core/specified_label.cpp deleted file mode 100644 index 624b7ef62..000000000 --- a/phlex/core/specified_label.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "phlex/core/specified_label.hpp" - -#include "fmt/format.h" - -#include -#include -#include - -namespace phlex::experimental { - specified_label specified_label::operator()(std::string family) && - { - return {std::move(name), std::move(family)}; - } - - std::string specified_label::to_string() const - { - if (family.empty()) { - return name.full(); - } - return fmt::format("{} ϵ {}", name.full(), family); - } - - specified_label operator""_in(char const* name, std::size_t length) - { - if (length == 0ull) { - throw std::runtime_error("Cannot specify product with empty name."); - } - return specified_label::create(name); - } - - bool operator==(specified_label const& a, specified_label const& b) - { - return std::tie(a.name, a.family) == std::tie(b.name, b.family); - } - - bool operator!=(specified_label const& a, specified_label const& b) { return !(a == b); } - - bool operator<(specified_label const& a, specified_label const& b) - { - return std::tie(a.name, a.family) < std::tie(b.name, b.family); - } - - std::ostream& operator<<(std::ostream& os, specified_label const& label) - { - os << label.to_string(); - return os; - } - - specified_label specified_label::create(char const* c) { return create(std::string{c}); } - - specified_label specified_label::create(std::string const& s) - { - return {qualified_name::create(s)}; - } - - specified_label specified_label::create(specified_label l) { return l; } -} diff --git a/phlex/core/specified_label.hpp b/phlex/core/specified_label.hpp deleted file mode 100644 index c2f06d902..000000000 --- a/phlex/core/specified_label.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef PHLEX_CORE_SPECIFIED_LABEL_HPP -#define PHLEX_CORE_SPECIFIED_LABEL_HPP - -#include "phlex/model/qualified_name.hpp" - -#include -#include -#include -#include -#include - -namespace phlex::experimental { - struct specified_label { - qualified_name name; - std::string family; - specified_label operator()(std::string family) &&; - std::string to_string() const; - - static specified_label create(char const* c); - static specified_label create(std::string const& s); - static specified_label create(specified_label l); - }; - - using specified_labels = std::vector; - - inline auto& to_name(specified_label const& label) { return label.name.name(); } - inline auto& to_family(specified_label& label) { return label.family; } - - specified_label operator""_in(char const* str, std::size_t); - bool operator==(specified_label const& a, specified_label const& b); - bool operator!=(specified_label const& a, specified_label const& b); - bool operator<(specified_label const& a, specified_label const& b); - std::ostream& operator<<(std::ostream& os, specified_label const& label); - - template - concept label_compatible = requires(T t) { - { specified_label::create(t) }; - }; - - template - auto to_labels(std::array const& like_labels) - { - std::array labels; - std::ranges::transform( - like_labels, labels.begin(), [](T const& t) { return specified_label::create(t); }); - return labels; - } - -} - -#endif // PHLEX_CORE_SPECIFIED_LABEL_HPP diff --git a/phlex/metaprogramming/type_deduction.hpp b/phlex/metaprogramming/type_deduction.hpp index 1321fd6e3..0b026e375 100644 --- a/phlex/metaprogramming/type_deduction.hpp +++ b/phlex/metaprogramming/type_deduction.hpp @@ -7,7 +7,10 @@ #include "phlex/metaprogramming/detail/parameter_types.hpp" #include "phlex/metaprogramming/detail/return_type.hpp" +#include +#include #include +#include namespace phlex::experimental { template @@ -33,6 +36,9 @@ namespace phlex::experimental { namespace detail { template std::tuple skip_first_type_impl(std::tuple const&); + + template + std::tuple skip_first_type_impl(std::pair const&); } template @@ -59,6 +65,40 @@ namespace phlex::experimental { template struct is_non_const_lvalue_reference : std::false_type {}; + + template + class remove_atomic { + public: + using type = T; + }; + + template + class remove_atomic> { + public: + using type = T; + }; + + template + using remove_atomic_t = remove_atomic::type; + + template + concept container = requires { + // NB: Just a few basics, not the full set of requirements of the Container named requirement + typename T::iterator; + typename T::value_type; + }; + + template + concept contiguous_container = requires { + requires container; + requires std::contiguous_iterator; + }; + + template + class is_tuple : public std::false_type {}; + + template + class is_tuple> : public std::true_type {}; } #endif // PHLEX_METAPROGRAMMING_TYPE_DEDUCTION_HPP diff --git a/phlex/model/CMakeLists.txt b/phlex/model/CMakeLists.txt index 93c83ae75..9ec2f0745 100644 --- a/phlex/model/CMakeLists.txt +++ b/phlex/model/CMakeLists.txt @@ -7,12 +7,12 @@ add_library( product_matcher.cpp product_store.cpp products.cpp - qualified_name.cpp + product_specification.cpp ) target_include_directories(phlex_model PRIVATE ${PROJECT_SOURCE_DIR}) target_link_libraries( phlex_model - PUBLIC Boost::boost spdlog::spdlog + PUBLIC Boost::boost spdlog::spdlog fmt::fmt PRIVATE phlex::graph phlex::utilities ) diff --git a/phlex/model/product_specification.cpp b/phlex/model/product_specification.cpp new file mode 100644 index 000000000..88f96c025 --- /dev/null +++ b/phlex/model/product_specification.cpp @@ -0,0 +1,78 @@ +#include "phlex/model/product_specification.hpp" +#include "phlex/model/algorithm_name.hpp" + +#include + +namespace phlex::experimental { + product_specification::product_specification() = default; + + product_specification::product_specification(char const* name) : + product_specification{std::string{name}} + { + } + product_specification::product_specification(std::string name) { *this = create(name); } + + product_specification::product_specification(algorithm_name qualifier, + std::string name, + type_id type) : + qualifier_{std::move(qualifier)}, name_{std::move(name)}, type_id_{type} + { + } + + std::string product_specification::full() const + { + auto const qualifier = qualifier_.full(); + if (qualifier.empty()) { + return name_; + } + return qualifier + "/" + name_; + } + + bool product_specification::operator==(product_specification const& other) const + { + return std::tie(qualifier_, name_, type_id_) == + std::tie(other.qualifier_, other.name_, other.type_id_); + } + + bool product_specification::operator!=(product_specification const& other) const + { + return !operator==(other); + } + + bool product_specification::operator<(product_specification const& other) const + { + return std::tie(qualifier_, name_, type_id_) < + std::tie(other.qualifier_, other.name_, type_id_); + } + + product_specification product_specification::create(char const* c) + { + return create(std::string{c}); + } + + product_specification product_specification::create(std::string const& s) + { + auto forward_slash = s.find("/"); + if (forward_slash != std::string::npos) { + return { + algorithm_name::create(s.substr(0, forward_slash)), s.substr(forward_slash + 1), type_id{}}; + } + return {algorithm_name::create(""), s, type_id{}}; + } + + product_specifications to_product_specifications(std::string const& name, + std::vector output_labels, + std::vector output_types) + { + assert(output_labels.size() == output_types.size()); + product_specifications outputs; + outputs.reserve(output_labels.size()); + + to_product_specification make_product_specification{name}; + // zip view isn't available until C++23 so we have to use a loop over the index + for (std::size_t i = 0; i < output_labels.size(); ++i) { + outputs.push_back(make_product_specification(output_labels.at(i), output_types.at(i))); + } + return outputs; + } +} diff --git a/phlex/model/product_specification.hpp b/phlex/model/product_specification.hpp new file mode 100644 index 000000000..da072938c --- /dev/null +++ b/phlex/model/product_specification.hpp @@ -0,0 +1,60 @@ +#ifndef PHLEX_MODEL_PRODUCT_SPECIFICATION_HPP +#define PHLEX_MODEL_PRODUCT_SPECIFICATION_HPP + +#include "phlex/model/algorithm_name.hpp" +#include "phlex/model/type_id.hpp" + +#include +#include +#include + +namespace phlex::experimental { + class product_specification { + public: + product_specification(); + product_specification(char const* name); + product_specification(std::string name); + product_specification(algorithm_name qualifier, std::string name, type_id type); + + std::string full() const; + algorithm_name const& qualifier() const noexcept { return qualifier_; } + std::string const& plugin() const noexcept { return qualifier_.plugin(); } + std::string const& algorithm() const noexcept { return qualifier_.algorithm(); } + std::string const& name() const noexcept { return name_; } + type_id type() const noexcept { return type_id_; } + + void set_type(type_id&& type) { type_id_ = std::move(type); } + + bool operator==(product_specification const& other) const; + bool operator!=(product_specification const& other) const; + bool operator<(product_specification const& other) const; + + static product_specification create(char const* c); + static product_specification create(std::string const& s); + + private: + algorithm_name qualifier_; + std::string name_; + type_id type_id_{}; + }; + + using product_specifications = std::vector; + + class to_product_specification { + public: + explicit to_product_specification(algorithm_name const& qualifier) : qualifier_{qualifier} {} + product_specification operator()(std::string const& name, type_id type) const + { + return product_specification{qualifier_, name, std::move(type)}; + } + + private: + algorithm_name const& qualifier_; + }; + + product_specifications to_product_specifications(std::string const& name, + std::vector output_labels, + std::vector output_types); +} + +#endif // PHLEX_MODEL_PRODUCT_SPECIFICATION_HPP diff --git a/phlex/model/products.hpp b/phlex/model/products.hpp index f57d1513a..f6664f7a9 100644 --- a/phlex/model/products.hpp +++ b/phlex/model/products.hpp @@ -1,7 +1,7 @@ #ifndef PHLEX_MODEL_PRODUCTS_HPP #define PHLEX_MODEL_PRODUCTS_HPP -#include "phlex/model/qualified_name.hpp" +#include "phlex/model/product_specification.hpp" #include #include @@ -53,14 +53,14 @@ namespace phlex::experimental { } template - void add_all(qualified_names const& names, Ts&& ts) + void add_all(product_specifications const& names, Ts&& ts) { assert(names.size() == 1ull); add(names[0].name(), std::forward(ts)); } template - void add_all(qualified_names const& names, std::tuple ts) + void add_all(product_specifications const& names, std::tuple ts) { assert(names.size() == sizeof...(Ts)); [this, &names](auto const& ts, std::index_sequence) { diff --git a/phlex/model/qualified_name.cpp b/phlex/model/qualified_name.cpp deleted file mode 100644 index 5b2934ced..000000000 --- a/phlex/model/qualified_name.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "phlex/model/qualified_name.hpp" -#include "phlex/model/algorithm_name.hpp" - -#include -#include - -namespace phlex::experimental { - qualified_name::qualified_name() = default; - - qualified_name::qualified_name(char const* name) : qualified_name{std::string{name}} {} - qualified_name::qualified_name(std::string name) { *this = create(name); } - - qualified_name::qualified_name(algorithm_name qualifier, std::string name) : - qualifier_{std::move(qualifier)}, name_{std::move(name)} - { - } - - std::string qualified_name::full() const - { - auto const qualifier = qualifier_.full(); - if (qualifier.empty()) { - return name_; - } - return qualifier + "/" + name_; - } - - bool qualified_name::operator==(qualified_name const& other) const - { - return std::tie(qualifier_, name_) == std::tie(other.qualifier_, other.name_); - } - - bool qualified_name::operator!=(qualified_name const& other) const { return !operator==(other); } - - bool qualified_name::operator<(qualified_name const& other) const - { - return std::tie(qualifier_, name_) < std::tie(other.qualifier_, other.name_); - } - - qualified_name qualified_name::create(char const* c) { return create(std::string{c}); } - - qualified_name qualified_name::create(std::string const& s) - { - auto forward_slash = s.find("/"); - if (forward_slash != std::string::npos) { - return {algorithm_name::create(s.substr(0, forward_slash)), s.substr(forward_slash + 1)}; - } - return {algorithm_name::create(""), s}; - } - - qualified_names to_qualified_names(std::string const& name, - std::vector output_labels) - { - qualified_names outputs; - outputs.reserve(output_labels.size()); - std::ranges::transform(output_labels, std::back_inserter(outputs), to_qualified_name{name}); - return outputs; - } -} diff --git a/phlex/model/qualified_name.hpp b/phlex/model/qualified_name.hpp deleted file mode 100644 index 962c54e9e..000000000 --- a/phlex/model/qualified_name.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef PHLEX_MODEL_QUALIFIED_NAME_HPP -#define PHLEX_MODEL_QUALIFIED_NAME_HPP - -#include "phlex/model/algorithm_name.hpp" - -#include -#include - -namespace phlex::experimental { - class qualified_name { - public: - qualified_name(); - qualified_name(char const* name); - qualified_name(std::string name); - qualified_name(algorithm_name qualifier, std::string name); - - std::string full() const; - algorithm_name const& qualifier() const noexcept { return qualifier_; } - std::string const& plugin() const noexcept { return qualifier_.plugin(); } - std::string const& algorithm() const noexcept { return qualifier_.algorithm(); } - std::string const& name() const noexcept { return name_; } - - bool operator==(qualified_name const& other) const; - bool operator!=(qualified_name const& other) const; - bool operator<(qualified_name const& other) const; - - static qualified_name create(char const* c); - static qualified_name create(std::string const& s); - - private: - algorithm_name qualifier_; - std::string name_; - }; - - using qualified_names = std::vector; - - class to_qualified_name { - public: - explicit to_qualified_name(algorithm_name const& qualifier) : qualifier_{qualifier} {} - qualified_name operator()(std::string const& name) const - { - return qualified_name{qualifier_, name}; - } - - private: - algorithm_name const& qualifier_; - }; - - qualified_names to_qualified_names(std::string const& name, - std::vector output_labels); -} - -#endif // PHLEX_MODEL_QUALIFIED_NAME_HPP diff --git a/phlex/model/type_id.hpp b/phlex/model/type_id.hpp new file mode 100644 index 000000000..f74ab9ebd --- /dev/null +++ b/phlex/model/type_id.hpp @@ -0,0 +1,318 @@ +#ifndef PHLEX_MODE_TYPE_ID_HPP +#define PHLEX_MODE_TYPE_ID_HPP + +#include "phlex/metaprogramming/type_deduction.hpp" +#include "phlex/model/handle.hpp" + +#include "fmt/format.h" +#include "fmt/ranges.h" +#include +#include +#include + +#include +#include +#include + +// This is a type_id class to store the "product concept" +// Using our own class means we can treat, for example, all "List"s the same +namespace phlex::experimental { + class type_id { + public: + // Least significant nibble will store the fundamental type + enum class builtin : unsigned char { + void_v = 0, // For completeness + bool_v = 1, + char_v = 2, + int_v = 3, + short_v = 4, + long_v = 5, + long_long_v = 6, + float_v = 7, + double_v = 8, + long_double_v = 9 + }; + + constexpr bool valid() const { return not(id_ == 0xFF); } + + constexpr bool is_unsigned() const { return valid() && (id_ & 0x10); } + + constexpr bool is_list() const { return valid() && (id_ & 0x20); } + + constexpr bool has_children() const { return valid() && (id_ & 0x40); } + + constexpr builtin fundamental() const { return static_cast(id_ & 0x0F); } + + constexpr std::strong_ordering operator<=>(type_id const& rhs) const + { + // This ordering is arbitrary but defined + std::strong_ordering cmp_ids = id_ <=> rhs.id_; + if (cmp_ids == std::strong_ordering::equal) { + if (!this->has_children()) { + return std::strong_ordering::equal; + } + return children_ <=> rhs.children_; + } + return cmp_ids; + } + + constexpr bool operator==(type_id const& rhs) const + { + return (*this <=> rhs) == std::strong_ordering::equal; + }; + + bool exact_compare(type_id const& rhs) const { return *exact_ == *(rhs.exact_); } + + std::string exact_name() const { return boost::core::demangle(exact_->name()); } + + template + friend constexpr type_id make_type_id(); + friend struct fmt::formatter; + + private: + unsigned char id_ = 0xFF; + std::type_info const* exact_{}; // Lifetime of type_info is defined to last until end of program + + // This is used only if the product type is a struct + std::vector children_; + }; + + using type_ids = std::vector; + + namespace detail { + template + consteval unsigned char make_type_id_helper_integral() + { + unsigned char id = 0; + // The following are integral types so we also need to get their signedness + if constexpr (std::is_unsigned_v) { + id = 0x10; + } else { + id = 0x0; + } + using SignedT = std::make_signed_t; + + if constexpr (std::is_same_v) { + // We're choosing to treat signed char and char identically + return id | static_cast(type_id::builtin::char_v); + } else if constexpr (std::is_same_v) { + // ints are generally either long or long long, depending on implementation + // Treating them separately here to reduce confusion + id |= static_cast(type_id::builtin::int_v); + return id; + } else if constexpr (std::is_same_v) { + id |= static_cast(type_id::builtin::short_v); + return id; + } else if constexpr (std::is_same_v) { + id |= static_cast(type_id::builtin::long_v); + return id; + } else if constexpr (std::is_same_v) { + id |= static_cast(type_id::builtin::long_long_v); + return id; + } else { + // If we got here, something went wrong + // This condition is always false, but makes the error message more useful + static_assert(std::is_same_v, "Taking type_id of an unsupported fundamental type"); + } + } + + template + consteval unsigned char make_type_id_helper_fundamental() + { + if constexpr (std::is_same_v) { + return static_cast(type_id::builtin::void_v); + } else if constexpr (std::is_same_v) { + return static_cast(type_id::builtin::bool_v); + } else if constexpr (std::is_same_v) { + return static_cast(type_id::builtin::float_v); + } else if constexpr (std::is_same_v) { + return static_cast(type_id::builtin::double_v); + } else if constexpr (std::is_same_v) { + return static_cast(type_id::builtin::long_double_v); + } else { + return make_type_id_helper_integral(); + } + } + + template + requires(std::is_aggregate_v) + class aggregate_to_plain_tuple { + private: + template + static consteval auto get_tuple(std::index_sequence) -> auto + { + // Atomics are why we can't just use boost::pfr::structure_to_tuple + return std::tuple< + remove_atomic_t>>...>{}; + } + + public: + using type = decltype(get_tuple(std::make_index_sequence>())); + }; + + template + using aggregate_to_plain_tuple_t = aggregate_to_plain_tuple::type; + + template + class is_handle : public std::false_type {}; + + template + class is_handle> : public std::true_type {}; + } + + // Forward declaration + template + type_ids make_type_ids(); + + template + constexpr type_id make_type_id() + { + // First deal with handles + if constexpr (detail::is_handle::value) { + return make_type_id(); + } + + type_id result{}; + using basic = remove_atomic_t>>; + if constexpr (std::is_fundamental_v) { + result.id_ = detail::make_type_id_helper_fundamental(); + } + + // builtin arrays + else if constexpr (std::is_array_v) { + result = make_type_id>(); + result.id_ |= 0x20; + } + + // classes (both containers and "simple" aggregates) + else if constexpr (std::is_class_v) { + if constexpr (contiguous_container) { + result = make_type_id(); + result.id_ |= 0x20; + } else if constexpr (std::is_aggregate_v) { + // This case isn't evaluable at compile time because vector uses operator new + using child_tuple = detail::aggregate_to_plain_tuple_t; + result.id_ = 0x40; // has_children + result.children_ = make_type_ids(); + } else { + // // If we got here, something went wrong + // // This condition is always false, but makes the error message more useful + // static_assert(contiguous_container || std::is_aggregate_v, + // "Taking type_id of an unsupported class type"); + // FIXME + result.id_ = 0xFF; + } + } + + else { + // If we got here, something went wrong + // This condition is always false, but makes the error message more useful + static_assert(std::is_fundamental_v || std::is_array_v || + std::is_class_v, + "Taking type_id of an unsupported type"); + } + + result.exact_ = &typeid(basic); + return result; + } + + namespace detail { + template + class tuple_type_ids { + public: + static type_ids get() { return {make_type_id()}; } + }; + + template + class tuple_type_ids> { + public: + static type_ids get() { return {make_type_id()...}; } + }; + + template + class tuple_type_ids> { + public: + static type_ids get() { return {make_type_id()...}; } + }; + } + + template + type_ids make_type_ids() + { + if constexpr (sizeof...(Ts) == 0) { + return detail::tuple_type_ids::get(); + } else { + return type_ids{make_type_id(), make_type_id()...}; + } + } + + template + type_ids make_output_type_ids() + { + return make_type_ids>(); + } + +} + +template <> +struct fmt::formatter : formatter { + auto format(phlex::experimental::type_id type, format_context& ctx) const + { + using namespace std::string_literals; + using namespace phlex::experimental; + if (!type.valid()) { + return fmt::formatter::format("INVALID / EMPTY"s, ctx); + } + if (type.has_children()) { + std::string const out = fmt::format( + "{}STRUCT {{{}}}", type.is_list() ? "LIST " : "", fmt::join(type.children_, ", ")); + return fmt::formatter::format(out, ctx); + } + + std::string fundamental = "void"s; + switch (type.fundamental()) { + case type_id::builtin::void_v: + fundamental = "void"s; + break; + case type_id::builtin::bool_v: + fundamental = "bool"s; + break; + case type_id::builtin::char_v: + fundamental = "char"s; + break; + case type_id::builtin::int_v: + fundamental = "int"s; + break; + + case type_id::builtin::short_v: + fundamental = "short"s; + break; + + case type_id::builtin::long_v: + fundamental = "long"s; + break; + + case type_id::builtin::long_long_v: + fundamental = "long long"s; + break; + + case type_id::builtin::float_v: + fundamental = "float"s; + break; + + case type_id::builtin::double_v: + fundamental = "double"s; + break; + + case type_id::builtin::long_double_v: + fundamental = "long double"s; + break; + } + std::string const out = fmt::format("{}{}{}", + type.is_list() ? "LIST "s : ""s, + type.is_unsigned() ? "unsigned "s : ""s, + fundamental); + return fmt::formatter::format(out, ctx); + } +}; +#endif // PHLEX_MODE_TYPE_ID_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7ef6f56be..3af3c2e16 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,6 +29,7 @@ endfunction() add_unit_test(concepts LIBRARIES phlex::core) add_unit_test(string_literal LIBRARIES phlex::utilities) add_unit_test(type_deduction LIBRARIES phlex::metaprogramming) +add_unit_test(type_id LIBRARIES phlex::model fmt::fmt) add_unit_test(yielding_driver LIBRARIES phlex::core TBB::tbb) add_catch_test(allowed_families LIBRARIES phlex::core Boost::json) @@ -50,7 +51,8 @@ add_catch_test(product_store LIBRARIES phlex::core) add_catch_test(fold LIBRARIES phlex::core) add_catch_test(replicated LIBRARIES TBB::tbb phlex::utilities spdlog::spdlog) add_catch_test(serializer LIBRARIES phlex::core TBB::tbb) -add_catch_test(specified_label LIBRARIES phlex::core) +add_catch_test(product_query LIBRARIES phlex::core) +add_catch_test(type_distinction LIBRARIES spdlog::spdlog phlex::core) add_catch_test(unfold LIBRARIES Boost::json phlex::core TBB::tbb) add_subdirectory(benchmarks) diff --git a/test/class_registration.cpp b/test/class_registration.cpp index bb537b28c..cd87a68b6 100644 --- a/test/class_registration.cpp +++ b/test/class_registration.cpp @@ -50,7 +50,7 @@ namespace { TEST_CASE("Call non-framework functions", "[programming model]") { std::array const product_names{ - specified_label{"number"}, specified_label{"temperature"}, specified_label{"name"}}; + product_query{"number"}, product_query{"temperature"}, product_query{"name"}}; std::array const oproduct_names{"onumber"s, "otemperature"s, "oname"s}; auto store = product_store::base(); diff --git a/test/function_registration.cpp b/test/function_registration.cpp index 4ec5b59f2..d76c19ecd 100644 --- a/test/function_registration.cpp +++ b/test/function_registration.cpp @@ -49,7 +49,7 @@ namespace { TEST_CASE("Call non-framework functions", "[programming model]") { std::array const product_names{ - specified_label{"number"}, specified_label{"temperature"}, specified_label{"name"}}; + product_query{"number"}, product_query{"temperature"}, product_query{"name"}}; std::array const oproduct_names = {"number"s, "temperature"s, "name"s}; std::array const result{"result"s}; diff --git a/test/specified_label.cpp b/test/product_query.cpp similarity index 75% rename from test/specified_label.cpp rename to test/product_query.cpp index f86e12a45..97fbea257 100644 --- a/test/specified_label.cpp +++ b/test/product_query.cpp @@ -1,4 +1,4 @@ -#include "phlex/core/specified_label.hpp" +#include "phlex/core/product_query.hpp" #include "catch2/catch_test_macros.hpp" @@ -6,14 +6,14 @@ using namespace phlex::experimental; TEST_CASE("Empty label", "[data model]") { - specified_label empty{}; + product_query empty{}; CHECK_THROWS(""_in); CHECK_THROWS(""_in("")); } TEST_CASE("Only name in label", "[data model]") { - specified_label label{"product"}; + product_query label{"product"}; CHECK(label == "product"_in); // Empty family string is interpreted as a wildcard--i.e. any family. @@ -22,6 +22,6 @@ TEST_CASE("Only name in label", "[data model]") TEST_CASE("Label with family", "[data model]") { - specified_label label{"product", {"event"}}; + product_query label{"product", {"event"}}; CHECK(label == "product"_in("event")); } diff --git a/test/type_distinction.cpp b/test/type_distinction.cpp new file mode 100644 index 000000000..3fb29b35c --- /dev/null +++ b/test/type_distinction.cpp @@ -0,0 +1,102 @@ +#include "phlex/core/framework_graph.hpp" +#include "phlex/model/level_hierarchy.hpp" +#include "phlex/model/product_store.hpp" + +#include "spdlog/spdlog.h" + +#include "catch2/catch_test_macros.hpp" + +#include +#include + +using namespace phlex::experimental; + +namespace { + auto add_numbers(int x, int y) { return x + y; } + + auto triple(int x) { return 3 * x; } + + auto square(int x) { return std::tuple{x * x, double((x * x) + 0.5)}; } + + int id(int x) { return x; } + + auto add_vectors(std::vector const& x, std::vector const& y) + { + std::vector res; + std::size_t const len = std::min(x.size(), y.size()); + + res.reserve(len); + for (std::size_t i = 0; i < len; ++i) { + res.push_back(x[i] + y[i]); + } + return res; + } + + auto expand(int x, std::size_t len) { return std::vector(len, x); } +} + +TEST_CASE("Distinguish products with same name and different types", "[programming model]") +{ + + auto gen = [](auto& driver) { + std::vector numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto job_store = product_store::base(); + driver.yield(job_store); + for (int i : numbers) { + auto event_store = job_store->make_child(unsigned(i), "event"); + event_store->add_product("numbers", +i); + event_store->add_product("length", +i); + driver.yield(event_store); + } + }; + + framework_graph g{gen}; + + SECTION("Duplicate product name but differ in producer name") + { + g.observe("starter", [](int num) { spdlog::info("Recieved {}", num); }).input_family("numbers"); + g.transform("triple_numbers", triple, concurrency::unlimited) + .input_family("numbers") + .output_products("tripled"); + spdlog::info("Registered tripled"); + g.transform("expand_orig", expand, concurrency::unlimited) + .input_family("numbers", "length") + .output_products("expanded_one"); + spdlog::info("Registered expanded_one"); + g.transform("expand_triples", expand, concurrency::unlimited) + .input_family("tripled", "length") + .output_products("expanded_three"); + spdlog::info("Registered expanded_three"); + + g.transform("add_nums", add_numbers, concurrency::unlimited) + .input_family("numbers", "tripled") + .output_products("sums"); + spdlog::info("Registered sums"); + + g.transform("add_vect", add_vectors, concurrency::unlimited) + .input_family("expanded_one", "expanded_three") + .output_products("sums"); + + g.transform("test_add_num", triple, concurrency::unlimited) + .input_family("sums") + .output_products("result"); + spdlog::info("Registered result"); + } + + SECTION("Duplicate product name and producer, differ only in type") + { + g.transform("square", square, concurrency::unlimited) + .input_family("numbers") + .output_products("square_result", "square_result"); + + g.transform("extract_result", id, concurrency::unlimited) + .input_family("square_result") + .output_products("result"); + } + + g.observe("print_result", [](int res) { spdlog::info("Result: {}", res); }) + .input_family("result"); + spdlog::info("Registered observe"); + g.execute(); + spdlog::info("Executed"); +} diff --git a/test/type_id.cpp b/test/type_id.cpp new file mode 100644 index 000000000..96891f2d9 --- /dev/null +++ b/test/type_id.cpp @@ -0,0 +1,110 @@ +#include "phlex/model/type_id.hpp" + +#include "fmt/format.h" + +#include +#include +#include +#include +#include + +using namespace phlex::experimental; + +struct A { + int a; + int b; + char c; + + std::atomic d; +}; + +int main() +{ + static_assert(make_type_id().fundamental() == type_id::builtin::bool_v); + static_assert(make_type_id().fundamental() == type_id::builtin::char_v); + static_assert(make_type_id().fundamental() == type_id::builtin::int_v); + static_assert(make_type_id().fundamental() == type_id::builtin::double_v); + static_assert(make_type_id().fundamental() == type_id::builtin::long_v); + static_assert(make_type_id().fundamental() == type_id::builtin::long_long_v); + + static_assert(not make_type_id().is_unsigned()); + static_assert(make_type_id().is_unsigned()); + + static_assert(not make_type_id().is_list()); + static_assert(not make_type_id().is_list()); + static_assert(not make_type_id().is_list()); + + static_assert(make_type_id>().is_list()); + static_assert(make_type_id>().is_list()); + static_assert(make_type_id>().is_list()); + + static_assert(make_type_id>().is_unsigned()); + static_assert(make_type_id>().is_list()); + + static_assert(make_type_id() < make_type_id()); + static_assert(make_type_id() == make_type_id()); + + std::function test_fn = [](int a, float b) -> std::tuple { return {a, b}; }; + type_ids test_fn_out{make_type_id(), make_type_id()}; + assert(make_output_type_ids() == test_fn_out); + + // Now assert all the static_asserts so the coverage script picks them up + assert(make_type_id().fundamental() == type_id::builtin::bool_v); + assert(make_type_id().fundamental() == type_id::builtin::char_v); + assert(make_type_id().fundamental() == type_id::builtin::int_v); + assert(make_type_id().fundamental() == type_id::builtin::double_v); + assert(make_type_id().fundamental() == type_id::builtin::long_v); + assert(make_type_id().fundamental() == type_id::builtin::long_long_v); + + assert(not make_type_id().is_unsigned()); + assert(make_type_id().is_unsigned()); + + assert(not make_type_id().is_list()); + assert(not make_type_id().is_list()); + assert(not make_type_id().is_list()); + + assert(make_type_id>().is_list()); + assert(make_type_id>().is_list()); + assert(make_type_id>().is_list()); + + assert((make_type_id>().is_unsigned())); + assert((make_type_id>().is_list())); + + assert(make_type_id().has_children()); + assert(not make_type_id().has_children()); + assert(make_type_id() > make_type_id()); + assert(make_type_id() < make_type_id()); + assert(make_type_id() == make_type_id()); + + // Print some type IDs + fmt::print("void: {}\n", make_type_id()); + fmt::print("bool: {}\n", make_type_id()); + fmt::print("char: {}\n", make_type_id()); + fmt::print("int: {}\n", make_type_id()); + fmt::print("short: {}\n", make_type_id()); + fmt::print("long: {}\n", make_type_id()); + fmt::print("long long: {}\n", make_type_id()); + fmt::print("float: {}\n", make_type_id()); + fmt::print("double: {}\n", make_type_id()); + fmt::print("long double: {}\n", make_type_id()); + fmt::print("vector: {}\n", make_type_id>()); + fmt::print("vector: {}\n", make_type_id>()); + fmt::print("A: {}\n", make_type_id()); + fmt::print("vector: {}\n", make_type_id>()); + + assert(fmt::format("{}", make_type_id()) == "void"); + assert(fmt::format("{}", make_type_id()) == "bool"); + assert(fmt::format("{}", make_type_id()) == "char"); + assert(fmt::format("{}", make_type_id()) == "int"); + assert(fmt::format("{}", make_type_id()) == "short"); + assert(fmt::format("{}", make_type_id()) == "long"); + assert(fmt::format("{}", make_type_id()) == "long long"); + assert(fmt::format("{}", make_type_id()) == "float"); + assert(fmt::format("{}", make_type_id()) == "double"); + assert(fmt::format("{}", make_type_id()) == "long double"); + assert(fmt::format("{}", make_type_id>()) == "LIST float"); + assert(fmt::format("{}", make_type_id>()) == "LIST unsigned int"); + assert(fmt::format("{}", make_type_id()) == "STRUCT {int, int, char, int}"); + assert(fmt::format("{}", make_type_id>()) == "LIST STRUCT {int, int, char, int}"); + return 0; +}