Skip to content

Commit 87762a0

Browse files
justonedev1Michal Tichák
andauthored
[QC-1298] Extensible interface for accessing QC data sources (#2589)
* [QC-1298] first version of Data * iterators in the middle of work * filtering iterators working * version 2 * Data used in aggregators * benchmarks * helpers implemented * data definition moved to inl file * rename Data to QCInputs * docs * iterateQualityObjects with check name * reworded docs and removed generic container from QCInputs --------- Co-authored-by: Michal Tichák <michal.tichak@cern.ch>
1 parent 89c7d7d commit 87762a0

17 files changed

+933
-43
lines changed

Framework/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,10 @@ add_library(O2QualityControl
136136
src/ReductorHelpers.cxx
137137
src/KafkaPoller.cxx
138138
src/FlagHelpers.cxx
139-
src/ObjectMetadataHelpers.cxx)
139+
src/ObjectMetadataHelpers.cxx
140+
src/QCInputsAdapters.cxx
141+
src/QCInputsFactory.cxx
142+
)
140143

141144
target_include_directories(
142145
O2QualityControl
@@ -289,6 +292,7 @@ add_executable(o2-qc-test-core
289292
test/testKafkaTests.cxx
290293
test/testFlagHelpers.cxx
291294
test/testQualitiesToFlagCollectionConverter.cxx
295+
test/testQCInputs.cxx
292296
)
293297
set_property(TARGET o2-qc-test-core
294298
PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests)

Framework/include/QualityControl/AggregatorInterface.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "QualityControl/UserCodeInterface.h"
2424
#include "QualityControl/Quality.h"
2525
#include "QualityControl/Activity.h"
26+
#include "QualityControl/QCInputs.h"
2627

2728
namespace o2::quality_control::checker
2829
{
@@ -42,7 +43,13 @@ class AggregatorInterface : public o2::quality_control::core::UserCodeInterface
4243
///
4344
/// @param qoMap A map of the the QualityObjects to aggregate and their full names.
4445
/// @return The new qualities, associated with a name.
45-
virtual std::map<std::string, core::Quality> aggregate(std::map<std::string, std::shared_ptr<const core::QualityObject>>& qoMap) = 0;
46+
virtual std::map<std::string, core::Quality> aggregate(std::map<std::string, std::shared_ptr<const core::QualityObject>>& qoMap);
47+
48+
/// \brief Returns new qualities (usually fewer) based on the input qualities stored in Data structure
49+
///
50+
/// @param data A generic data structure containing QualityObjects or possible other inputs.
51+
/// @return The new qualities, associated with a name.
52+
virtual std::map<std::string, core::Quality> aggregate(const core::QCInputs& data);
4653

4754
virtual void startOfActivity(const core::Activity& activity);
4855
virtual void endOfActivity(const core::Activity& activity);

Framework/include/QualityControl/CheckInterface.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222
#include "QualityControl/UserCodeInterface.h"
2323
#include "QualityControl/Activity.h"
2424

25+
#include "QualityControl/QCInputs.h"
26+
2527
namespace o2::quality_control::core
2628
{
2729
class Activity;
2830
class MonitorObject;
29-
}
31+
32+
} // namespace o2::quality_control::core
3033

3134
using namespace o2::quality_control::core;
3235

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

4750
/// \brief Returns the quality associated with these objects.
51+
/// \deprecated This function won't be deleted in future releases for compatibility reasons but users should
52+
/// use check(const Data&) for any new Checks.
4853
///
4954
/// @param moMap A map of the the MonitorObjects to check and their full names (i.e. <task_name>/<mo name>) as keys.
5055
/// @return The quality associated with these objects.
51-
virtual core::Quality check(std::map<std::string, std::shared_ptr<core::MonitorObject>>* moMap) = 0;
56+
virtual core::Quality check(std::map<std::string, std::shared_ptr<core::MonitorObject>>* moMap);
57+
58+
/// \brief Returns the quality associated with these objects.
59+
///
60+
/// @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.
61+
/// @return The quality associated with these objects.
62+
virtual core::Quality check(const core::QCInputs& data);
5263

5364
/// \brief Modify the aspect of the plot.
5465
///
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
// Copyright 2025 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
///
13+
/// \file QCInputs.h
14+
/// \author Michal Tichak
15+
/// \brief Generic container for heterogeneous QC input data.
16+
///
17+
/// \par Example
18+
/// \code{.cpp}
19+
/// QCInputs data;
20+
/// auto* h1 = new TH1F("th11", "th11", 100, 0, 99);
21+
/// data.insert("mo", std::make_shared<MonitorObject>(h1, "taskname", "class1", "TST"));
22+
/// if (auto opt = data.get<MonitorObject>("mo")) {
23+
/// MonitorObject& moObject = opt.value();
24+
/// std::cout << "mo name: " << moObject.getName() << std::endl;
25+
/// }
26+
/// for (const auto& mo : data.iterateByType<MonitorObject>()) {
27+
/// // process each value
28+
/// }
29+
/// \endcode
30+
///
31+
32+
#ifndef QC_CORE_DATA_H
33+
#define QC_CORE_DATA_H
34+
35+
#include <any>
36+
#include <concepts>
37+
#include <functional>
38+
#include <memory>
39+
#include <optional>
40+
#include <string>
41+
#include <ranges>
42+
#include <type_traits>
43+
44+
namespace o2::quality_control::core
45+
{
46+
47+
/// \brief Requires a callable to return exactly the specified type.
48+
/// \tparam Function Callable type to invoke.
49+
/// \tparam Result Expected return type.
50+
/// \tparam Args Argument types for invocation.
51+
template <typename Function, typename Result, typename... Args>
52+
concept invocable_r = std::invocable<Function, Args...> &&
53+
std::same_as<std::invoke_result_t<Function, Args...>, Result>;
54+
55+
/// \brief Heterogeneous storage for named QC input objects.
56+
///
57+
/// Stores values in an std::unordered_map<std::string, std::any> while
58+
/// offering type-safe get, iteration, filtering, and transformation.
59+
class QCInputs
60+
{
61+
public:
62+
QCInputs() = default;
63+
64+
/// \brief Retrieve the object stored under the given key with matching type.
65+
/// \tparam Result Expected stored type.
66+
/// \param key Identifier for the stored object.
67+
/// \returns Optional reference to const Result if found desired item of type Result.
68+
/// \par Example
69+
/// \code{.cpp}
70+
/// if (auto opt = data.get<MonitorObject>("mo")) {
71+
/// if (opt.has_value()){
72+
/// const unsigned& value = opt.value(); // careful about using auto here as we want to invoke implicit conversion operator of reference_wrapper
73+
/// }
74+
/// }
75+
/// \endcode
76+
template <typename Result>
77+
std::optional<std::reference_wrapper<const Result>> get(std::string_view key);
78+
79+
/// \brief Construct and store an object of type T under the given key.
80+
/// \tparam T Type to construct and store.
81+
/// \param key Identifier under which to store the object.
82+
/// \param args Arguments forwarded to T's constructor.
83+
/// \par Example
84+
/// \code{.cpp}
85+
/// auto* h1 = new TH1F("th11", "th11", 100, 0, 99);
86+
/// data.emplace<MonitorObject>("mo", h1, "taskname", "class1", "TST");
87+
/// \endcode
88+
template <typename T, typename... Args>
89+
void emplace(std::string_view key, Args&&... args);
90+
91+
/// \brief Store a copy of value under the given key.
92+
/// \tparam T Type of the value to store.
93+
/// \param key Identifier under which to store the value.
94+
/// \param value Const reference to the value to insert.
95+
/// \par Example
96+
/// \code{.cpp}
97+
/// auto* h1 = new TH1F("th11", "th11", 100, 0, 99);
98+
/// data.insert("mo", std::make_shared<MonitorObject>(h1, "taskname", "class1", "TST"));
99+
/// \endcode
100+
template <typename T>
101+
void insert(std::string_view key, const T& value);
102+
103+
/// \brief Iterate over all stored objects matching type Result.
104+
/// \tparam Result Type filter for iteration.
105+
/// \returns Range of const references to stored Result instances.
106+
/// \par Example
107+
/// \code{.cpp}
108+
/// for (auto& mo : data.iterateByType<MonitorObject>()) {
109+
/// // use val
110+
/// }
111+
/// \endcode
112+
template <typename Result>
113+
auto iterateByType() const;
114+
115+
/// \brief Iterate over stored objects of type Result satisfying a predicate.
116+
/// \tparam Result type filter for iteration.
117+
/// \tparam Pred Callable predicate on (key, Result*) pairs.
118+
/// \param filter Predicate to apply for filtering entries.
119+
/// \returns Range of const references to Result passing the filter.
120+
/// \par Example
121+
/// \code{.cpp}
122+
/// auto nameFilter = [](auto const& pair) { return pair.second->getName() == "name"; };
123+
/// for (auto& mo : data.iterateByTypeAndFilter<MonitorObject>(nameFilter)) {
124+
/// // use mo
125+
/// }
126+
/// \endcode
127+
template <typename Result, std::predicate<const std::pair<std::string_view, const Result*>&> Pred>
128+
auto iterateByTypeAndFilter(Pred&& filter) const;
129+
130+
/// \brief Filter entries of type Stored, then transform to type Result.
131+
/// \tparam Stored Original stored type for filtering.
132+
/// \tparam Result Target type after transformation.
133+
/// \tparam Pred Callable predicate on (key, Stored*) pairs.
134+
/// \tparam Transform Callable transforming Stored* to Result*.
135+
/// This Callable can return nullptr but it will be filtered out
136+
/// from results
137+
/// \param filter Predicate to apply before transformation.
138+
/// \param transform Callable to convert Stored to Result.
139+
/// \returns Range of const references to resulting objects.
140+
/// \par Example
141+
/// \code{.cpp}
142+
/// // if we stored some MOs that are not TH1F, these will be filtered out of results
143+
/// auto toHistogram = [](auto const& p) -> const auto* { return dynamic_cast<TH1F*>(p.second->getObject()); };
144+
/// auto nameFilter = [](auto const& p){ return p.first == "histo"; };
145+
/// for (auto& h : data.iterateByTypeFilterAndTransform<MonitorObject, TH1F>(nameFilter, toHistogram)) {
146+
/// // use histogram h
147+
/// }
148+
/// \endcode
149+
template <typename Stored, typename Result, std::predicate<const std::pair<std::string_view, const Stored*>&> Pred, invocable_r<const Result*, const Stored*> Transform>
150+
auto iterateByTypeFilterAndTransform(Pred&& filter, Transform&& transform) const;
151+
152+
/// \brief Number of stored entries.
153+
/// \returns Size of the underlying container.
154+
/// \par Example
155+
/// \code{.cpp}
156+
/// size_t n = data.size();
157+
/// \endcode
158+
size_t size() const noexcept;
159+
160+
private:
161+
/// \brief Transparent hash functor for string and string_view.
162+
///
163+
/// Enables heterogeneous lookup in unordered maps keyed by std::string.
164+
struct StringHash {
165+
using is_transparent = void; // Required for heterogeneous lookup
166+
167+
std::size_t operator()(const std::string& str) const
168+
{
169+
return std::hash<std::string>{}(str);
170+
}
171+
172+
std::size_t operator()(std::string_view sv) const
173+
{
174+
return std::hash<std::string_view>{}(sv);
175+
}
176+
};
177+
178+
std::unordered_map<std::string, std::any, StringHash, std::equal_to<>> mObjects;
179+
};
180+
181+
} // namespace o2::quality_control::core
182+
183+
#include "QCInputs.inl"
184+
185+
#endif

0 commit comments

Comments
 (0)