diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index 322cf978..f9bfad77 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -218,7 +218,13 @@ jobs: fi determine-languages: - needs: [pre-check, detect-changes-cpp, detect-changes-python, detect-changes-actions] + needs: + [ + pre-check, + detect-changes-cpp, + detect-changes-python, + detect-changes-actions, + ] if: always() && needs.pre-check.result == 'success' runs-on: ubuntu-latest outputs: diff --git a/form/form_module.cpp b/form/form_module.cpp index 7c884169..f18a5c07 100644 --- a/form/form_module.cpp +++ b/form/form_module.cpp @@ -63,7 +63,7 @@ namespace { auto segment_id = store.index()->to_string(); std::cout << "\n=== FormOutputModule::save_data_products ===\n"; - std::cout << "Creator: " << creator << "\n"; + std::cout << "Creator: " << creator.full() << "\n"; std::cout << "Segment ID: " << segment_id << "\n"; std::cout << "Number of products: " << store.size() << "\n"; @@ -81,10 +81,10 @@ namespace { // product_ptr: pointer to the actual product data assert(product_ptr && "store should not contain null product_ptr"); - std::cout << " Product: " << product_name << "\n"; + std::cout << " Product: " << product_name.full() << "\n"; // Create FORM product with metadata - products.emplace_back(product_name, // label, from map key + products.emplace_back(product_name.name().trans_get_string(), // label, from map key product_ptr->address(), // data, from phlex product_base &product_ptr->type() // type, from phlex product_base ); @@ -95,7 +95,7 @@ namespace { // Write all products to FORM // Pass segment_id once for entire collection (not duplicated in each product) // No need to check if products is empty - already checked store.empty() above - m_form_interface->write(creator, segment_id, products); + m_form_interface->write(creator.full(), segment_id, products); std::cout << "Wrote " << products.size() << " products to FORM\n"; } diff --git a/phlex/core/consumer.cpp b/phlex/core/consumer.cpp index f02a89d5..8aa8bef1 100644 --- a/phlex/core/consumer.cpp +++ b/phlex/core/consumer.cpp @@ -8,8 +8,8 @@ namespace phlex::experimental { std::string consumer::full_name() const { return name_.full(); } - std::string const& consumer::plugin() const noexcept { return name_.plugin(); } - std::string const& consumer::algorithm() const noexcept { return name_.algorithm(); } + identifier const& consumer::plugin() const noexcept { return name_.plugin(); } + identifier const& consumer::algorithm() const noexcept { return name_.algorithm(); } std::vector const& consumer::when() const noexcept { return predicates_; } } diff --git a/phlex/core/consumer.hpp b/phlex/core/consumer.hpp index 11f5205e..fbd38b05 100644 --- a/phlex/core/consumer.hpp +++ b/phlex/core/consumer.hpp @@ -12,8 +12,8 @@ namespace phlex::experimental { consumer(algorithm_name name, std::vector predicates); std::string full_name() const; - std::string const& plugin() const noexcept; - std::string const& algorithm() const noexcept; + identifier const& plugin() const noexcept; + identifier const& algorithm() const noexcept; std::vector const& when() const noexcept; private: diff --git a/phlex/core/declared_fold.hpp b/phlex/core/declared_fold.hpp index 5d7afd86..4a641532 100644 --- a/phlex/core/declared_fold.hpp +++ b/phlex/core/declared_fold.hpp @@ -186,9 +186,9 @@ namespace phlex::experimental { { auto& result = results_.at(store->index()->hash()); if constexpr (requires { send(*result); }) { - store->add_product(output()[0].name(), send(*result)); + store->add_product(output()[0], send(*result)); } else { - store->add_product(output()[0].name(), std::move(result)); + store->add_product(output()[0], std::move(result)); } // Reclaim some memory; it would be better to erase the entire entry from the map, // but that is not thread-safe. @@ -198,7 +198,7 @@ namespace phlex::experimental { InitTuple initializer_; input_retriever_types input_{input_arguments()}; product_specifications output_; - std::string partition_; + identifier partition_; tbb::flow::function_node flush_receiver_; join_or_none_t join_; tbb::flow::multifunction_node, message_tuple<1>> fold_; diff --git a/phlex/core/declared_provider.hpp b/phlex/core/declared_provider.hpp index 9c3228d4..33576de6 100644 --- a/phlex/core/declared_provider.hpp +++ b/phlex/core/declared_provider.hpp @@ -56,7 +56,9 @@ namespace phlex::experimental { AlgorithmBits alg, product_query output) : declared_provider{std::move(name), output}, - output_{output.spec()}, + output_{algorithm_name::create(std::string_view(identifier(output.creator))), + output.suffix.value_or(identifier("")), + output.type}, provider_{g, concurrency, [this, ft = alg.release_algorithm()](index_message const& index_msg, auto& output) { @@ -66,7 +68,7 @@ namespace phlex::experimental { ++calls_; products new_products; - new_products.add(output_.name(), std::move(result)); + new_products.add(output_, std::move(result)); auto store = std::make_shared( index, this->full_name(), std::move(new_products)); diff --git a/phlex/core/declared_unfold.cpp b/phlex/core/declared_unfold.cpp index 27343132..07bc8297 100644 --- a/phlex/core/declared_unfold.cpp +++ b/phlex/core/declared_unfold.cpp @@ -8,7 +8,7 @@ namespace phlex::experimental { generator::generator(product_store_const_ptr const& parent, - std::string node_name, + algorithm_name node_name, std::string const& child_layer_name) : parent_{std::const_pointer_cast(parent)}, node_name_{std::move(node_name)}, diff --git a/phlex/core/declared_unfold.hpp b/phlex/core/declared_unfold.hpp index 6349637c..8d5010cf 100644 --- a/phlex/core/declared_unfold.hpp +++ b/phlex/core/declared_unfold.hpp @@ -10,6 +10,7 @@ #include "phlex/model/algorithm_name.hpp" #include "phlex/model/data_cell_index.hpp" #include "phlex/model/handle.hpp" +#include "phlex/model/identifier.hpp" #include "phlex/model/product_specification.hpp" #include "phlex/model/product_store.hpp" #include "phlex/utilities/simple_ptr_map.hpp" @@ -34,7 +35,7 @@ namespace phlex::experimental { class generator { public: explicit generator(product_store_const_ptr const& parent, - std::string node_name, + algorithm_name node_name, std::string const& child_layer_name); flush_counts_ptr flush_result() const; @@ -47,7 +48,7 @@ namespace phlex::experimental { private: product_store_const_ptr make_child(std::size_t i, products new_products); product_store_ptr parent_; - std::string node_name_; + algorithm_name node_name_; std::string const& child_layer_name_; std::map child_counts_; }; diff --git a/phlex/core/detail/filter_impl.cpp b/phlex/core/detail/filter_impl.cpp index 6a9095ed..ad58daa3 100644 --- a/phlex/core/detail/filter_impl.cpp +++ b/phlex/core/detail/filter_impl.cpp @@ -76,7 +76,7 @@ namespace phlex::experimental { // Fill slots in the order of the input arguments to the downstream node. for (std::size_t i = 0; i != nargs_; ++i) { - if (elem[i] or not store->contains_product((*product_names_)[i].spec().full())) + if (elem[i] or not store->contains_product((*product_names_)[i].spec())) continue; elem[i] = store; } diff --git a/phlex/core/detail/make_algorithm_name.cpp b/phlex/core/detail/make_algorithm_name.cpp index 30c269bc..382aa9e5 100644 --- a/phlex/core/detail/make_algorithm_name.cpp +++ b/phlex/core/detail/make_algorithm_name.cpp @@ -1,10 +1,12 @@ #include "phlex/core/detail/make_algorithm_name.hpp" #include "phlex/configuration.hpp" #include "phlex/model/algorithm_name.hpp" +#include "phlex/model/identifier.hpp" namespace phlex::experimental::detail { - algorithm_name make_algorithm_name(configuration const* config, std::string name) + algorithm_name make_algorithm_name(configuration const* config, std::string_view name) { - return {config ? config->get("module_label") : "", std::move(name)}; + return {config ? identifier(config->get("module_label")) : ""_id, + identifier(name)}; } } diff --git a/phlex/core/detail/make_algorithm_name.hpp b/phlex/core/detail/make_algorithm_name.hpp index 1e86b2dd..2edde613 100644 --- a/phlex/core/detail/make_algorithm_name.hpp +++ b/phlex/core/detail/make_algorithm_name.hpp @@ -4,7 +4,7 @@ // This simple utility is placed in an implementation file to avoid including the // phlex/configuration.hpp in framework code. -#include +#include namespace phlex { class configuration; @@ -14,7 +14,7 @@ namespace phlex::experimental { class algorithm_name; namespace detail { - algorithm_name make_algorithm_name(configuration const* config, std::string name); + algorithm_name make_algorithm_name(configuration const* config, std::string_view name); } } diff --git a/phlex/core/edge_creation_policy.cpp b/phlex/core/edge_creation_policy.cpp index 98fe6954..36abd0d1 100644 --- a/phlex/core/edge_creation_policy.cpp +++ b/phlex/core/edge_creation_policy.cpp @@ -10,7 +10,7 @@ namespace phlex::experimental { product_query const& query) const { // TODO: Update later with correct querying - auto [b, e] = producers_.equal_range(query.suffix.value_or(""_id).trans_get_string()); + auto [b, e] = producers_.equal_range(query.suffix.value_or(""_id)); if (b == e) { spdlog::debug( "Failed to find an algorithm that creates {} products. Assuming it comes from a provider", @@ -19,9 +19,9 @@ namespace phlex::experimental { } std::map candidates; for (auto const& [key, producer] : std::ranges::subrange{b, e}) { - // TODO: Definitely not right yet - if (producer.node.plugin() == std::string_view(identifier(query.creator)) || - producer.node.algorithm() == std::string_view(identifier(query.creator))) { + // TODO: Getting there -- this whole thing needs to be replaced with something + // that indexes all the fields from the beginning. + if (producer.node.plugin() == query.creator || producer.node.algorithm() == query.creator) { if (query.type != producer.type) { spdlog::debug("Matched ({}) from {} but types don't match (`{}` vs `{}`). Excluding " "from candidate list.", diff --git a/phlex/core/edge_creation_policy.hpp b/phlex/core/edge_creation_policy.hpp index 66654497..b6f0783e 100644 --- a/phlex/core/edge_creation_policy.hpp +++ b/phlex/core/edge_creation_policy.hpp @@ -2,6 +2,7 @@ #define PHLEX_CORE_EDGE_CREATION_POLICY_HPP #include "phlex/core/message.hpp" +#include "phlex/model/identifier.hpp" #include "phlex/model/product_specification.hpp" #include "phlex/model/type_id.hpp" @@ -12,7 +13,7 @@ #include namespace phlex::experimental { - using product_name_t = std::string; + using product_name_t = identifier; class edge_creation_policy { public: @@ -44,7 +45,7 @@ namespace phlex::experimental { std::multimap result; for (auto const& [node_name, node] : nodes) { for (auto const& product_name : node->output()) { - if (empty(product_name.name())) + if (product_name.name().empty()) continue; result.emplace(product_name.name(), diff --git a/phlex/core/edge_maker.hpp b/phlex/core/edge_maker.hpp index 07dbef26..fb4bf328 100644 --- a/phlex/core/edge_maker.hpp +++ b/phlex/core/edge_maker.hpp @@ -23,7 +23,7 @@ namespace phlex::experimental { using namespace std::string_literals; - using product_name_t = std::string; + using product_name_t = identifier; index_router::provider_input_ports_t make_provider_edges(index_router::head_ports_t head_ports, declared_providers& providers); diff --git a/phlex/core/index_router.cpp b/phlex/core/index_router.cpp index 2874591a..7d81abb0 100644 --- a/phlex/core/index_router.cpp +++ b/phlex/core/index_router.cpp @@ -89,8 +89,7 @@ namespace phlex::experimental { void detail::multilayer_slot::put_message(data_cell_index_ptr const& index, std::size_t message_id) { - auto const layer = static_cast(layer_); - if (layer == index->layer_name()) { + if (layer_ == index->layer_name()) { broadcaster_.try_put({.index = index, .msg_id = message_id, .cache = false}); return; } @@ -98,7 +97,7 @@ namespace phlex::experimental { // Flush values are only used for indices that are *not* the "lowest" in the branch // of the hierarchy. ++counter_; - broadcaster_.try_put({.index = index->parent(layer), .msg_id = message_id}); + broadcaster_.try_put({.index = index->parent(layer_), .msg_id = message_id}); } void detail::multilayer_slot::put_end_token(data_cell_index_ptr const& index) @@ -119,7 +118,7 @@ namespace phlex::experimental { bool detail::multilayer_slot::is_parent_of(data_cell_index_ptr const& index) const { - return index->parent(static_cast(layer_)) != nullptr; + return index->parent(layer_) != nullptr; } //======================================================================================== @@ -197,7 +196,8 @@ namespace phlex::experimental { return; } - auto broadcaster = index_node_for(index->layer_name()); + std::string const layerish_path{static_cast(index->layer_name())}; + auto broadcaster = index_node_for(layerish_path); if (broadcaster) { broadcaster->try_put({.index = index, .msg_id = message_id}); } diff --git a/phlex/core/input_arguments.hpp b/phlex/core/input_arguments.hpp index 1ad1b5c2..c2286787 100644 --- a/phlex/core/input_arguments.hpp +++ b/phlex/core/input_arguments.hpp @@ -5,7 +5,11 @@ #include "phlex/core/product_query.hpp" #include "phlex/model/handle.hpp" +#include "fmt/format.h" + +#include #include +#include #include #include #include @@ -18,7 +22,30 @@ namespace phlex::experimental { product_query query; auto retrieve(message const& msg) const { - return msg.store->get_handle(query.spec().name()); + namespace views = std::ranges::views; + auto const& store = msg.store; + // TODO: This needs to be replaced with a properly engineered solution + auto all_products = std::ranges::subrange(store->begin(), store->end()) | views::keys; + auto products = + all_products | + views::filter([this](product_specification const& spec) { return query.match(spec); }) | + views::transform([](product_specification const& spec) { return std::cref(spec); }) | + std::ranges::to(); + if (products.empty()) { + throw std::runtime_error(fmt::format( + "No products found matching the query {}\n Store (id {} from {}) contains:\n - {}", + query, + store->index()->to_string(), + store->source().full(), + fmt::join(all_products | views::transform(&product_specification::full), "\n - "))); + } + if (products.size() > 1) { + throw std::runtime_error(fmt::format( + "Multiple products found matching the query {}:\n - {}", + query, + fmt::join(products | views::transform(&product_specification::full), "\n - "))); + } + return store->get_handle(products[0]); } }; diff --git a/phlex/core/message.cpp b/phlex/core/message.cpp index 1378b60f..f0124c95 100644 --- a/phlex/core/message.cpp +++ b/phlex/core/message.cpp @@ -1,6 +1,8 @@ #include "phlex/core/message.hpp" #include "phlex/model/data_cell_index.hpp" +#include "fmt/format.h" + #include #include #include @@ -26,8 +28,8 @@ namespace phlex::experimental { auto const [b, e] = std::tuple{cbegin(product_labels), cend(product_labels)}; auto it = std::find(b, e, product_label); if (it == e) { - throw std::runtime_error("Algorithm does not accept product '" + product_label.spec().name() + - "'."); + throw std::runtime_error( + fmt::format("Algorithm does not accept product '{}'.", product_label)); } return std::distance(b, it); } diff --git a/phlex/core/product_query.cpp b/phlex/core/product_query.cpp index 920724e8..7f5f8f8b 100644 --- a/phlex/core/product_query.cpp +++ b/phlex/core/product_query.cpp @@ -29,15 +29,15 @@ namespace phlex { // Check if a product_specification satisfies this query bool product_query::match(experimental::product_specification const& spec) const { - // string comparisons for now for a gradual transition - if (std::string_view(experimental::identifier(creator)) != spec.algorithm()) { + experimental::identifier tmp_creator{this->creator}; + if (tmp_creator != spec.algorithm() && tmp_creator != spec.plugin()) { return false; } if (type != spec.type()) { return false; } if (suffix) { - if (std::string_view(*suffix) != spec.name()) { + if (*suffix != spec.name()) { return false; } } @@ -58,8 +58,12 @@ namespace phlex { throw std::logic_error("Product suffixes are (temporarily) mandatory"); } // Not efficient, but this should be temporary - return experimental::product_specification::create(suffix->trans_get_string()); + using namespace phlex::experimental; + auto const& creator_identifier = static_cast(creator); + return product_specification{ + algorithm_name::create(static_cast(creator_identifier)), *suffix, type}; } + bool product_query::operator==(product_query const& rhs) const { using experimental::identifier; diff --git a/phlex/core/product_query.hpp b/phlex/core/product_query.hpp index 76f1919d..70540ef3 100644 --- a/phlex/core/product_query.hpp +++ b/phlex/core/product_query.hpp @@ -82,6 +82,7 @@ namespace phlex { experimental::product_specification spec() const; }; + inline std::string format_as(product_query const& q) { return q.to_string(); } using product_queries = std::vector; namespace detail { // C is a container of product_queries diff --git a/phlex/model/algorithm_name.cpp b/phlex/model/algorithm_name.cpp index f4d83fae..09230377 100644 --- a/phlex/model/algorithm_name.cpp +++ b/phlex/model/algorithm_name.cpp @@ -1,34 +1,55 @@ #include "phlex/model/algorithm_name.hpp" +#include "fmt/format.h" + +#include #include -#include -#include namespace { - std::regex const algorithm_name_re{R"((\w+)?(:)?(\w+)?)"}; + // Check if a char is a word (\w) char + // Only care about C locale (ASCII) for now + bool is_word(char c) + { + if ('0' <= c && c <= '9') { + return true; + } + if ('A' <= c && c <= 'Z') { + return true; + } + if ('a' <= c && c <= 'z') { + return true; + } + if (c == '_' || c == '[' || c == ']') { + return true; + } + return false; + } + + // Check that a string_view only contains word chars + bool words_only(std::string_view str) { return std::ranges::all_of(str, is_word); } } namespace phlex::experimental { algorithm_name::algorithm_name() = default; - algorithm_name::algorithm_name(char const* name) : algorithm_name{std::string{name}} {} - algorithm_name::algorithm_name(std::string name) { *this = create(name); } + algorithm_name::algorithm_name(char const* spec) : algorithm_name{std::string_view{spec}} {} + algorithm_name::algorithm_name(std::string const& spec) : algorithm_name{std::string_view{spec}} + { + } + algorithm_name::algorithm_name(std::string_view spec) { *this = create(spec); } - algorithm_name::algorithm_name(std::string plugin, - std::string algorithm, - specified_fields fields) : + algorithm_name::algorithm_name(identifier plugin, identifier algorithm, specified_fields fields) : plugin_{std::move(plugin)}, algorithm_{std::move(algorithm)}, fields_{fields} { } std::string algorithm_name::full() const { - std::string result{plugin_}; - if (not plugin_.empty()) { - result += ":"; + if (!plugin_.empty()) { + return fmt::format("{}:{}", plugin_, algorithm_); } - result += algorithm_; - return result; + // This will stay after trans_get_string is removed + return fmt::format("{}", algorithm_); } bool algorithm_name::match(algorithm_name const& other) const @@ -40,10 +61,6 @@ namespace phlex::experimental { } case specified_fields::either: { // Either the plugin or the algorithm can match - if (other.plugin_.empty()) { - return plugin_ == other.algorithm_ or algorithm_ == other.algorithm_; - } - assert(other.algorithm_.empty()); return other.plugin_ == plugin_ or other.plugin_ == algorithm_; } case specified_fields::both: { @@ -51,31 +68,38 @@ namespace phlex::experimental { return operator==(other); } } - - return false; + return false; // other is an invalid algorithm_name } - algorithm_name algorithm_name::create(char const* spec) { return create(std::string{spec}); } - algorithm_name algorithm_name::create(std::string const& spec) + algorithm_name algorithm_name::create(char const* spec) { return create(std::string_view{spec}); } + algorithm_name algorithm_name::create(std::string_view spec) { - if (std::smatch matches; std::regex_match(spec, matches, algorithm_name_re)) { - assert(matches.size() == 4ull); - // If a colon ":" is specified, then both the plugin and algorithm must be specified. - if (matches[2] == ":") { - if (matches[3].str().empty()) { - throw std::runtime_error("Cannot create an algorithm name that ends with a colon (':')"); - } - return {matches[1], matches[3], specified_fields::both}; + // Empty spec means nothing is specified + if (spec.empty()) { + return {}; + } + + // Do we have a colon? If so, have both plugin and algorithm + if (spec.contains(':')) { + if (spec.ends_with(':')) { + throw std::runtime_error("Cannot create an algorithm name that ends with a colon (':')"); } + auto const colon_pos = spec.find(':'); + std::string_view const plugin = spec.substr(0, colon_pos); + std::string_view const algorithm = spec.substr(colon_pos + 1); // +1 OK, : not at end - // Nothing specified - if (matches[1].str().empty() and matches[3].str().empty()) { - return {}; + if (!words_only(plugin) or !words_only(algorithm)) { + throw std::runtime_error( + fmt::format("The specification '{}' is not a valid algorithm name.", spec)); } + return {identifier(plugin), identifier(algorithm), specified_fields::both}; + } - // Only one word is specified--could be either the plugin or the algorithm - return {matches[1], matches[3], specified_fields::either}; + // No colon -- could be plugin or algorithm + if (!words_only(spec)) { + throw std::runtime_error( + fmt::format("The specification '{}' is not a valid algorithm name.", spec)); } - throw std::runtime_error("The specification '" + spec + "' is not a valid algorithm name."); + return {identifier(spec), identifier(spec), specified_fields::either}; } } diff --git a/phlex/model/algorithm_name.hpp b/phlex/model/algorithm_name.hpp index 4ff1c5d3..13970440 100644 --- a/phlex/model/algorithm_name.hpp +++ b/phlex/model/algorithm_name.hpp @@ -1,35 +1,35 @@ #ifndef PHLEX_MODEL_ALGORITHM_NAME_HPP #define PHLEX_MODEL_ALGORITHM_NAME_HPP -#include +#include "phlex/model/identifier.hpp" namespace phlex::experimental { class algorithm_name { - enum specified_fields { neither, either, both }; + enum class specified_fields { neither, either, both }; public: algorithm_name(); algorithm_name(char const* spec); - algorithm_name(std::string spec); - algorithm_name(std::string plugin, - std::string algorithm, + algorithm_name(std::string const& spec); + algorithm_name(std::string_view spec); + algorithm_name(identifier plugin, + identifier algorithm, specified_fields fields = specified_fields::both); std::string full() const; - std::string const& plugin() const noexcept { return plugin_; } - std::string const& algorithm() const noexcept { return algorithm_; } + identifier const& plugin() const noexcept { return plugin_; } + identifier const& algorithm() const noexcept { return algorithm_; } bool match(algorithm_name const& other) const; auto operator<=>(algorithm_name const&) const = default; static algorithm_name create(char const* spec); - static algorithm_name create(std::string const& spec); + static algorithm_name create(std::string_view spec); private: - auto cmp_tuple() const; - std::string plugin_; - std::string algorithm_; + identifier plugin_; + identifier algorithm_; specified_fields fields_{specified_fields::neither}; }; diff --git a/phlex/model/data_cell_counter.cpp b/phlex/model/data_cell_counter.cpp index f25382d7..a7e51b52 100644 --- a/phlex/model/data_cell_counter.cpp +++ b/phlex/model/data_cell_counter.cpp @@ -1,9 +1,11 @@ #include "phlex/model/data_cell_counter.hpp" +#include "phlex/model/identifier.hpp" #include "phlex/utilities/hashing.hpp" #include namespace phlex::experimental { + using namespace phlex::experimental::literals; flush_counts::flush_counts() = default; @@ -12,10 +14,11 @@ namespace phlex::experimental { { } - data_cell_counter::data_cell_counter() : data_cell_counter{nullptr, "job"} {} + data_cell_counter::data_cell_counter() : data_cell_counter{nullptr, "job"_id} {} - data_cell_counter::data_cell_counter(data_cell_counter* parent, std::string const& layer_name) : - parent_{parent}, layer_hash_{parent_ ? hash(parent->layer_hash_, layer_name) : hash(layer_name)} + data_cell_counter::data_cell_counter(data_cell_counter* parent, identifier const& layer_name) : + parent_{parent}, + layer_hash_{parent_ ? hash(parent->layer_hash_, layer_name.hash()) : layer_name.hash()} { } @@ -26,7 +29,7 @@ namespace phlex::experimental { } } - data_cell_counter data_cell_counter::make_child(std::string const& layer_name) + data_cell_counter data_cell_counter::make_child(identifier const& layer_name) { return {this, layer_name}; } diff --git a/phlex/model/data_cell_counter.hpp b/phlex/model/data_cell_counter.hpp index ff5f0d50..6cbd615c 100644 --- a/phlex/model/data_cell_counter.hpp +++ b/phlex/model/data_cell_counter.hpp @@ -3,6 +3,7 @@ #include "phlex/model/data_cell_index.hpp" #include "phlex/model/fwd.hpp" +#include "phlex/model/identifier.hpp" #include "oneapi/tbb/concurrent_hash_map.h" @@ -36,10 +37,10 @@ namespace phlex::experimental { class data_cell_counter { public: data_cell_counter(); - data_cell_counter(data_cell_counter* parent, std::string const& layer_name); + data_cell_counter(data_cell_counter* parent, identifier const& layer_name); ~data_cell_counter(); - data_cell_counter make_child(std::string const& layer_name); + data_cell_counter make_child(identifier const& layer_name); flush_counts result() const { if (empty(child_counts_)) { diff --git a/phlex/model/data_cell_index.cpp b/phlex/model/data_cell_index.cpp index 42e1cff5..e3c0e31f 100644 --- a/phlex/model/data_cell_index.cpp +++ b/phlex/model/data_cell_index.cpp @@ -36,18 +36,15 @@ namespace { namespace phlex { - data_cell_index::data_cell_index() : - layer_name_{"job"}, layer_hash_{phlex::experimental::hash(layer_name_)} - { - } + data_cell_index::data_cell_index() : layer_name_{"job"}, layer_hash_{layer_name_.hash()} {} data_cell_index::data_cell_index(data_cell_index_ptr parent, std::size_t i, - std::string layer_name) : + experimental::identifier layer_name) : parent_{std::move(parent)}, number_{i}, layer_name_{std::move(layer_name)}, - layer_hash_{phlex::experimental::hash(parent_->layer_hash_, layer_name_)}, + layer_hash_{phlex::experimental::hash(parent_->layer_hash_, layer_name_.hash())}, depth_{parent_->depth_ + 1}, hash_{phlex::experimental::hash(parent_->hash_, number_, layer_hash_)} { @@ -61,14 +58,17 @@ namespace phlex { return base_id; } - std::string const& data_cell_index::layer_name() const noexcept { return layer_name_; } + experimental::identifier const& data_cell_index::layer_name() const noexcept + { + return layer_name_; + } std::string data_cell_index::layer_path() const { - std::vector layers_in_reverse{layer_name_}; + std::vector layers_in_reverse{std::string_view(layer_name_)}; auto next_parent = parent(); while (next_parent) { - layers_in_reverse.push_back(next_parent->layer_name()); + layers_in_reverse.push_back(std::string_view(next_parent->layer_name())); next_parent = next_parent->parent(); } return fmt::format("/{}", fmt::join(std::views::reverse(layers_in_reverse), "/")); @@ -79,8 +79,8 @@ namespace phlex { data_cell_index_ptr data_cell_index::make_child(std::size_t const data_cell_number, std::string child_layer_name) const { - return data_cell_index_ptr{ - new data_cell_index{shared_from_this(), data_cell_number, std::move(child_layer_name)}}; + return data_cell_index_ptr{new data_cell_index{ + shared_from_this(), data_cell_number, experimental::identifier{std::move(child_layer_name)}}}; } bool data_cell_index::has_parent() const noexcept { return static_cast(parent_); } @@ -110,7 +110,7 @@ namespace phlex { data_cell_index_ptr data_cell_index::parent() const noexcept { return parent_; } - data_cell_index_ptr data_cell_index::parent(std::string_view layer_name) const + data_cell_index_ptr data_cell_index::parent(experimental::identifier const& layer_name) const { data_cell_index_ptr parent = parent_; while (parent) { @@ -142,10 +142,10 @@ namespace phlex { std::string data_cell_index::to_string_this_layer() const { - if (empty(layer_name_)) { + if (layer_name_.empty()) { return std::to_string(number_); } - return layer_name_ + ":" + std::to_string(number_); + return fmt::format("{}:{}", layer_name_, number_); } std::ostream& operator<<(std::ostream& os, data_cell_index const& id) diff --git a/phlex/model/data_cell_index.hpp b/phlex/model/data_cell_index.hpp index 3cebad72..d4bdc83f 100644 --- a/phlex/model/data_cell_index.hpp +++ b/phlex/model/data_cell_index.hpp @@ -2,6 +2,7 @@ #define PHLEX_MODEL_DATA_CELL_INDEX_HPP #include "phlex/model/fwd.hpp" +#include "phlex/model/identifier.hpp" #include #include @@ -20,10 +21,10 @@ namespace phlex { using hash_type = std::size_t; data_cell_index_ptr make_child(std::size_t data_cell_number, std::string layer_name) const; - std::string const& layer_name() const noexcept; + experimental::identifier const& layer_name() const noexcept; std::string layer_path() const; std::size_t depth() const noexcept; - data_cell_index_ptr parent(std::string_view layer_name) const; + data_cell_index_ptr parent(experimental::identifier const& layer_name) const; data_cell_index_ptr parent() const noexcept; bool has_parent() const noexcept; std::size_t number() const; @@ -39,10 +40,12 @@ namespace phlex { private: data_cell_index(); - explicit data_cell_index(data_cell_index_ptr parent, std::size_t i, std::string layer_name); + explicit data_cell_index(data_cell_index_ptr parent, + std::size_t i, + experimental::identifier layer_name); data_cell_index_ptr parent_{nullptr}; std::size_t number_{-1ull}; - std::string layer_name_; + experimental::identifier layer_name_; std::size_t layer_hash_; std::size_t depth_{}; hash_type hash_{0}; diff --git a/phlex/model/data_layer_hierarchy.cpp b/phlex/model/data_layer_hierarchy.cpp index e7551e8b..1de22aad 100644 --- a/phlex/model/data_layer_hierarchy.cpp +++ b/phlex/model/data_layer_hierarchy.cpp @@ -104,8 +104,8 @@ namespace phlex::experimental { if (parent_hash == -1ull) { continue; } - auto const& parent_name = layers_.at(parent_hash)->name; - tree[parent_name].emplace_back(layer_entry->name, layer_hash); + auto const& parent_name = fmt::to_string(layers_.at(parent_hash)->name); + tree[parent_name].emplace_back(fmt::to_string(layer_entry->name), layer_hash); } auto const initial_indent = " "; diff --git a/phlex/model/data_layer_hierarchy.hpp b/phlex/model/data_layer_hierarchy.hpp index b9414131..8e03819a 100644 --- a/phlex/model/data_layer_hierarchy.hpp +++ b/phlex/model/data_layer_hierarchy.hpp @@ -31,12 +31,12 @@ namespace phlex::experimental { std::string indent = {}) const; struct layer_entry { - layer_entry(std::string n, std::string path, std::size_t par_hash) : + layer_entry(identifier n, std::string path, std::size_t par_hash) : name{std::move(n)}, layer_path{std::move(path)}, parent_hash{par_hash} { } - std::string name; + identifier name; std::string layer_path; std::size_t parent_hash; std::atomic count{}; diff --git a/phlex/model/identifier.cpp b/phlex/model/identifier.cpp index 1d1c2b2f..5a698fc7 100644 --- a/phlex/model/identifier.cpp +++ b/phlex/model/identifier.cpp @@ -19,6 +19,9 @@ namespace phlex::experimental { } identifier::identifier(std::string_view str) : content_(str), hash_(hash_string(content_)) {} + identifier::identifier(std::string&& str) : content_(std::move(str)), hash_(hash_string(content_)) + { + } identifier::operator std::string_view() const noexcept { return std::string_view(content_); } bool identifier::operator==(identifier const& rhs) const noexcept diff --git a/phlex/model/identifier.hpp b/phlex/model/identifier.hpp index d78c638a..115a6e1e 100644 --- a/phlex/model/identifier.hpp +++ b/phlex/model/identifier.hpp @@ -21,11 +21,21 @@ namespace phlex::experimental { class identifier { public: static std::uint64_t hash_string(std::string_view str); + // The default constructor is necessary so other classes containing identifiers + // can have default constructors. + identifier() = default; identifier(identifier const& other) = default; identifier(identifier&& other) noexcept = default; explicit identifier(std::string_view str); + // This is here to allow the node API which heretofore stored names as strings to be easily transitioned + // over to identifiers + explicit identifier(std::string&& str); + + // char const* calls string_view + explicit identifier(char const* lit) : identifier(std::string_view(lit)) {} + identifier& operator=(identifier const& rhs) = default; identifier& operator=(identifier&& rhs) noexcept = default; @@ -39,6 +49,8 @@ namespace phlex::experimental { // check if empty bool empty() const noexcept { return content_.empty(); } + // get hash + std::size_t hash() const noexcept { return hash_; } // transitional access to contained string std::string const& trans_get_string() const noexcept { return content_; } @@ -50,7 +62,7 @@ namespace phlex::experimental { private: std::string content_; - std::uint64_t hash_; + std::uint64_t hash_{hash_string("")}; }; // Identifier UDL diff --git a/phlex/model/product_specification.cpp b/phlex/model/product_specification.cpp index 93fe1dfe..f386e1fd 100644 --- a/phlex/model/product_specification.cpp +++ b/phlex/model/product_specification.cpp @@ -2,48 +2,53 @@ #include "phlex/model/algorithm_name.hpp" #include +#include namespace phlex::experimental { product_specification::product_specification() = default; product_specification::product_specification(char const* name) : - product_specification{std::string{name}} + product_specification{std::string_view{name}} { } - product_specification::product_specification(std::string name) { *this = create(name); } + product_specification::product_specification(std::string const& name) : + product_specification{std::string_view{name}} + { + } + product_specification::product_specification(std::string_view name) { *this = create(name); } product_specification::product_specification(algorithm_name qualifier, - std::string name, + identifier name, type_id type) : - qualifier_{std::move(qualifier)}, name_{std::move(name)}, type_id_{type} + qualifier_{std::move(qualifier)}, name_{std::move(name)}, type_id_{std::move(type)} { } std::string product_specification::full() const { - auto const qualifier = qualifier_.full(); - if (qualifier.empty()) { - return name_; + if (qualifier_.plugin().empty() && qualifier_.algorithm().empty()) { + return fmt::format("{}", name_); } - return qualifier + "/" + name_; + return fmt::format("{}/{}", qualifier_.full(), name_); } product_specification product_specification::create(char const* c) { - return create(std::string{c}); + return create(std::string_view{c}); } - product_specification product_specification::create(std::string const& s) + product_specification product_specification::create(std::string_view 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{}}; + auto forward_slash = s.find('/'); + if (forward_slash != std::string_view::npos) { + return {algorithm_name::create(s.substr(0, forward_slash)), + identifier(s.substr(forward_slash + 1)), + type_id{}}; } - return {algorithm_name::create(""), s, type_id{}}; + return {algorithm_name::create(""), identifier(s), type_id{}}; } - product_specifications to_product_specifications(std::string const name, + product_specifications to_product_specifications(std::string_view const name, std::vector output_labels, std::vector output_types) { @@ -53,7 +58,8 @@ namespace phlex::experimental { // 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.emplace_back(name, output_labels[i], output_types[i]); + outputs.emplace_back( + algorithm_name::create(name), identifier(output_labels[i]), output_types[i]); } return outputs; } diff --git a/phlex/model/product_specification.hpp b/phlex/model/product_specification.hpp index 18a09e05..5167f474 100644 --- a/phlex/model/product_specification.hpp +++ b/phlex/model/product_specification.hpp @@ -2,8 +2,11 @@ #define PHLEX_MODEL_PRODUCT_SPECIFICATION_HPP #include "phlex/model/algorithm_name.hpp" +#include "phlex/model/identifier.hpp" #include "phlex/model/type_id.hpp" +#include "boost/container_hash/hash.hpp" + #include #include #include @@ -13,14 +16,15 @@ namespace phlex::experimental { public: product_specification(); product_specification(char const* name); - product_specification(std::string name); - product_specification(algorithm_name qualifier, std::string name, type_id type); + product_specification(std::string const& name); + product_specification(std::string_view name); + product_specification(algorithm_name qualifier, identifier 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_; } + identifier const& plugin() const noexcept { return qualifier_.plugin(); } + identifier const& algorithm() const noexcept { return qualifier_.algorithm(); } + identifier 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); } @@ -28,19 +32,32 @@ namespace phlex::experimental { auto operator<=>(product_specification const&) const = default; static product_specification create(char const* c); - static product_specification create(std::string const& s); + static product_specification create(std::string_view s); + + friend struct std::hash; private: algorithm_name qualifier_; - std::string name_; + identifier name_; type_id type_id_{}; }; using product_specifications = std::vector; - product_specifications to_product_specifications(std::string name, + product_specifications to_product_specifications(std::string_view name, std::vector output_labels, std::vector output_types); } +template <> +struct std::hash { + std::size_t operator()(phlex::experimental::product_specification const& spec) const noexcept + { + std::size_t hash = spec.qualifier_.plugin().hash(); + boost::hash_combine(hash, spec.qualifier_.algorithm().hash()); + boost::hash_combine(hash, spec.name_.hash()); + boost::hash_combine(hash, spec.type_id_); + return hash; + } +}; #endif // PHLEX_MODEL_PRODUCT_SPECIFICATION_HPP diff --git a/phlex/model/product_store.cpp b/phlex/model/product_store.cpp index ea43db2d..437c0e0b 100644 --- a/phlex/model/product_store.cpp +++ b/phlex/model/product_store.cpp @@ -6,25 +6,27 @@ namespace phlex::experimental { - product_store::product_store(data_cell_index_ptr id, std::string source, products new_products) : + product_store::product_store(data_cell_index_ptr id, + algorithm_name source, + products new_products) : products_{std::move(new_products)}, id_{std::move(id)}, source_{std::move(source)} { } product_store::~product_store() = default; - product_store_ptr product_store::base(std::string base_name) + product_store_ptr product_store::base(algorithm_name base_name) { return product_store_ptr{new product_store{data_cell_index::base_ptr(), std::move(base_name)}}; } - std::string const& product_store::layer_name() const noexcept { return id_->layer_name(); } - std::string const& product_store::source() const noexcept { return source_; } + identifier const& product_store::layer_name() const noexcept { return id_->layer_name(); } + algorithm_name const& product_store::source() const noexcept { return source_; } data_cell_index_ptr const& product_store::index() const noexcept { return id_; } - bool product_store::contains_product(std::string const& product_name) const + bool product_store::contains_product(product_specification const& key) const { - return products_.contains(product_name); + return products_.contains(key); } product_store_ptr const& more_derived(product_store_ptr const& a, product_store_ptr const& b) @@ -34,4 +36,10 @@ namespace phlex::experimental { } return b; } + + algorithm_name product_store::default_source() + { + static algorithm_name const def = algorithm_name::create("[Source]"); + return def; + } } diff --git a/phlex/model/product_store.hpp b/phlex/model/product_store.hpp index 480ff96a..23199001 100644 --- a/phlex/model/product_store.hpp +++ b/phlex/model/product_store.hpp @@ -1,9 +1,11 @@ #ifndef PHLEX_MODEL_PRODUCT_STORE_HPP #define PHLEX_MODEL_PRODUCT_STORE_HPP +#include "phlex/model/algorithm_name.hpp" #include "phlex/model/data_cell_index.hpp" #include "phlex/model/fwd.hpp" #include "phlex/model/handle.hpp" +#include "phlex/model/identifier.hpp" #include "phlex/model/products.hpp" #include @@ -16,41 +18,44 @@ namespace phlex::experimental { class product_store { public: explicit product_store(data_cell_index_ptr id, - std::string source = "Source", + algorithm_name source = default_source(), products new_products = {}); ~product_store(); - static product_store_ptr base(std::string base_name = "Source"); + static product_store_ptr base(algorithm_name base_name = default_source()); auto begin() const noexcept { return products_.begin(); } auto end() const noexcept { return products_.end(); } auto size() const noexcept { return products_.size(); } bool empty() const noexcept { return products_.empty(); } - std::string const& layer_name() const noexcept; - std::string const& source() const noexcept; + identifier const& layer_name() const noexcept; + algorithm_name const& source() const noexcept; data_cell_index_ptr const& index() const noexcept; // Product interface - bool contains_product(std::string const& key) const; + bool contains_product(product_specification const& key) const; template - T const& get_product(std::string const& key) const; + T const& get_product(product_specification const& key) const; template - handle get_handle(std::string const& key) const; + handle get_handle(product_specification const& key) const; // Thread-unsafe operations template - void add_product(std::string const& key, T&& t); + void add_product(product_specification const& key, T&& t); template - void add_product(std::string const& key, std::unique_ptr>&& t); + void add_product(product_specification const& key, std::unique_ptr>&& t); + + // default Source identifier + static algorithm_name default_source(); private: products products_{}; data_cell_index_ptr id_; - std::string - source_; // FIXME: Should not have to copy the string (the source should outlive the product store) + algorithm_name + source_; // FIXME: Should not have to copy (the source should outlive the product store) }; product_store_ptr const& more_derived(product_store_ptr const& a, product_store_ptr const& b); @@ -78,25 +83,25 @@ namespace phlex::experimental { // Implementation details template - void product_store::add_product(std::string const& key, T&& t) + void product_store::add_product(product_specification const& key, T&& t) { add_product(key, std::make_unique>>(std::forward(t))); } template - void product_store::add_product(std::string const& key, std::unique_ptr>&& t) + void product_store::add_product(product_specification const& key, std::unique_ptr>&& t) { products_.add(key, std::move(t)); } template - [[nodiscard]] handle product_store::get_handle(std::string const& key) const + [[nodiscard]] handle product_store::get_handle(product_specification const& key) const { return handle{products_.get(key), *id_}; } template - [[nodiscard]] T const& product_store::get_product(std::string const& key) const + [[nodiscard]] T const& product_store::get_product(product_specification const& key) const { return *get_handle(key); } diff --git a/phlex/model/products.cpp b/phlex/model/products.cpp index 5bd41f31..8d09062a 100644 --- a/phlex/model/products.cpp +++ b/phlex/model/products.cpp @@ -5,9 +5,9 @@ #include namespace phlex::experimental { - bool products::contains(std::string const& product_name) const + bool products::contains(product_specification const& spec) const { - return products_.contains(product_name); + return products_.contains(spec); } products::const_iterator products::begin() const noexcept { return products_.begin(); } @@ -15,12 +15,15 @@ namespace phlex::experimental { products::size_type products::size() const noexcept { return products_.size(); } bool products::empty() const noexcept { return products_.empty(); } - void products::throw_mismatched_type(std::string const& product_name, + void products::throw_mismatched_type(product_specification const& spec, char const* requested_type, char const* available_type) { - throw std::runtime_error("Cannot get product '" + product_name + "' with type '" + - boost::core::demangle(requested_type) + "' -- must specify type '" + - boost::core::demangle(available_type) + "'."); + std::string const msg = + fmt::format("Cannot get product '{}' with type '{}' -- must specify type '{}'.", + spec.full(), + boost::core::demangle(requested_type), + boost::core::demangle(available_type)); + throw std::runtime_error(msg); } } diff --git a/phlex/model/products.hpp b/phlex/model/products.hpp index 1729427f..d4ca44d9 100644 --- a/phlex/model/products.hpp +++ b/phlex/model/products.hpp @@ -33,30 +33,29 @@ namespace phlex::experimental { }; class products { - using collection_t = std::unordered_map>; + using collection_t = std::unordered_map>; public: using const_iterator = collection_t::const_iterator; using size_type = collection_t::size_type; template - void add(std::string const& product_name, T t) + void add(product_specification const& spec, T t) { - products_.emplace(product_name, - std::make_unique>>(std::move(t))); + products_.emplace(spec, std::make_unique>>(std::move(t))); } template - void add(std::string const& product_name, std::unique_ptr> t) + void add(product_specification const& spec, std::unique_ptr> t) { - products_.emplace(product_name, std::move(t)); + products_.emplace(spec, std::move(t)); } template void add_all(product_specifications const& names, Ts ts) { assert(names.size() == 1ull); - add(names[0].name(), std::move(ts)); + add(names[0], std::move(ts)); } template @@ -64,16 +63,16 @@ namespace phlex::experimental { { assert(names.size() == sizeof...(Ts)); [this, &names](auto tuple, std::index_sequence) { - (this->add(names[Is].name(), std::move(std::get(tuple))), ...); + (this->add(names[Is], std::move(std::get(tuple))), ...); }(std::move(ts), std::index_sequence_for{}); } template - T const& get(std::string const& product_name) const + T const& get(product_specification const& spec) const { - auto it = products_.find(product_name); + auto it = products_.find(spec); if (it == cend(products_)) { - throw std::runtime_error("No product exists with the name '" + product_name + "'."); + throw std::runtime_error(fmt::format("No product exists with the name '{}'.", spec.full())); } auto const* available_product = it->second.get(); @@ -82,17 +81,17 @@ namespace phlex::experimental { return desired_product->obj; } - throw_mismatched_type(product_name, typeid(T).name(), available_product->type().name()); + throw_mismatched_type(spec, typeid(T).name(), available_product->type().name()); } - bool contains(std::string const& product_name) const; + bool contains(product_specification const& spec) const; const_iterator begin() const noexcept; const_iterator end() const noexcept; size_type size() const noexcept; bool empty() const noexcept; private: - static void throw_mismatched_type [[noreturn]] (std::string const& product_name, + static void throw_mismatched_type [[noreturn]] (product_specification const& spec, char const* requested_type, char const* available_type); diff --git a/phlex/model/type_id.hpp b/phlex/model/type_id.hpp index fc13fd9b..9fbc8224 100644 --- a/phlex/model/type_id.hpp +++ b/phlex/model/type_id.hpp @@ -4,12 +4,13 @@ #include "phlex/metaprogramming/type_deduction.hpp" #include "phlex/model/handle.hpp" +#include "boost/container_hash/hash.hpp" +#include "boost/core/demangle.hpp" +#include "boost/hash2/hash_append.hpp" +#include "boost/pfr/core.hpp" + #include "fmt/format.h" #include "fmt/ranges.h" -#include -#include -#include -#include #include #include @@ -81,7 +82,9 @@ namespace phlex::experimental { template friend constexpr type_id make_type_id(); + friend std::size_t hash_value(type_id const& id); friend struct fmt::formatter; + friend struct std::hash; private: unsigned char id_ = 0xFF; @@ -271,6 +274,14 @@ namespace phlex::experimental { return make_type_ids>(); } + inline std::size_t hash_value(type_id const& id) + { + std::size_t hash = std::hash{}(id.id_); + if (id.has_children()) { + boost::hash_combine(hash, id.children_); + } + return hash; + } } template <> @@ -334,4 +345,5 @@ struct fmt::formatter : formatter { return fmt::formatter::format(out, ctx); } }; + #endif // PHLEX_MODEL_TYPE_ID_HPP diff --git a/test/core_misc_test.cpp b/test/core_misc_test.cpp index b6e33c83..4d12a330 100644 --- a/test/core_misc_test.cpp +++ b/test/core_misc_test.cpp @@ -27,8 +27,8 @@ TEST_CASE("algorithm_name tests", "[model]") SECTION("Create from string without colon") { auto an = algorithm_name::create("algo"); - // For 'either' cases, the first word is stored as plugin_ - CHECK(an.full() == "algo:"); + // For 'either' cases, the word is stored as plugin_ and algorithm_ + CHECK(an.full() == "algo:algo"); } SECTION("Create from char pointer") { @@ -62,12 +62,13 @@ TEST_CASE("algorithm_name tests", "[model]") TEST_CASE("consumer tests", "[core]") { using namespace phlex::experimental; + using namespace phlex::experimental::literals; algorithm_name an = algorithm_name::create("p:a"); consumer c(an, {"pred1"}); CHECK(c.full_name() == "p:a"); - CHECK(c.plugin() == "p"); - CHECK(c.algorithm() == "a"); + CHECK(c.plugin() == "p"_idq); + CHECK(c.algorithm() == "a"_idq); CHECK(c.when().size() == 1); } diff --git a/test/data_cell_counting.cpp b/test/data_cell_counting.cpp index 9d396171..486d4e1a 100644 --- a/test/data_cell_counting.cpp +++ b/test/data_cell_counting.cpp @@ -6,10 +6,11 @@ #include "catch2/catch_test_macros.hpp" using namespace phlex::experimental; +using namespace phlex::experimental::literals; using phlex::data_cell_index; namespace { - auto const job_hash_value = hash("job"); + auto const job_hash_value = "job"_idq.hash; } TEST_CASE("Counter with nothing processed", "[data model]") @@ -22,9 +23,9 @@ TEST_CASE("Counter one layer deep", "[data model]") { data_cell_counter job_counter{}; for (std::size_t i = 0; i != 10; ++i) { - job_counter.make_child("event"); + job_counter.make_child("event"_id); } - auto const event_hash_value = hash(job_hash_value, "event"); + auto const event_hash_value = hash(job_hash_value, "event"_idq.hash); CHECK(job_counter.result().count_for(event_hash_value) == 10); } @@ -77,9 +78,9 @@ TEST_CASE("Counter multiple layers deep", "[data model]") CHECK(h.count_for("event", true) == processed_events); }; - auto const run_hash_value = hash(job_hash_value, "run"); - auto const subrun_hash_value = hash(run_hash_value, "subrun"); - auto const event_hash_value = hash(subrun_hash_value, "event"); + auto const run_hash_value = hash(job_hash_value, "run"_idq.hash); + auto const subrun_hash_value = hash(run_hash_value, "subrun"_idq.hash); + auto const event_hash_value = hash(subrun_hash_value, "event"_idq.hash); auto job_index = data_cell_index::base_ptr(); counters.update(job_index); diff --git a/test/data_cell_index.cpp b/test/data_cell_index.cpp index ca3af274..681367d5 100644 --- a/test/data_cell_index.cpp +++ b/test/data_cell_index.cpp @@ -3,6 +3,7 @@ #include "catch2/catch_test_macros.hpp" using namespace phlex; +using namespace phlex::experimental::literals; TEST_CASE("Verify independent hashes", "[data model]") { @@ -57,9 +58,9 @@ TEST_CASE("data_cell_index methods", "[data model]") auto subrun = run0->make_child(5, "subrun"); auto event = subrun->make_child(100, "event"); - CHECK(event->parent("subrun") == subrun); - CHECK(event->parent("run") == run0); - CHECK(event->parent("nonexistent") == nullptr); + CHECK(event->parent("subrun"_id) == subrun); + CHECK(event->parent("run"_id) == run0); + CHECK(event->parent("nonexistent"_id) == nullptr); } SECTION("Layer path") diff --git a/test/filter.cpp b/test/filter.cpp index 51785a59..10702c49 100644 --- a/test/filter.cpp +++ b/test/filter.cpp @@ -5,6 +5,8 @@ #include "catch2/catch_test_macros.hpp" #include "oneapi/tbb/concurrent_vector.h" +#include "spdlog/spdlog.h" + using namespace phlex; using namespace oneapi::tbb; @@ -33,8 +35,16 @@ namespace { unsigned int give_me_other_nums(data_cell_index const& id) { return 100 + id.number() - 1; } - constexpr bool evens_only(unsigned int const value) { return value % 2u == 0u; } - constexpr bool odds_only(unsigned int const value) { return not evens_only(value); } + constexpr bool evens_only(unsigned int const value) + { + spdlog::debug("evens_only: {} is {}", value, value % 2 == 0 ? "even" : "odd"); + return value % 2u == 0u; + } + constexpr bool odds_only(unsigned int const value) + { + spdlog::debug("odds_only: {} is {}", value, value % 2 == 0 ? "even" : "odd"); + return not evens_only(value); + } // Hacky! struct sum_numbers { @@ -61,12 +71,17 @@ namespace { // Hacky! struct check_multiple_numbers { - check_multiple_numbers(int const n) : total{n} {} + check_multiple_numbers(int const n) : total{n} + { + spdlog::debug("construct check_multiple_numbers with n = {}", n); + } ~check_multiple_numbers() { CHECK(std::abs(sum) >= std::abs(total)); } void add_difference(unsigned int const a, unsigned int const b) { // The difference is calculated to test that add(a, b) yields a different result // than add(b, a). + spdlog::debug( + "check_multiple_numbers(n = {}): run add_difference. a = {}, b = {}", total, a, b); sum += static_cast(b) - static_cast(a); } std::atomic sum; @@ -209,7 +224,22 @@ TEST_CASE("Two predicates in parallel (each with multiple arguments)", "[filteri product_query{ .creator = "input"_id, .layer = "event"_id, .suffix = "num"_id}) // <= Note input order .experimental_when("odds_only"); - + g.observe( + "print_evens", + [](unsigned int i, unsigned int j) { spdlog::debug("{} is EVEN, j is {}", i, j); }, + concurrency::unlimited) + .input_family( + product_query{.creator = "input"_id, .layer = "event"_id, .suffix = "num"_id}, + product_query{.creator = "input"_id, .layer = "event"_id, .suffix = "other_num"_id}) + .experimental_when("evens_only"); + g.observe( + "print_odds", + [](unsigned int i, unsigned int j) { spdlog::debug("{} is ODD. j is {}", i, j); }, + concurrency::unlimited) + .input_family( + product_query{.creator = "input"_id, .layer = "event"_id, .suffix = "num"_id}, + product_query{.creator = "input"_id, .layer = "event"_id, .suffix = "other_num"_id}) + .experimental_when("odds_only"); g.execute(); CHECK(g.execution_count("check_odds") == 5); diff --git a/test/filter_impl.cpp b/test/filter_impl.cpp index dbf38048..15118c3a 100644 --- a/test/filter_impl.cpp +++ b/test/filter_impl.cpp @@ -52,10 +52,10 @@ TEST_CASE("Filter data map", "[filtering]") data_map data{data_products_to_cache}; // Stores with the data products "a" and "b" - auto store_with_a = product_store::base("provide_a"); - store_with_a->add_product("a", 1); - auto store_with_b = product_store::base("provide_b"); - store_with_b->add_product("b", 2); + auto store_with_a = product_store::base(algorithm_name::create("input")); + store_with_a->add_product("input/a", 1); + auto store_with_b = product_store::base(algorithm_name::create("input")); + store_with_b->add_product("input/b", 2); std::size_t const msg_id{1}; CHECK(not data.is_complete(msg_id)); diff --git a/test/output_products.cpp b/test/output_products.cpp index a33a952f..007c335f 100644 --- a/test/output_products.cpp +++ b/test/output_products.cpp @@ -24,8 +24,8 @@ namespace { void record(experimental::product_store const& store) { - for (auto const& product_name : store | std::views::keys) { - products_->insert(product_name); + for (auto const& spec : store | std::views::keys) { + products_->insert(spec.full()); } } @@ -65,5 +65,6 @@ TEST_CASE("Output data products", "[graph]") // store from the "provide_number" provider, and once to receive the data store from the // "square_number" transform. CHECK(g.execution_count("record_numbers") == 2u); - CHECK(products_from_nodes == std::set{"number_from_provider", "squared_number"}); + CHECK(products_from_nodes == std::set{"input:input/number_from_provider", + "square_number:square_number/squared_number"}); } diff --git a/test/product_handle.cpp b/test/product_handle.cpp index c080def4..98aa261f 100644 --- a/test/product_handle.cpp +++ b/test/product_handle.cpp @@ -8,6 +8,7 @@ #include using namespace phlex; +using namespace phlex::experimental::literals; namespace { struct Composer { diff --git a/test/products_for_output.hpp b/test/products_for_output.hpp index ffb29dbe..bd833c59 100644 --- a/test/products_for_output.hpp +++ b/test/products_for_output.hpp @@ -13,9 +13,10 @@ namespace phlex::experimental::test { void save(product_store const& store) const { std::ostringstream oss; - oss << "Saving data for store id: " << store.index() << " from source: " << store.source(); - for (auto const& [product_name, _] : store) { - oss << "\n -> Product name: " << product_name; + oss << "Saving data for store id: " << store.index() + << " from source: " << store.source().full(); + for (auto const& [spec, _] : store) { + oss << "\n -> Product spec: " << spec.full(); } spdlog::debug(oss.str()); }