Project
Loading...
Searching...
No Matches
ConfigurableParamHelper.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 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//first version 8/2018, Sandro Wenzel
13
16#include <TClass.h>
17#include <TDataMember.h>
18#include <TDataType.h>
19#include <TEnum.h>
20#include <TEnumConstant.h>
21#include <TIterator.h>
22#include <TList.h>
23#include <iostream>
24#include <sstream>
25#include <fairlogger/Logger.h>
26#include <boost/property_tree/ptree.hpp>
27#include <boost/functional/hash.hpp>
28#include <functional>
29#include <format>
30#ifdef NDEBUG
31#undef NDEBUG
32#endif
33#include <cassert>
34
35using namespace o2::conf;
36
37// ----------------------------------------------------------------------
38
39std::string ParamDataMember::toString(std::string const& prefix, bool showProv, size_t padding) const
40{
41 const std::string label = prefix + "." + name + " : " + value;
42 std::ostringstream out;
43 out << label;
44
45 if (showProv) {
46 std::string prov = (provenance.compare("") == 0 ? "<null>" : provenance);
47 if (padding) {
48 size_t len = label.size() - prefix.size() - 5; // 4 four the extra chars + 1 for the maxpad
49 if (len < padding) {
50 out << std::string(padding - len, ' ');
51 } else {
52 out << ' ';
53 }
54 out << "[ " + prov + " ]";
55 } else {
56 out << "\t\t[ " + prov + " ]";
57 }
58 }
59 return out.str();
60}
61
62std::ostream& operator<<(std::ostream& out, const ParamDataMember& pdm)
63{
64 out << pdm.toString("", false) << "\n";
65 return out;
66}
67
68// ----------------------------------------------------------------------
69
70bool isString(TDataMember const& dm)
71{
72 return strcmp(dm.GetTrueTypeName(), "string") == 0;
73}
74
75// ----------------------------------------------------------------------
76
77// a generic looper of data members of a TClass; calling a callback
78// reused in various functions below
79void loopOverMembers(TClass* cl, void* obj,
80 std::function<void(const TDataMember*, int, int)>&& callback)
81{
82 auto memberlist = cl->GetListOfDataMembers();
83 for (int i = 0; i < memberlist->GetEntries(); ++i) {
84 auto dm = (TDataMember*)memberlist->At(i);
85
86 auto isValidComplex = [dm]() {
87 return isString(*dm) || dm->IsEnum();
88 };
89
90 // filter out static members for now
91 if (dm->Property() & kIsStatic) {
92 continue;
93 }
94
95 if (dm->IsaPointer()) {
96 LOG(warning) << "Pointer types not supported in ConfigurableParams: " << dm->GetFullTypeName() << " " << dm->GetName();
97 continue;
98 }
99 if (!dm->IsBasic() && !isValidComplex()) {
100 LOG(warning) << "Generic complex types not supported in ConfigurableParams: " << dm->GetFullTypeName() << " " << dm->GetName();
101 continue;
102 }
103
104 const auto dim = dm->GetArrayDim();
105 // we support very simple vectored data in 1D for now
106 if (dim > 1) {
107 LOG(warning) << "We support at most 1 dimensional arrays in ConfigurableParams: " << dm->GetFullTypeName() << " " << dm->GetName();
108 continue;
109 }
110
111 const auto size = (dim == 1) ? dm->GetMaxIndex(dim - 1) : 1; // size of array (1 if scalar)
112 for (int index = 0; index < size; ++index) {
113 callback(dm, index, size);
114 }
115 }
116}
117
118// ----------------------------------------------------------------------
119
120// construct name (in dependence on vector or scalar data and index)
121std::string getName(const TDataMember* dm, int index, int size)
122{
123 std::stringstream namestream;
124 namestream << dm->GetName();
125 if (size > 1) {
126 namestream << "[" << index << "]";
127 }
128 return namestream.str();
129}
130
131// ----------------------------------------------------------------------
132size_t getSizeOfUnderlyingType(const TDataMember& dm)
133{
134 auto dt = dm.GetDataType();
135 if (dt) {
136 // if basic built-in type supported by ROOT
137 return dt->Size();
138 } else {
139 // for now only catch std::string as other supported type
140 auto tname = dm.GetFullTypeName();
141 if (strcmp(tname, "string") == 0 || strcmp(tname, "std::string")) {
142 return sizeof(std::string);
143 }
144 LOG(error) << "ENCOUNTERED AN UNSUPPORTED TYPE " << tname << "IN A CONFIGURABLE PARAMETER";
145 }
146 return 0;
147}
148
149// ----------------------------------------------------------------------
150
151std::string asString(TDataMember const& dm, char* pointer)
152{
153 // first check if this is a basic data type, in which case
154 // we let ROOT do the work
155 if (auto dt = dm.GetDataType()) {
156 // we put the numeric interpration for char / unsigned char
157 // instead of the string one
158 if (dt->GetType() == EDataType::kChar_t) {
159 auto c = (char)(*pointer);
160 return std::to_string((int)c).c_str();
161 } else if (dt->GetType() == EDataType::kUChar_t) {
162 auto u = (unsigned char)(*pointer);
163 return std::to_string((unsigned int)u).c_str();
164 }
165
166 auto val = dt->AsString(pointer);
167
168 // For enums we grab the string value of the member
169 // and use that instead if its int value
170 if (dm.IsEnum()) {
171 const auto enumtype = TEnum::GetEnum(dm.GetTypeName());
172 assert(enumtype != nullptr);
173 const auto constantlist = enumtype->GetConstants();
174 assert(constantlist != nullptr);
175 if (enumtype) {
176 for (int i = 0; i < constantlist->GetEntries(); ++i) {
177 const auto e = (TEnumConstant*)(constantlist->At(i));
178 if (val == std::to_string((int)e->GetValue())) {
179 return std::string(e->GetName());
180 }
181 }
182 }
183 }
184
185 return std::string(val);
186 }
187
188 // if data member is a std::string just return
189 else if (isString(dm)) {
190 return ((std::string*)pointer)->c_str();
191 }
192 // potentially other cases to be added here
193
194 LOG(error) << "COULD NOT REPRESENT AS STRING";
195 return std::string();
196}
197
198// ----------------------------------------------------------------------
199
200std::vector<ParamDataMember>* _ParamHelper::getDataMembersImpl(std::string const& mainkey, TClass* cl, void* obj,
201 std::map<std::string, ConfigurableParam::EParamProvenance> const* provmap, size_t globaloffset)
202{
203 std::vector<ParamDataMember>* members = new std::vector<ParamDataMember>;
204
205 auto toDataMember = [&members, obj, mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) {
206 auto TS = getSizeOfUnderlyingType(*dm);
207 char* pointer = ((char*)obj) + dm->GetOffset() + index * TS + globaloffset;
208 const std::string name = getName(dm, index, size);
209 auto value = asString(*dm, pointer);
210
211 std::string prov = "";
212 auto iter = provmap->find(mainkey + "." + name);
213 if (iter != provmap->end()) {
214 prov = ConfigurableParam::toString(iter->second);
215 }
216 ParamDataMember member{name, value, prov};
217 members->push_back(member);
218 };
219
220 loopOverMembers(cl, obj, toDataMember);
221 return members;
222}
223
224// ----------------------------------------------------------------------
225
226// a function converting a string representing a type to the type_info
227// because unfortunately typeid(double) != typeid("double")
228// but we can take the TDataType (if it exists) as a hint in order to
229// minimize string comparisons
230std::type_info const& nameToTypeInfo(const char* tname, TDataType const* dt)
231{
232 if (dt) {
233 switch (dt->GetType()) {
234 case kChar_t: {
235 return typeid(char);
236 }
237 case kUChar_t: {
238 return typeid(unsigned char);
239 }
240 case kShort_t: {
241 return typeid(short);
242 }
243 case kUShort_t: {
244 return typeid(unsigned short);
245 }
246 case kInt_t: {
247 return typeid(int);
248 }
249 case kUInt_t: {
250 return typeid(unsigned int);
251 }
252 case kLong_t: {
253 return typeid(long);
254 }
255 case kULong_t: {
256 return typeid(unsigned long);
257 }
258 case kFloat_t: {
259 return typeid(float);
260 }
261 case kDouble_t: {
262 return typeid(double);
263 }
264 case kDouble32_t: {
265 return typeid(double);
266 }
267 case kBool_t: {
268 return typeid(bool);
269 }
270 case kLong64_t: {
271 return typeid(long long);
272 }
273 case kULong64_t: {
274 return typeid(unsigned long long);
275 }
276 default: {
277 break;
278 }
279 }
280 }
281 // if we get here none of the above worked
282 if (strcmp(tname, "string") == 0 || strcmp(tname, "std::string")) {
283 return typeid(std::string);
284 }
285 LOG(error) << "ENCOUNTERED AN UNSUPPORTED TYPE " << tname << "IN A CONFIGURABLE PARAMETER";
286 return typeid("ERROR");
287}
288
289// ----------------------------------------------------------------------
290
291void _ParamHelper::fillKeyValuesImpl(std::string const& mainkey, TClass* cl, void* obj, boost::property_tree::ptree* tree,
292 std::map<std::string, std::pair<std::type_info const&, void*>>* keytostoragemap,
293 EnumRegistry* enumRegistry, size_t globaloffset)
294{
295 boost::property_tree::ptree localtree;
296 auto fillMap = [obj, &mainkey, &localtree, &keytostoragemap, &enumRegistry, globaloffset](const TDataMember* dm, int index, int size) {
297 const auto name = getName(dm, index, size);
298 auto dt = dm->GetDataType();
299 auto TS = getSizeOfUnderlyingType(*dm);
300 char* pointer = ((char*)obj) + dm->GetOffset() + index * TS + globaloffset;
301 localtree.put(name, asString(*dm, pointer));
302
303 auto key = mainkey + "." + name;
304
305 // If it's an enum, we need to store separately all the legal
306 // values so that we can map to them from the command line
307 if (dm->IsEnum()) {
308 enumRegistry->add(key, dm);
309 }
310
311 using mapped_t = std::pair<std::type_info const&, void*>;
312 auto& ti = nameToTypeInfo(dm->GetTrueTypeName(), dt);
313 keytostoragemap->insert(std::pair<std::string, mapped_t>(key, mapped_t(ti, pointer)));
314 };
315 loopOverMembers(cl, obj, fillMap);
316 tree->add_child(mainkey, localtree);
317}
318
319// ----------------------------------------------------------------------
320
321void _ParamHelper::printMembersImpl(std::string const& mainkey, std::vector<ParamDataMember> const* members, bool showProv, bool useLogger, bool withPadding, bool showHash)
322{
323
324 _ParamHelper::outputMembersImpl(std::cout, mainkey, members, showProv, useLogger, withPadding, showHash);
325}
326
327void _ParamHelper::outputMembersImpl(std::ostream& out, std::string const& mainkey, std::vector<ParamDataMember> const* members, bool showProv, bool useLogger, bool withPadding, bool showHash)
328{
329 if (members == nullptr) {
330 return;
331 }
332
333 size_t maxpad{0};
334 if (withPadding) {
335 for (auto& member : *members) {
336 maxpad = std::max(maxpad, member.name.size() + member.value.size());
337 }
338 }
339
340 if (showHash) {
341 std::string shash = std::format("{:07x}", getHashImpl(mainkey, members));
342 shash = shash.substr(0, 7);
343 if (useLogger) {
344 LOG(info) << mainkey << " [Hash#" << shash << "]";
345 } else {
346 out << mainkey << " [Hash#" << shash << "]\n";
347 }
348 }
349
350 for (auto& member : *members) {
351 if (useLogger) {
352 LOG(info) << member.toString(mainkey, showProv, maxpad);
353 } else {
354 out << member.toString(mainkey, showProv, maxpad) << "\n";
355 }
356 }
357}
358
359size_t _ParamHelper::getHashImpl(std::string const& mainkey, std::vector<ParamDataMember> const* members)
360{
361 size_t hash = 0;
362 boost::hash_combine(hash, mainkey);
363 for (auto& member : *members) {
364 boost::hash_combine(hash, member.value);
365 }
366 return hash;
367}
368
369// ----------------------------------------------------------------------
370
371bool isMemblockDifferent(char const* block1, char const* block2, int sizeinbytes)
372{
373 // loop over thing in elements of bytes
374 for (int i = 0; i < sizeinbytes / sizeof(char); ++i) {
375 if (block1[i] != block2[i]) {
376 return false;
377 }
378 }
379 return true;
380}
381
382// ----------------------------------------------------------------------
383
384void _ParamHelper::assignmentImpl(std::string const& mainkey, TClass* cl, void* to, void* from,
385 std::map<std::string, ConfigurableParam::EParamProvenance>* provmap, size_t globaloffset)
386{
387 auto assignifchanged = [to, from, &mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) {
388 const auto name = getName(dm, index, size);
389 auto dt = dm->GetDataType();
390 auto TS = getSizeOfUnderlyingType(*dm);
391 char* pointerto = ((char*)to) + dm->GetOffset() + index * TS + globaloffset;
392 char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS + globaloffset;
393
394 // lambda to update the provenance
395 auto updateProv = [&mainkey, name, provmap]() {
396 auto key = mainkey + "." + name;
397 auto iter = provmap->find(key);
398 if (iter != provmap->end()) {
399 iter->second = ConfigurableParam::EParamProvenance::kCCDB; // TODO: change to "current STATE"??
400 } else {
401 LOG(warn) << "KEY " << key << " NOT FOUND WHILE UPDATING PARAMETER PROVENANCE";
402 }
403 };
404
405 // TODO: this could dispatch to the same method used in ConfigurableParam::setValue
406 // but will be slower
407
408 // test if a complicated case
409 if (isString(*dm)) {
410 std::string& target = *(std::string*)pointerto;
411 std::string const& origin = *(std::string*)pointerfrom;
412 if (target.compare(origin) != 0) {
413 updateProv();
414 target = origin;
415 }
416 return;
417 }
418
419 //
420 if (!isMemblockDifferent(pointerto, pointerfrom, TS)) {
421 updateProv();
422 // actually copy
423 std::memcpy(pointerto, pointerfrom, getSizeOfUnderlyingType(*dm));
424 }
425 };
426 loopOverMembers(cl, to, assignifchanged);
427}
428
429// ----------------------------------------------------------------------
430
431void _ParamHelper::syncCCDBandRegistry(const std::string& mainkey, TClass* cl, void* to, void* from,
432 std::map<std::string, ConfigurableParam::EParamProvenance>* provmap, size_t globaloffset)
433{
434 auto sync = [to, from, &mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) {
435 const auto name = getName(dm, index, size);
436 auto dt = dm->GetDataType();
437 auto TS = getSizeOfUnderlyingType(*dm);
438 char* pointerto = ((char*)to) + dm->GetOffset() + index * TS + globaloffset;
439 char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS + globaloffset;
440
441 // check current provenance
442 auto key = mainkey + "." + name;
443 auto proviter = provmap->find(key);
444 bool isRT = proviter != provmap->end() && proviter->second == ConfigurableParam::EParamProvenance::kRT;
445 if (isRT) {
446 return;
447 }
448 // lambda to update the provenance
449 auto updateProv = [&proviter]() {
451 };
452
453 // test if a complicated case
454 if (isString(*dm)) {
455 std::string& target = *(std::string*)pointerto;
456 std::string const& origin = *(std::string*)pointerfrom;
457 // if (target.compare(origin) != 0) {
458 updateProv();
459 target = origin;
460 // }
461 return;
462 }
463
464 //
465 // if (!isMemblockDifferent(pointerto, pointerfrom, TS)) {
466 updateProv();
467 // actually copy
468 std::memcpy(pointerto, pointerfrom, getSizeOfUnderlyingType(*dm));
469 // }
470 };
471 loopOverMembers(cl, to, sync);
472}
473
474// ----------------------------------------------------------------------
475
476void _ParamHelper::printWarning(std::type_info const& tinfo)
477{
478 LOG(warning) << "Registered parameter class with name " << tinfo.name()
479 << " has no ROOT dictionary and will not be available in the configurable parameter system";
480}
uint16_t padding
size_t getSizeOfUnderlyingType(const TDataMember &dm)
void loopOverMembers(TClass *cl, void *obj, std::function< void(const TDataMember *, int, int)> &&callback)
std::string getName(const TDataMember *dm, int index, int size)
std::string asString(TDataMember const &dm, char *pointer)
bool isString(TDataMember const &dm)
std::type_info const & nameToTypeInfo(const char *tname, TDataType const *dt)
int32_t i
uint32_t c
Definition RawData.h:2
StringRef key
static std::string toString(EParamProvenance p)
void add(const std::string &key, const TDataMember *dm)
GLenum void ** pointer
Definition glcorearb.h:805
GLsizeiptr size
Definition glcorearb.h:659
GLuint index
Definition glcorearb.h:781
GLuint const GLchar * name
Definition glcorearb.h:781
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLenum target
Definition glcorearb.h:1641
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
GLuint GLfloat * val
Definition glcorearb.h:1582
GLenum GLenum GLsizei len
Definition glcorearb.h:4232
consteval header::DataOrigin origin()
Definition ASoA.h:350
bool isMemblockDifferent(void const *block1, void const *block2)
std::ostream & operator<<(std::ostream &out, ConfigurableParam const &param)
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
std::string toString(std::string const &prefix, bool showProv, size_t padding=0) const
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))