Skip to content

Commit 50ee218

Browse files
committed
CommonUtils: testing
Signed-off-by: Felix Schlepper <[email protected]>
1 parent ee4bb3b commit 50ee218

File tree

6 files changed

+521
-15
lines changed

6 files changed

+521
-15
lines changed

Common/Utils/include/CommonUtils/ConfigurableParam.h

Lines changed: 178 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@
99
// granted to it by virtue of its status as an Intergovernmental Organization
1010
// or submit itself to any jurisdiction.
1111

12-
//first version 8/2018, Sandro Wenzel
12+
// first version 8/2018, Sandro Wenzel
1313

1414
#ifndef COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAM_H_
1515
#define COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAM_H_
1616

1717
#include <vector>
1818
#include <cassert>
19+
#include <concepts>
1920
#include <map>
21+
#include <sstream>
2022
#include <unordered_map>
2123
#include <boost/property_tree/ptree_fwd.hpp>
2224
#include <typeinfo>
@@ -136,6 +138,179 @@ class EnumRegistry
136138
std::unordered_map<std::string, EnumLegalValues> entries;
137139
};
138140

141+
template <typename T>
142+
concept Container = requires(T t) {
143+
typename T::value_type;
144+
typename T::iterator;
145+
{ t.begin() } -> std::same_as<typename T::iterator>;
146+
{ t.end() } -> std::same_as<typename T::iterator>;
147+
};
148+
149+
template <typename T>
150+
concept MapLike = Container<T> && requires {
151+
typename T::key_type;
152+
typename T::mapped_type;
153+
};
154+
155+
template <typename T>
156+
concept SequenceContainer = Container<T> && !MapLike<T>;
157+
158+
template <typename T>
159+
concept HasPushBack = requires(T a, typename T::value_type v) {
160+
{ a.push_back(v) } -> std::same_as<void>;
161+
};
162+
163+
class ContainerParser
164+
{
165+
public:
166+
template <typename T>
167+
static T parse(const std::string& str)
168+
{
169+
if constexpr (MapLike<T>) {
170+
return parseMap<T>(str);
171+
} else if constexpr (SequenceContainer<T>) {
172+
return parseSet<T>(str);
173+
} else if constexpr (Container<T>) {
174+
return parseSequence<T>(str);
175+
} else {
176+
return parseScalar<T>(str);
177+
}
178+
}
179+
180+
static std::string trim(const std::string& str)
181+
{
182+
auto start = str.find_first_not_of(" \t\n\r\f\v");
183+
if (start == std::string::npos) {
184+
return "";
185+
}
186+
auto end = str.find_last_not_of(" \t\n\r\f\v");
187+
return str.substr(start, end - start + 1);
188+
}
189+
190+
private:
191+
// Parse vector, list, deque, array
192+
template <SequenceContainer SequenceT>
193+
static SequenceT parseSequence(const std::string& str)
194+
{
195+
SequenceT result;
196+
using ValueType = typename SequenceT::value_type;
197+
std::string cleaned = str;
198+
if (!cleaned.empty() && cleaned.front() == '[' && cleaned.back() == ']') { // removed brackets [1,2,3] -> 1,2,3
199+
cleaned = cleaned.substr(1, cleaned.length() - 2);
200+
}
201+
if (cleaned.empty() || cleaned == "{}") { // noting to do
202+
return result;
203+
}
204+
auto tokens = split(cleaned, ',');
205+
for (const auto& token : tokens) {
206+
std::string trimmed = trim(token);
207+
result.insert(result.end(), parse<ValueType>(trimmed)); // handles nested containers
208+
}
209+
return result;
210+
}
211+
212+
// Parse map, unordered_map, multimap
213+
template <MapLike MapT>
214+
static MapT parseMap(const std::string& str)
215+
{
216+
MapT result;
217+
using KeyType = typename MapT::key_type;
218+
using ValueType = typename MapT::mapped_type;
219+
std::string cleaned = str;
220+
if (!cleaned.empty() && cleaned.front() == '{' && cleaned.back() == '}') { // stip braces {a:1,b:2} -> a:1,b:2
221+
cleaned = cleaned.substr(1, cleaned.length() - 2);
222+
}
223+
if (cleaned.empty()) { // nothing to do
224+
return result;
225+
}
226+
auto pairs = split(cleaned, ',');
227+
for (const auto& pair_str : pairs) {
228+
auto kv = split(pair_str, ':');
229+
if (kv.size() != 2) {
230+
throw std::runtime_error("Invalid map syntax: " + pair_str + ". Expected 'key:value' format, got ");
231+
}
232+
KeyType key = parseScalar<KeyType>(trim(kv[0]));
233+
result[key] = parse<ValueType>(trim(kv[1]));
234+
}
235+
return result;
236+
}
237+
238+
// Parse set containers
239+
template <SequenceContainer SetT>
240+
static SetT parseSet(const std::string& str)
241+
{
242+
SetT result;
243+
using ValueType = typename SetT::value_type;
244+
auto vec = parseSequence<std::vector<ValueType>>(str);
245+
for (const auto& val : vec) {
246+
if constexpr (HasPushBack<SetT>) {
247+
result.push_back(val);
248+
} else {
249+
result.insert(val);
250+
}
251+
}
252+
return result;
253+
}
254+
255+
// Parse scalar types
256+
template <typename T>
257+
static T parseScalar(const std::string& str)
258+
{
259+
if constexpr (std::is_same_v<T, std::string>) {
260+
return str;
261+
} else if constexpr (std::is_same_v<T, bool>) {
262+
std::string lower = str;
263+
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
264+
if (lower == "true" || lower == "1") {
265+
return true;
266+
}
267+
if (lower == "false" || lower == "0") {
268+
return false;
269+
}
270+
throw std::runtime_error("Invalid boolean value: " + str);
271+
} else {
272+
std::istringstream iss(str);
273+
T value;
274+
iss >> value;
275+
if (iss.fail()) {
276+
throw std::runtime_error("Failed to parse '" + str + "' as " + typeid(T).name());
277+
}
278+
return value;
279+
}
280+
}
281+
282+
// Split respecting nested brackets and braces
283+
static std::vector<std::string> split(const std::string& str, char delimiter)
284+
{
285+
std::vector<std::string> tokens;
286+
std::string current;
287+
int bracket_depth = 0;
288+
int brace_depth = 0;
289+
for (char c : str) {
290+
if (c == '[') {
291+
bracket_depth++;
292+
} else if (c == ']') {
293+
bracket_depth--;
294+
} else if (c == '{') {
295+
brace_depth++;
296+
} else if (c == '}') {
297+
brace_depth--;
298+
} else if (c == delimiter && bracket_depth == 0 && brace_depth == 0) {
299+
if (!current.empty()) {
300+
tokens.push_back(current);
301+
current.clear();
302+
}
303+
continue;
304+
}
305+
current += c;
306+
}
307+
if (!current.empty()) {
308+
tokens.push_back(current);
309+
}
310+
return tokens;
311+
}
312+
};
313+
139314
class ConfigurableParam
140315
{
141316
public:
@@ -244,14 +419,15 @@ class ConfigurableParam
244419
static void setValue(std::string const& key, std::string const& valuestring);
245420
static void setEnumValue(const std::string&, const std::string&);
246421
static void setArrayValue(const std::string&, const std::string&);
422+
static void setContainerValue(const std::string&, const std::string&);
247423

248424
// update the storagemap from a vector of key/value pairs, calling setValue for each pair
249425
static void setValues(std::vector<std::pair<std::string, std::string>> const& keyValues);
250426

251427
// initializes the parameter database
252428
static void initialize();
253429

254-
// create CCDB snapsnot
430+
// create CCDB snapshot
255431
static void toCCDB(std::string filename);
256432
// load from (CCDB) snapshot
257433
static void fromCCDB(std::string filename);

Common/Utils/include/CommonUtils/ConfigurableParamHelper.h

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@
99
// granted to it by virtue of its status as an Intergovernmental Organization
1010
// or submit itself to any jurisdiction.
1111

12-
//first version 8/2018, Sandro Wenzel
12+
// first version 8/2018, Sandro Wenzel
1313

1414
#ifndef COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAMHELPER_H_
1515
#define COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAMHELPER_H_
1616

1717
#include "CommonUtils/ConfigurableParam.h"
18-
#include "TClass.h"
18+
#include <TClass.h>
19+
#include <TFile.h>
20+
#include <TDataMember.h>
1921
#include <type_traits>
2022
#include <typeinfo>
21-
#include "TFile.h"
2223

2324
namespace o2
2425
{
@@ -339,6 +340,23 @@ class ConfigurableParamPromoter : public Base, virtual public ConfigurableParam
339340
}
340341
};
341342

