diff --git a/phlex/core/edge_creation_policy.hpp b/phlex/core/edge_creation_policy.hpp index 98e5e426..c5d2d85d 100644 --- a/phlex/core/edge_creation_policy.hpp +++ b/phlex/core/edge_creation_policy.hpp @@ -45,9 +45,6 @@ namespace phlex::experimental { std::multimap result; for (auto const& [node_name, node] : nodes) { for (auto const& product_spec : node->output()) { - if (product_spec.suffix().empty()) - continue; - result.emplace(product_spec.suffix(), named_output_port{node_name, &node->output_port(), product_spec.type()}); } diff --git a/phlex/model/product_specification.cpp b/phlex/model/product_specification.cpp index 6f018ce7..9768beaf 100644 --- a/phlex/model/product_specification.cpp +++ b/phlex/model/product_specification.cpp @@ -2,6 +2,8 @@ #include "phlex/model/algorithm_name.hpp" #include +#include +#include #include namespace phlex::experimental { @@ -48,19 +50,29 @@ namespace phlex::experimental { return {algorithm_name::create(""), identifier(s), type_id{}}; } - product_specifications to_product_specifications(std::string_view const name, + product_specifications to_product_specifications(std::string_view const algorithm_specification, std::vector output_suffixes, std::vector output_types) { - assert(output_suffixes.size() == output_types.size()); - product_specifications outputs; - outputs.reserve(output_suffixes.size()); + static std::string const default_product_suffix = ""; - // 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_suffixes.size(); ++i) { - outputs.emplace_back( - algorithm_name::create(name), identifier(output_suffixes[i]), output_types[i]); + if (output_suffixes.empty()) { + // This happens whenever the user does not specify output_product_suffixes(...), so we + // assign the default suffix to all output data products. + output_suffixes.assign(output_types.size(), default_product_suffix); } - return outputs; + + // If output_suffixes and output_types are non-empty, the framework guarantees that they will + // have the same length. They will either have the same length due to the default-suffix + // assignment above, or because the call to output_product_suffixes(...) demands it. + assert(output_suffixes.size() == output_types.size()); + + // We can use std::views::zip_transform once the AppleClang C++ STL supports it. + auto const algo_name = algorithm_name::create(algorithm_specification); + return std::views::zip(output_suffixes, output_types) | + std::views::transform([&algo_name](auto const& p) { + return product_specification{algo_name, identifier(std::get<0>(p)), std::get<1>(p)}; + }) | + std::ranges::to(); } } diff --git a/phlex/model/product_specification.hpp b/phlex/model/product_specification.hpp index 37f95ce9..1eeb93c8 100644 --- a/phlex/model/product_specification.hpp +++ b/phlex/model/product_specification.hpp @@ -38,7 +38,7 @@ namespace phlex::experimental { private: algorithm_name qualifier_; - identifier suffix_; + identifier suffix_; // Default suffix is empty string type_id type_id_{}; }; diff --git a/test/hierarchical_nodes.cpp b/test/hierarchical_nodes.cpp index 70f87147..025970aa 100644 --- a/test/hierarchical_nodes.cpp +++ b/test/hierarchical_nodes.cpp @@ -101,23 +101,19 @@ TEST_CASE("Hierarchical nodes", "[graph]") g.transform("get_the_time", strtime, concurrency::unlimited) .input_family(product_query{.creator = "input", .layer = "run", .suffix = "time"}) - .experimental_when() - .output_product_suffixes("strtime"); + .experimental_when(); g.transform("square", square, concurrency::unlimited) - .input_family(product_query{.creator = "input", .layer = "event", .suffix = "number"}) - .output_product_suffixes("squared_number"); + .input_family(product_query{.creator = "input", .layer = "event", .suffix = "number"}); g.fold("add", add, concurrency::unlimited, "run", 15u) - .input_family(product_query{.creator = "square", .layer = "event", .suffix = "squared_number"}) - .experimental_when() - .output_product_suffixes("added_data"); + .input_family(product_query{.creator = "square", .layer = "event"}) + .experimental_when(); g.transform("scale", scale, concurrency::unlimited) - .input_family(product_query{.creator = "add", .layer = "run", .suffix = "added_data"}) - .output_product_suffixes("result"); + .input_family(product_query{.creator = "add", .layer = "run"}); g.observe("print_result", print_result, concurrency::unlimited) - .input_family(product_query{.creator = "scale", .layer = "run", .suffix = "result"}, - product_query{.creator = "get_the_time", .layer = "run", .suffix = "strtime"}); + .input_family(product_query{.creator = "scale", .layer = "run"}, + product_query{.creator = "get_the_time", .layer = "run"}); g.make() .output("save", &experimental::test::products_for_output::save) diff --git a/test/provider_test.cpp b/test/provider_test.cpp index fd02eeb7..1c87b38b 100644 --- a/test/provider_test.cpp +++ b/test/provider_test.cpp @@ -43,10 +43,10 @@ TEST_CASE("provider_test") product_query{.creator = "input", .layer = "spill", .suffix = "happy_vertices"}); g.transform("passer", pass_on, concurrency::unlimited) - .input_family(product_query{.creator = "input", .layer = "spill", .suffix = "happy_vertices"}) - .output_product_suffixes("vertex_data"); + .input_family(product_query{.creator = "input", .layer = "spill", .suffix = "happy_vertices"}); g.execute(); + CHECK(g.execution_count("passer") == max_events); CHECK(g.execution_count("my_name_here") == max_events); }