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