343+
inline bool isContainer(const std::string& typeName)
344+
{
345+
return (typeName.starts_with("vector") ||
346+
typeName.starts_with("map") ||
347+
typeName.starts_with("set") ||
348+
typeName.starts_with("list") ||
349+
typeName.starts_with("deque") ||
350+
typeName.starts_with("unordered_map") ||
351+
typeName.starts_with("unordered_set"));
352+
}
353+
354+
inline bool isContainer(TDataMember const& dm)
355+
{
356+
const std::string typeName = dm.GetTrueTypeName();
357+
return isContainer(typeName);
358+
}
359+
342360
} // namespace conf
343361
} // namespace o2
344362

Common/Utils/include/CommonUtils/ConfigurableParamTest.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
#include "CommonUtils/ConfigurableParam.h"
1616
#include "CommonUtils/ConfigurableParamHelper.h"
1717

18+
#include <vector>
19+
#include <array>
20+
#include <map>
21+
1822
namespace o2::conf::test
1923
{
2024
struct TestParam : public o2::conf::ConfigurableParamHelper<TestParam> {
@@ -37,6 +41,8 @@ struct TestParam : public o2::conf::ConfigurableParamHelper<TestParam> {
3741
int iValueProvenanceTest{0};
3842
TestEnum eValue = TestEnum::C;
3943
int caValue[3] = {0, 1, 2};
44+
std::vector<int> vec;
45+
std::map<int, unsigned int> map;
4046

4147
O2ParamDef(TestParam, "TestParam");
4248
};

0 commit comments

Comments
 (0)