Skip to content
Open
10 changes: 5 additions & 5 deletions phlex/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ namespace phlex {
product_selector tag_invoke(boost::json::value_to_tag<product_selector> const&,
boost::json::value const& jv)
{
using detail::value_decorate_exception;
using detail::value_if_exists;
auto query_object = jv.as_object();
auto creator = value_decorate_exception<experimental::identifier>(query_object, "creator");
auto layer = value_decorate_exception<experimental::identifier>(query_object, "layer");
auto suffix = detail::value_if_exists(query_object, "suffix");
auto stage = detail::value_if_exists(query_object, "stage");
auto creator = value_if_exists(query_object, "creator");
auto layer = value_if_exists(query_object, "layer");
auto suffix = value_if_exists(query_object, "suffix");
auto stage = value_if_exists(query_object, "stage");
return product_selector{
.creator = std::move(creator), .layer = std::move(layer), .suffix = suffix, .stage = stage};
}
Expand Down
13 changes: 13 additions & 0 deletions phlex/core/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14.1)
set(_std_stacktrace_library stdc++exp)
else()
set(_std_stacktrace_library stdc++_libbacktrace)
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(_std_stacktrace_library stdc++exp)
endif()

cet_make_library(
LIBRARY_NAME
phlex_core
Expand Down Expand Up @@ -34,10 +44,12 @@ cet_make_library(
phlex::metaprogramming
phlex::model
phlex::utilities
${_std_stacktrace_library}
PRIVATE
Boost::json
spdlog::spdlog
)

install(
FILES
concepts.hpp
Expand Down Expand Up @@ -94,6 +106,7 @@ phlex_make_internal_library(
phlex_core
LIBRARIES
PUBLIC TBB::tbb phlex::metaprogramming phlex_model_internal phlex_utilities_internal
${_std_stacktrace_library}
PRIVATE Boost::json spdlog::spdlog
)
add_library(phlex::core_internal ALIAS phlex_core_internal)
Expand Down
3 changes: 2 additions & 1 deletion phlex/core/declared_fold.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ namespace phlex::detail {
std::vector<std::string> predicates,
product_selectors input_products,
std::string partition_layer) :
products_consumer{std::move(name), std::move(predicates), std::move(input_products)},
products_consumer{
std::move(name), std::move(predicates), std::move(input_products), layers_required::always},
partition_layer_{std::move(partition_layer)}
{
}
Expand Down
3 changes: 2 additions & 1 deletion phlex/core/declared_unfold.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ namespace phlex::detail {
std::vector<std::string> predicates,
product_selectors input_products,
std::string child_layer) :
products_consumer{std::move(name), std::move(predicates), std::move(input_products)},
products_consumer{
std::move(name), std::move(predicates), std::move(input_products), layers_required::always},
child_layer_{std::move(child_layer)}
{
}
Expand Down
4 changes: 1 addition & 3 deletions phlex/core/framework_graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,9 @@ namespace phlex::detail {
phlex::experimental::identifier const child_layer{n->child_layer()};
std::set<phlex::experimental::identifier> input_layers_for_unfold;
for (auto const& input : n->input()) {
// Existence of layer already validated
auto const& input_layer =
static_cast<phlex::experimental::identifier const&>(input.layer);
if (input_layer.empty()) {
continue;
}
if (not input_layers_for_unfold.insert(input_layer).second) {
continue;
}
Expand Down
3 changes: 2 additions & 1 deletion phlex/core/index_router.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ namespace phlex::detail {
fold_partition_ports_t fold_partition_ports,
std::map<std::string, named_index_ports> multilayer_join_ports)
{
using namespace phlex::experimental::literals;
// We must have at least one provider port, or there can be no data to process.
assert(!provider_input_ports.empty());

Expand Down Expand Up @@ -197,7 +198,7 @@ namespace phlex::detail {
provider_input_ports_t provider_input_ports)
{
for (auto& [input_product, provider_port] : provider_input_ports | std::views::values) {
auto [it, _] = index_set_nodes_.emplace(input_product.layer,
auto [it, _] = index_set_nodes_.emplace(input_product.layer ? *input_product.layer : "*"_id,
std::make_shared<internal::index_set_node>(g));
make_edge(*it->second, *provider_port);
}
Expand Down
34 changes: 28 additions & 6 deletions phlex/core/make_computational_edges.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,24 @@ namespace phlex::detail {
};
auto proj = [](auto const& pair) -> provider_node* { return pair.second.get(); };

if (auto it = std::ranges::find_if(providers, pred, proj); it != providers.end()) {
return it->second.get();
auto candidates = providers | std::views::transform(proj) | std::views::filter(pred) |
std::ranges::to<std::vector>();
switch (candidates.size()) {
case 0:
return nullptr;
case 1:
return candidates[0];
default:
auto items = candidates | std::views::transform([](provider_node* p) {
return fmt::format("spec: {}, layer: {}, stage: {}",
p->output_product().to_string(),
p->layer(),
p->stage());
});
throw std::runtime_error(fmt::format("Multiple explicit providers found for {}:\n{}",
input_product.to_string(),
bulleted_list(items)));
}
return nullptr;
}

provider_bundles find_matching_implicit_providers(source_map const& sources,
Expand All @@ -52,8 +66,12 @@ namespace phlex::detail {
// output port) and the right family (hidden in the input port).
if (auto* matched_provider = find_matching_provider(explicit_providers, input_product)) {
auto const provider_name = matched_provider->name().to_string();
provider_input_ports.try_emplace(
auto&& [it, _] = provider_input_ports.try_emplace(
provider_name, input_product, matched_provider->input_port());
// Rewrite the stored layer if it's empty
if (!input_product.layer) {
it->second.input_product.layer = matched_provider->layer();
}
spdlog::debug("Connecting provider {} to node {} (product: {})",
provider_name,
node_name,
Expand All @@ -79,7 +97,7 @@ namespace phlex::detail {
for (auto const& [input_product, port] : ports) {
auto existing_provider_it = std::ranges::find_if(
provider_input_ports, [&input_product](auto const& provider_entry) {
return provider_entry.second.input_product == input_product;
return input_product.match(provider_entry.second.input_product);
});

if (existing_provider_it != provider_input_ports.end()) {
Expand Down Expand Up @@ -116,8 +134,12 @@ namespace phlex::detail {
phlex::experimental::identifier{bundle.layer},
phlex::experimental::identifier{bundle.stage});
auto const provider_name = node->name().to_string();
auto [_, inserted] =
auto&& [it, inserted] =
provider_input_ports.try_emplace(provider_name, input_product, node->input_port());
// Rewrite the stored layer if it's empty
if (!input_product.layer) {
it->second.input_product.layer = node->layer();
}
if (!inserted) {
throw std::runtime_error(
fmt::format("Failed to create implicit provider for product selector '{}'\n"
Expand Down
3 changes: 1 addition & 2 deletions phlex/core/multilayer_join_node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ namespace phlex::detail {
name_{std::move(node_name)},
layers_{std::move(layer_names)}
{
assert(NInputs == layers_.size());

using namespace experimental::literals;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// Collapse to the set of distinct layer names. More than one distinct layer means
// at least one input crosses a layer boundary and therefore every input stream
// needs a repeater_node.
Expand Down
14 changes: 5 additions & 9 deletions phlex/core/product_selector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace phlex {
if (creator && creator != other.creator) {
return false;
}
if (layer != other.layer) {
if (layer && layer != other.layer) {
return false;
}
if (suffix && suffix != other.suffix) {
Expand Down Expand Up @@ -50,7 +50,7 @@ namespace phlex {
if (!match(spec)) {
return false;
}
if (experimental::identifier(this->layer) != layer) {
if (this->layer && experimental::identifier(this->layer) != layer) {
Comment thread
beojan marked this conversation as resolved.
return false;
}
if (this->stage && this->stage != stage) {
Expand All @@ -77,7 +77,7 @@ namespace phlex {
suffix.transform(&identifier::operator std::string_view).value_or("[ANY]");
std::string type_str = this->type.valid() ? fmt::format("<{}>", this->type)
: "[UNSET TYPE]"; // will later be concept
auto layer_str = std::string_view(layer);
auto layer_str = layer ? std::string_view(layer) : "[ANY]";
std::string_view creator_str = creator ? std::string_view(*creator) : "[ANY]";
std::string_view stage_str =
stage.transform(&identifier::operator std::string_view).value_or("[ANY]");
Expand All @@ -99,12 +99,8 @@ namespace phlex {
std::strong_ordering product_selector::operator<=>(product_selector const& rhs) const
{
using experimental::identifier;
return std::tie(type, creator, static_cast<identifier const&>(layer), suffix, stage) <=>
std::tie(rhs.type,
rhs.creator,
static_cast<identifier const&>(rhs.layer),
rhs.suffix,
rhs.stage);
return std::tie(type, creator, layer, suffix, stage) <=>
std::tie(rhs.type, rhs.creator, rhs.layer, rhs.suffix, rhs.stage);
}

detail::product_specification const* resolve_in_store(product_selector const& query,
Expand Down
50 changes: 40 additions & 10 deletions phlex/core/product_selector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#include "phlex/model/type_id.hpp"

#include <concepts>
#include <format>
#include <optional>
#include <stacktrace>
#include <string>
#include <tuple>
#include <utility>
Expand All @@ -25,6 +27,12 @@ namespace phlex {
class creator_name {
public:
creator_name() : content_{std::nullopt} {}
creator_name(std::optional<experimental::identifier>&& content) : content_{std::move(content)}
{
if (content_ && content_.value().empty()) {
throw std::runtime_error("Cannot specify product with empty creator name.");
}
}
template <typename U>
requires std::constructible_from<T, U>
// NOLINTNEXTLINE(google-explicit-constructor) - Implicit conversion is intentional
Expand All @@ -42,42 +50,64 @@ namespace phlex {
return me.content_.value_or("[ANY]");
}
bool operator==(creator_name const&) const noexcept = default;
auto operator<=>(creator_name const&) const noexcept = default;

private:
std::optional<experimental::identifier> content_;
};

// The required_layer_name has to be a template for static_assert(false)
template <std::same_as<experimental::identifier> T>
class required_layer_name {
class layer_name {
public:
required_layer_name()
layer_name() : content_(std::nullopt) {}
Comment thread
beojan marked this conversation as resolved.
layer_name(std::optional<experimental::identifier>&& content) : content_{std::move(content)}
{
static_assert(false, "The layer name has not been set in this product_selector.");
if (content_ && content_.value().empty()) {
throw std::runtime_error("Cannot specify the empty string as a data layer.");
}
}
template <typename U>
requires std::constructible_from<T, U>
// NOLINTNEXTLINE(google-explicit-constructor) - Implicit conversion is intentional
required_layer_name(U&& rhs) : content_(std::forward<U>(rhs))
layer_name(U&& rhs) : content_(std::forward<U>(rhs))
{
if (content_.empty()) {
if (content_.value().empty()) {
throw std::runtime_error("Cannot specify the empty string as a data layer.");
}
}

// NOLINTNEXTLINE(google-explicit-constructor) - Implicit conversion is intentional
operator T const&() const noexcept { return content_; }
explicit operator std::string_view() const noexcept { return std::string_view(content_); }
bool operator==(required_layer_name const&) const noexcept = default;
operator T const&() const
{
if (!content_.has_value()) {
throw std::logic_error(
std::format("Cannot retrieve layer from product_selector with no layer\n{}\n",
std::stacktrace::current()));
Comment thread
beojan marked this conversation as resolved.
}
return content_.operator*();
}

experimental::identifier const& operator*() const noexcept { return content_.operator*(); }
explicit operator std::string_view() const noexcept
{
using namespace std::string_view_literals;
return content_
.transform([](experimental::identifier const& id) { return std::string_view(id); })
.value_or(""sv);
}
operator bool() const noexcept { return content_.has_value(); }
bool operator==(layer_name const&) const noexcept = default;
auto operator<=>(layer_name const&) const noexcept = default;

private:
experimental::identifier content_;
std::optional<experimental::identifier> content_;
};
}

struct PHLEX_CORE_EXPORT product_selector {
detail::creator_name<experimental::identifier> creator;
detail::required_layer_name<experimental::identifier> layer;
detail::layer_name<experimental::identifier> layer;
std::optional<experimental::identifier> suffix;
std::optional<experimental::identifier> stage;
detail::type_id type;
Expand Down
34 changes: 32 additions & 2 deletions phlex/core/products_consumer.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
#include "phlex/core/products_consumer.hpp"

#include "fmt/format.h"

namespace {
std::vector<phlex::experimental::identifier> layers_from(phlex::product_selectors const& queries)
{
using namespace phlex::experimental::literals;
std::vector<phlex::experimental::identifier> result;
result.reserve(queries.size());
for (auto const& query : queries) {
result.push_back(query.layer);
if (query.layer) {
result.push_back(query.layer);
} else {
result.push_back("*"_id);
}
}
result.shrink_to_fit();
return result;
}
}
Expand All @@ -16,11 +24,33 @@ namespace phlex::detail {

products_consumer::products_consumer(phlex::experimental::algorithm_name name,
std::vector<std::string> predicates,
product_selectors input_products) :
product_selectors input_products,
layers_required layers_required) :
consumer{std::move(name), std::move(predicates)},
input_products_{std::move(input_products)},
layers_{layers_from(input_products_)}
{
using namespace phlex::experimental::literals;
if (layers_required == layers_required::always ||
(layers_required != layers_required::never && input_products_.size() > 1)) {
Comment thread
beojan marked this conversation as resolved.
std::vector<std::string> err_selectors{};
for (auto const& p : input_products_) {
if (!p.layer) {
err_selectors.push_back(p.to_string());
}
}
if (!err_selectors.empty()) {
std::string type =
layers_required == layers_required::always ? "layer-mandatory" : "multi-input";
std::string error =
fmt::format("Product selectors in {} algorithm {} must define their layers:\n"
" (Only invalid selectors are listed)\n{}",
type,
this->name().to_string(),
bulleted_list(err_selectors));
throw std::runtime_error(error);
}
}
}

products_consumer::~products_consumer() = default;
Expand Down
4 changes: 3 additions & 1 deletion phlex/core/products_consumer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
#include <vector>

namespace phlex::detail {
enum class layers_required : char { never, multi_input_only, always };
class PHLEX_CORE_EXPORT products_consumer : public consumer {
public:
products_consumer(phlex::experimental::algorithm_name name,
std::vector<std::string> predicates,
product_selectors input_products);
product_selectors input_products,
layers_required layers_required = layers_required::multi_input_only);

virtual ~products_consumer();

Expand Down
Loading
Loading