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