Skip to content

Commit b8e6441

Browse files
committed
Allow erasing parameter subtrees in ConfigParamRegistry
1 parent 208e07f commit b8e6441

3 files changed

Lines changed: 84 additions & 0 deletions

File tree

‎Framework/Core/include/Framework/ConfigParamRegistry.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ class ConfigParamRegistry
8787

8888
void override(const char* key, ConfigValueType auto const& val) const;
8989

90+
// Remove an option (a leaf or a whole subtree) from the active store, no-op if it is missing
91+
void erase(const char* key) const;
92+
9093
// Load extra parameters discovered while we process data
9194
void loadExtra(std::vector<ConfigParamSpec>& extras);
9295

‎Framework/Core/src/ConfigParamRegistry.cxx‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "Framework/VariantPropertyTreeHelpers.h"
1313
#include "Framework/Array2D.h"
1414

15+
#include <string_view>
1516
#include <type_traits>
1617

1718
namespace o2::framework
@@ -69,6 +70,19 @@ auto getImpl(boost::property_tree::ptree& tree, const char* key)
6970
{
7071
return labeledArrayFromBranch<typename T::element_t>(tree.get_child(key));
7172
}
73+
74+
// ptree::erase only removes direct children, so the parent of a nested key has to be looked up first
75+
void eraseImpl(boost::property_tree::ptree& tree, std::string_view key)
76+
{
77+
auto pos = key.rfind('.');
78+
if (pos == std::string_view::npos) {
79+
tree.erase(std::string(key));
80+
return;
81+
}
82+
if (auto parent = tree.get_child_optional(std::string(key.substr(0, pos)))) {
83+
parent->erase(std::string(key.substr(pos + 1)));
84+
}
85+
}
7286
} // namespace
7387

7488
template <ConfigValueType T>
@@ -105,6 +119,12 @@ void ConfigParamRegistry::override(const char* key, ConfigValueType auto const&
105119
}
106120
}
107121

122+
void ConfigParamRegistry::erase(const char* key) const
123+
{
124+
eraseImpl(mStore->store(), key);
125+
eraseImpl(mStore->provenanceTree(), key);
126+
}
127+
108128
// Load extra parameters discovered while we process data
109129
void ConfigParamRegistry::loadExtra(std::vector<ConfigParamSpec>& extras)
110130
{

‎Framework/Core/test/test_ConfigParamRegistry.cxx‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,64 @@ TEST_CASE("TestConfigParamRegistryOverride")
196196
registry.override("aString", std::vector<std::string>{"now", "an", "array"});
197197
REQUIRE(registry.get<std::vector<std::string>>("aString") == std::vector<std::string>{"now", "an", "array"});
198198
}
199+
200+
TEST_CASE("TestConfigParamRegistryErase")
201+
{
202+
int l[2][2] = {{1, 2}, {3, 4}};
203+
// a Variant cannot be created from a temporary vector, so the defaults are named
204+
std::vector<int> defaultVector{1, 2, 3, 4, 5};
205+
std::vector<std::string> defaultStringVector{"a", "b", "c"};
206+
std::vector<double> defaultNestedVector{0.5, 1.5};
207+
// the store keeps a reference to the specs, which have to outlive it
208+
std::vector<ConfigParamSpec> specs{
209+
ConfigParamSpec{"anInt", VariantType::Int, 1, {"an int option"}},
210+
ConfigParamSpec{"aString", VariantType::String, "foo", {"a string option"}},
211+
ConfigParamSpec{"aVector", VariantType::ArrayInt, defaultVector, {"an int array option"}},
212+
ConfigParamSpec{"aStringVector", VariantType::ArrayString, defaultStringVector, {"a string array option"}},
213+
ConfigParamSpec{"aLabeledArray", VariantType::LabeledArrayInt, LabeledArray<int>{&l[0][0], 2, 2, {"r1", "r2"}, {"c1", "c2"}}, {"a labeled array option"}},
214+
ConfigParamSpec{"aNested.x", VariantType::Int, 2, {"an int option, nested in an object"}},
215+
ConfigParamSpec{"aNested.vec", VariantType::ArrayDouble, defaultNestedVector, {"a double array option, nested in an object"}},
216+
};
217+
auto store = std::make_unique<ConfigParamStore>(specs, std::vector<std::unique_ptr<ParamRetriever>>{});
218+
store->preload();
219+
store->activate();
220+
ConfigParamRegistry registry(std::move(store));
221+
222+
// a leaf value is removed
223+
REQUIRE(registry.hasOption("anInt"));
224+
registry.erase("anInt");
225+
REQUIRE(registry.hasOption("anInt") == false);
226+
REQUIRE(registry.isSet("anInt") == false);
227+
REQUIRE(registry.isDefault("anInt") == false);
228+
REQUIRE_THROWS_AS(registry.get<int>("anInt"), std::invalid_argument);
229+
230+
// arrays are removed as whole subtrees
231+
registry.erase("aVector");
232+
REQUIRE(registry.hasOption("aVector") == false);
233+
REQUIRE(registry.isSet("aVector") == false);
234+
registry.erase("aLabeledArray");
235+
REQUIRE(registry.hasOption("aLabeledArray") == false);
236+
REQUIRE(registry.isSet("aLabeledArray") == false);
237+
238+
// a nested key is removed without touching its siblings
239+
registry.erase("aNested.vec");
240+
REQUIRE(registry.hasOption("aNested.vec") == false);
241+
REQUIRE(registry.get<int>("aNested.x") == 2);
242+
243+
// other options are untouched
244+
REQUIRE(registry.get<std::string>("aString") == "foo");
245+
REQUIRE(registry.get<std::vector<std::string>>("aStringVector") == std::vector<std::string>{"a", "b", "c"});
246+
247+
// missing keys are ignored
248+
REQUIRE_NOTHROW(registry.erase("anInt"));
249+
REQUIRE_NOTHROW(registry.erase("noSuchKey"));
250+
REQUIRE_NOTHROW(registry.erase("noSuchParent.x"));
251+
REQUIRE_NOTHROW(registry.erase("aNested.noSuchKey"));
252+
REQUIRE(registry.get<int>("aNested.x") == 2);
253+
254+
// an erased key can be stored again
255+
registry.override("anInt", 3);
256+
REQUIRE(registry.get<int>("anInt") == 3);
257+
registry.override("aVector", std::vector<int>{7, 8});
258+
REQUIRE(registry.get<std::vector<int>>("aVector") == std::vector<int>{7, 8});
259+
}

0 commit comments

Comments
 (0)