Skip to content
6 changes: 5 additions & 1 deletion Framework/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,10 @@ add_library(O2QualityControl
src/ReductorHelpers.cxx
src/KafkaPoller.cxx
src/FlagHelpers.cxx
src/ObjectMetadataHelpers.cxx)
src/ObjectMetadataHelpers.cxx
src/QCInputsAdapters.cxx
src/QCInputsFactory.cxx
)

target_include_directories(
O2QualityControl
Expand Down Expand Up @@ -289,6 +292,7 @@ add_executable(o2-qc-test-core
test/testKafkaTests.cxx
test/testFlagHelpers.cxx
test/testQualitiesToFlagCollectionConverter.cxx
test/testQCInputs.cxx
)
set_property(TARGET o2-qc-test-core
PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests)
Expand Down
9 changes: 8 additions & 1 deletion Framework/include/QualityControl/AggregatorInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "QualityControl/UserCodeInterface.h"
#include "QualityControl/Quality.h"
#include "QualityControl/Activity.h"
#include "QualityControl/QCInputs.h"

namespace o2::quality_control::checker
{
Expand All @@ -42,7 +43,13 @@ class AggregatorInterface : public o2::quality_control::core::UserCodeInterface
///
/// @param qoMap A map of the the QualityObjects to aggregate and their full names.
/// @return The new qualities, associated with a name.
virtual std::map<std::string, core::Quality> aggregate(std::map<std::string, std::shared_ptr<const core::QualityObject>>& qoMap) = 0;
virtual std::map<std::string, core::Quality> aggregate(std::map<std::string, std::shared_ptr<const core::QualityObject>>& qoMap);

/// \brief Returns new qualities (usually fewer) based on the input qualities stored in Data structure
///
/// @param data A generic data structure containing QualityObjects or possible other inputs.
/// @return The new qualities, associated with a name.
virtual std::map<std::string, core::Quality> aggregate(const core::QCInputs& data);

virtual void startOfActivity(const core::Activity& activity);
virtual void endOfActivity(const core::Activity& activity);
Expand Down
15 changes: 13 additions & 2 deletions Framework/include/QualityControl/CheckInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@
#include "QualityControl/UserCodeInterface.h"
#include "QualityControl/Activity.h"

#include "QualityControl/QCInputs.h"

namespace o2::quality_control::core
{
class Activity;
class MonitorObject;
}

} // namespace o2::quality_control::core

using namespace o2::quality_control::core;

Expand All @@ -45,10 +48,18 @@ class CheckInterface : public core::UserCodeInterface
virtual ~CheckInterface() = default;

/// \brief Returns the quality associated with these objects.
/// \deprecated This function won't be deleted in future releases for compatibility reasons but users should
/// use check(const Data&) for any new Checks.
///
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add a note that users should prefer the other one and this one is for backwards-compatibility.

/// @param moMap A map of the the MonitorObjects to check and their full names (i.e. <task_name>/<mo name>) as keys.
/// @return The quality associated with these objects.
virtual core::Quality check(std::map<std::string, std::shared_ptr<core::MonitorObject>>* moMap) = 0;
virtual core::Quality check(std::map<std::string, std::shared_ptr<core::MonitorObject>>* moMap);

/// \brief Returns the quality associated with these objects.
///
/// @param data An object with any type of data possible accesible via full names (i.e. <task_name>/<mo name> in case of MOs) as keys.
/// @return The quality associated with these objects.
virtual core::Quality check(const core::QCInputs& data);

/// \brief Modify the aspect of the plot.
///
Expand Down
185 changes: 185 additions & 0 deletions Framework/include/QualityControl/QCInputs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// Copyright 2025 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

///
/// \file QCInputs.h
/// \author Michal Tichak
/// \brief Generic container for heterogeneous QC input data.
///
/// \par Example
/// \code{.cpp}
/// QCInputs data;
/// auto* h1 = new TH1F("th11", "th11", 100, 0, 99);
/// data.insert("mo", std::make_shared<MonitorObject>(h1, "taskname", "class1", "TST"));
/// if (auto opt = data.get<MonitorObject>("mo")) {
/// MonitorObject& moObject = opt.value();
/// std::cout << "mo name: " << moObject.getName() << std::endl;
/// }
/// for (const auto& mo : data.iterateByType<MonitorObject>()) {
/// // process each value
/// }
/// \endcode
///

#ifndef QC_CORE_DATA_H
#define QC_CORE_DATA_H

#include <any>
#include <concepts>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <ranges>
#include <type_traits>

namespace o2::quality_control::core
{

/// \brief Requires a callable to return exactly the specified type.
/// \tparam Function Callable type to invoke.
/// \tparam Result Expected return type.
/// \tparam Args Argument types for invocation.
template <typename Function, typename Result, typename... Args>
concept invocable_r = std::invocable<Function, Args...> &&
std::same_as<std::invoke_result_t<Function, Args...>, Result>;

/// \brief Heterogeneous storage for named QC input objects.
///
/// Stores values in an std::unordered_map<std::string, std::any> while
/// offering type-safe get, iteration, filtering, and transformation.
class QCInputs
{
public:
QCInputs() = default;

/// \brief Retrieve the object stored under the given key with matching type.
/// \tparam Result Expected stored type.
/// \param key Identifier for the stored object.
/// \returns Optional reference to const Result if found desired item of type Result.
/// \par Example
/// \code{.cpp}
/// if (auto opt = data.get<MonitorObject>("mo")) {
/// if (opt.has_value()){
/// const unsigned& value = opt.value(); // careful about using auto here as we want to invoke implicit conversion operator of reference_wrapper
/// }
/// }
/// \endcode
template <typename Result>
std::optional<std::reference_wrapper<const Result>> get(std::string_view key);

/// \brief Construct and store an object of type T under the given key.
/// \tparam T Type to construct and store.
/// \param key Identifier under which to store the object.
/// \param args Arguments forwarded to T's constructor.
/// \par Example
/// \code{.cpp}
/// auto* h1 = new TH1F("th11", "th11", 100, 0, 99);
/// data.emplace<MonitorObject>("mo", h1, "taskname", "class1", "TST");
/// \endcode
template <typename T, typename... Args>
void emplace(std::string_view key, Args&&... args);

/// \brief Store a copy of value under the given key.
/// \tparam T Type of the value to store.
/// \param key Identifier under which to store the value.
/// \param value Const reference to the value to insert.
/// \par Example
/// \code{.cpp}
/// auto* h1 = new TH1F("th11", "th11", 100, 0, 99);
/// data.insert("mo", std::make_shared<MonitorObject>(h1, "taskname", "class1", "TST"));
/// \endcode
template <typename T>
void insert(std::string_view key, const T& value);

/// \brief Iterate over all stored objects matching type Result.
/// \tparam Result Type filter for iteration.
/// \returns Range of const references to stored Result instances.
/// \par Example
/// \code{.cpp}
/// for (auto& mo : data.iterateByType<MonitorObject>()) {
/// // use val
/// }
/// \endcode
template <typename Result>
auto iterateByType() const;

/// \brief Iterate over stored objects of type Result satisfying a predicate.
/// \tparam Result type filter for iteration.
/// \tparam Pred Callable predicate on (key, Result*) pairs.
/// \param filter Predicate to apply for filtering entries.
/// \returns Range of const references to Result passing the filter.
/// \par Example
/// \code{.cpp}
/// auto nameFilter = [](auto const& pair) { return pair.second->getName() == "name"; };
/// for (auto& mo : data.iterateByTypeAndFilter<MonitorObject>(nameFilter)) {
/// // use mo
/// }
/// \endcode
template <typename Result, std::predicate<const std::pair<std::string_view, const Result*>&> Pred>
auto iterateByTypeAndFilter(Pred&& filter) const;

/// \brief Filter entries of type Stored, then transform to type Result.
/// \tparam Stored Original stored type for filtering.
/// \tparam Result Target type after transformation.
/// \tparam Pred Callable predicate on (key, Stored*) pairs.
/// \tparam Transform Callable transforming Stored* to Result*.
/// This Callable can return nullptr but it will be filtered out
/// from results
/// \param filter Predicate to apply before transformation.
/// \param transform Callable to convert Stored to Result.
/// \returns Range of const references to resulting objects.
/// \par Example
/// \code{.cpp}
/// // if we stored some MOs that are not TH1F, these will be filtered out of results
/// auto toHistogram = [](auto const& p) -> const auto* { return dynamic_cast<TH1F*>(p.second->getObject()); };
/// auto nameFilter = [](auto const& p){ return p.first == "histo"; };
/// for (auto& h : data.iterateByTypeFilterAndTransform<MonitorObject, TH1F>(nameFilter, toHistogram)) {
/// // use histogram h
/// }
/// \endcode
template <typename Stored, typename Result, std::predicate<const std::pair<std::string_view, const Stored*>&> Pred, invocable_r<const Result*, const Stored*> Transform>
auto iterateByTypeFilterAndTransform(Pred&& filter, Transform&& transform) const;

/// \brief Number of stored entries.
/// \returns Size of the underlying container.
/// \par Example
/// \code{.cpp}
/// size_t n = data.size();
/// \endcode
size_t size() const noexcept;

private:
/// \brief Transparent hash functor for string and string_view.
///
/// Enables heterogeneous lookup in unordered maps keyed by std::string.
struct StringHash {
using is_transparent = void; // Required for heterogeneous lookup

std::size_t operator()(const std::string& str) const
{
return std::hash<std::string>{}(str);
}

std::size_t operator()(std::string_view sv) const
{
return std::hash<std::string_view>{}(sv);
}
};

std::unordered_map<std::string, std::any, StringHash, std::equal_to<>> mObjects;
};

} // namespace o2::quality_control::core

#include "QCInputs.inl"

#endif
Loading