Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"hidden": false,
"cacheVariables": {
"CMAKE_EXPORT_COMPILE_COMMANDS": "YES",
"CMAKE_CXX_STANDARD": "20",
"CMAKE_CXX_STANDARD": "23",
"CMAKE_CXX_STANDARD_REQUIRED": "YES",
"CMAKE_CXX_EXTENSIONS": "NO",
"CMAKE_PROJECT_TOP_LEVEL_INCLUDES": "CetProvideDependency"
Expand Down
59 changes: 56 additions & 3 deletions phlex/configuration.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,55 @@
#include "phlex/configuration.hpp"
#include "phlex/core/product_query.hpp"

#include <ranges>
#include <algorithm>
#include <string>
#include <string_view>

namespace {
std::optional<std::string> value_if_exists(boost::json::object const& obj,
std::string_view parameter)
{
if (!obj.contains(parameter)) {
return std::nullopt;
}
auto const& val = obj.at(parameter);
if (!val.is_string()) {
std::string kind;
switch (val.kind()) {
case boost::json::kind::null:
// seems reasonable to interpret this as if the value were not provided
return std::nullopt;
break;
case boost::json::kind::bool_:
kind = "bool"s;
break;
case boost::json::kind::int64:
kind = "std::int64_t"s;
break;
case boost::json::kind::uint64:
kind = "std::uint64_t"s;
break;
case boost::json::kind::double_:
kind = "double"s;
break;
case boost::json::kind::array:
kind = "array"s;
break;
case boost::json::kind::object:
kind = "object"s;
break;
default:
kind = "HOW??"s;
break;
}
throw std::runtime_error(
fmt::format("Error retrieving parameter '{}'. Should be a string but is instead a {}",
parameter,
kind));
}
return std::string(val.get_string());
}
}

namespace phlex {
std::vector<std::string> configuration::keys() const
Expand All @@ -25,7 +72,13 @@ namespace phlex {
{
using detail::value_decorate_exception;
auto query_object = jv.as_object();
return product_query{{value_decorate_exception<std::string>(query_object, "product")},
value_decorate_exception<std::string>(query_object, "layer")};
auto creator = value_decorate_exception<std::string>(query_object, "creator");
auto layer = value_decorate_exception<std::string>(query_object, "layer");
auto suffix = value_if_exists(query_object, "suffix");
auto stage = value_if_exists(query_object, "stage");
return product_tag{.creator = std::move(creator),
.layer = std::move(layer),
.suffix = std::move(suffix),
.stage = std::move(stage)};
}
}
8 changes: 2 additions & 6 deletions phlex/core/detail/filter_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,8 @@
#include <string>

namespace {
phlex::product_query const output_dummy{
phlex::experimental::product_specification{
phlex::experimental::algorithm_name{"for_output_only", ""},
"for_output_only",
phlex::experimental::type_id{}},
"dummy_layer"};
phlex::product_query const output_dummy = phlex::product_tag{
.creator = "for_output_only"s, .layer = "dummy_layer"s, .suffix = "for_output_only"s};
phlex::product_queries const for_output_only{output_dummy};
}

Expand Down
40 changes: 20 additions & 20 deletions phlex/core/edge_creation_policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,55 +9,55 @@ namespace phlex::experimental {
edge_creation_policy::named_output_port const* edge_creation_policy::find_producer(
product_query const& query) const
{
auto const& spec = query.spec();
auto [b, e] = producers_.equal_range(spec.name());
// TODO: Update later with correct querying
auto [b, e] = producers_.equal_range(query.suffix().value_or(""));
if (b == e) {
spdlog::debug(
"Failed to find an algorithm that creates {} products. Assuming it comes from a provider",
spec.name());
query.suffix().value_or("\"\""));
return nullptr;
}
std::map<std::string, named_output_port const*> candidates;
for (auto const& [key, producer] : std::ranges::subrange{b, e}) {
if (producer.node.match(spec.qualifier())) {
if (spec.type() != producer.type) {
spdlog::debug("Matched {} ({}) from {} but types don't match (`{}` vs `{}`). Excluding "
// TODO: Definitely not right yet
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.",
spec.full(),
query.to_string(),
producer.node.full(),
spec.type(),
query.type(),
producer.type);
} else {
if (spec.type().exact_compare(producer.type)) {
spdlog::debug("Matched {} ({}) from {} and types match. Keeping in candidate list.",
spec.full(),
if (query.type().exact_compare(producer.type)) {
spdlog::debug("Matched ({}) from {} and types match. Keeping in candidate list.",
query.to_string(),
producer.node.full());
} else {
spdlog::warn("Matched {} ({}) from {} and types match, but not exactly (produce {} and "
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(),
query.type().exact_name(),
producer.type.exact_name());
}
candidates.emplace(producer.node.full(), &producer);
}
} else {
spdlog::error(
"Creator name mismatch between ({}) and {}", query.to_string(), producer.node.full());
}
}

if (candidates.empty()) {
throw std::runtime_error("Cannot identify product matching the specified label " +
spec.full());
throw std::runtime_error("Cannot identify product matching the query " + query.to_string());
}

if (candidates.size() > 1ull) {
std::string msg =
fmt::format("More than one candidate matches the specification {}: \n - {}\n",
spec.full(),
fmt::join(std::views::keys(candidates), "\n - "));
std::string msg = fmt::format("More than one candidate matches the query {}: \n - {}\n",
query.to_string(),
fmt::join(std::views::keys(candidates), "\n - "));
throw std::runtime_error(msg);
}

Expand Down
32 changes: 11 additions & 21 deletions phlex/core/input_arguments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,22 @@

#include "fmt/format.h"

#include <algorithm>
#include <set>
#include <stdexcept>
#include <vector>

namespace phlex::experimental::detail {
void verify_no_duplicate_input_products(std::string const& algorithm_name,
product_queries to_sort)
product_queries input_queries)
{
std::sort(begin(to_sort), end(to_sort));
std::set unique_and_sorted(begin(to_sort), end(to_sort));
product_queries duplicates;
std::set_difference(begin(to_sort),
end(to_sort),
begin(unique_and_sorted),
end(unique_and_sorted),
std::back_inserter(duplicates));
if (empty(duplicates)) {
return;
for (std::size_t i = 0; i < input_queries.size(); ++i) {
for (std::size_t j = i + 1; j < input_queries.size(); ++j) {
if (input_queries[i].match(input_queries[j]) || input_queries[j].match(input_queries[i])) {
throw std::runtime_error(
fmt::format("The algorithm {} has at least one duplicate input:\n- {}\n- {}\n",
algorithm_name,
input_queries[i].to_string(),
input_queries[j].to_string()));
}
}
}

std::string error =
fmt::format("Algorithm '{}' uses the following products more than once:\n", algorithm_name);
for (auto const& label : duplicates) {
error += fmt::format(" - '{}'\n", label.to_string());
}
throw std::runtime_error(error);
}
}
56 changes: 40 additions & 16 deletions phlex/core/product_query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,61 @@

#include "fmt/format.h"

#include <stdexcept>

namespace phlex {
product_query::product_query() = default;
void product_query::set_type(experimental::type_id&& type) { type_id_ = std::move(type); }

product_query::product_query(experimental::product_specification spec, std::string layer) :
spec_{std::move(spec)}, layer_{std::move(layer)}
// Check that all products selected by /other/ would satisfy this query
bool product_query::match(product_query const& other) const
{
if (creator_ != other.creator_) {
return false;
}
if (layer_ != other.layer_) {
return false;
}
if (suffix_ && suffix_ != other.suffix_) {
return false;
}
if (stage_ && stage_ != other.stage_) {
return false;
}
// Special case. If other has an unset type_id, ignore this in case it just hasn't been set yet
if (type_id_.valid() && other.type_id_.valid() && type_id_ != other.type_id_) {
return false;
}
return true;
}

product_query experimental::product_tag::operator()(std::string data_layer) &&
// Check if a product_specification satisfies this query
bool product_query::match(experimental::product_specification const& spec) const
{
if (data_layer.empty()) {
throw std::runtime_error("Cannot specify the empty string as a data layer.");
if (creator_ != spec.algorithm()) {
return false;
}
if (type_id_ != spec.type()) {
return false;
}
if (suffix_) {
if (*suffix_ != spec.name()) {
return false;
}
}
return {std::move(spec), std::move(data_layer)};
return true;
}

std::string product_query::to_string() const
{
if (layer_.empty()) {
return spec_.full();
if (suffix_) {
return fmt::format("{}/{} ϵ {}", creator_, *suffix_, layer_);
}
return fmt::format("{} ϵ {}", spec_.full(), layer_);
return fmt::format("{} ϵ {}", creator_, layer_);
}

experimental::product_tag operator""_in(char const* product_name, std::size_t length)
experimental::product_specification product_query::spec() const
{
if (length == 0ull) {
throw std::runtime_error("Cannot specify product with empty name.");
if (!suffix()) {
throw std::logic_error("Product suffixes are (temporarily) mandatory");
}
return {experimental::product_specification::create(product_name)};
return experimental::product_specification::create(*suffix());
}
}
Loading
Loading