Project
Loading...
Searching...
No Matches
ConfigurableParam.h
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
14#ifndef COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAM_H_
15#define COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAM_H_
16
17#include <vector>
18#include <cassert>
19#include <map>
20#include <unordered_map>
21#include <boost/property_tree/ptree_fwd.hpp>
22#include <typeinfo>
23#include <iostream>
24#include <array>
25
26class TFile;
27class TRootIOCtor;
28class TDataMember;
29
30namespace o2
31{
32namespace conf
33{
34// Base class for a configurable parameter.
35//
36// A configurable parameter (ConfigurableParameter) is a simple class, defining
37// a few (pod) properties/members which are registered
38// in a global (boost) property tree / structure.
39//
40// The features that we provide here are:
41// *) Automatic translation from C++ data description to INI/JSON/XML
42// format via ROOT introspection and boost property trees and
43// the possibility to readably save the configuration
44// *) Serialization/Deserialization into ROOT binary blobs (for the purpose
45// of writing/retrieving parameters from CCDB and to pass parameters along the processing chain)
46// *) Automatic integration of sub-classes into a common configuration
47// *) Be able to query properties from high level interfaces (just knowing
48// *) Be able to set properties from high-level interfaces (and modifying the underlying
49// C++ object)
50// *) Automatic ability to modify parameters from the command-line
51// *) Keeping track of the provenance of individual parameter values; The user is able
52// to see whether is parameter is defaulted-code-value/coming-from-CCDB/coming-from-comandline
53//
54// Note that concrete parameter sub-classes **must** be implemented
55// by inheriting from ConfigurableParamHelper and not from this class.
56//
57// ---------------------
58// Example: To define a parameter class TPCGasParameters, one does the following:
59//
60// class TPCGasParamer : public ConfigurableParamHelper<TPCGasParameter>
61// {
62// public:
63// double getGasDensity() const { return mGasDensity; }
64// private: // define properties AND default values
65// double mGasDensity = 1.23;
66// int mGasMaterialID = 1;
67//
68// O2ParamDef(TPCGasParameter, TPCGas); // a macro implementing some magic
69// }
70//
71//
72// We can now query the parameters in various ways
73// - All parameter classes are singletons and we can say: TPCGasParameter::Instance().getGasDensity();
74// - We can query by key (using classname + parameter name) from the global registry:
75// - ConfigurableParameter::getValueAs<double>("TPCGas", "mGasDensity");
76//
77// We can modify the parameters via the global registry together with an automatic syncing
78// of the underlying C++ object:
79// - ConfigurableParameter::setValue("TPCGas.mGasDensity", "0.5");
80//
81// - TPCGasParameter::Instance().getGasParameter() will now return 0.5;
82//
83// This feature allows to easily modify parameters at run-time via a textual representation
84// (for example by giving strings on the command line)
85//
86// The collection of all parameter keys and values can be stored to a human/machine readable
87// file
88// - ConfigurableParameter::writeJSON("thisconfiguration.json")
89
91 std::vector<std::pair<std::string, int>> vvalues;
92
93 bool isLegal(const std::string& value) const
94 {
95 for (auto& v : vvalues) {
96 if (v.first == value) {
97 return true;
98 }
99 }
100 return false;
101 }
102
103 bool isLegal(int value) const
104 {
105 for (auto& v : vvalues) {
106 if (v.second == value) {
107 return true;
108 }
109 }
110 return false;
111 }
112
113 std::string toString() const;
114 int getIntValue(const std::string& value) const;
115};
116
118{
119 public:
120 void add(const std::string& key, const TDataMember* dm);
121
122 bool contains(const std::string& key) const
123 {
124 return entries.count(key) > 0;
125 }
126
127 std::string toString() const;
128
129 const EnumLegalValues* operator[](const std::string& key) const
130 {
131 auto iter = entries.find(key);
132 return iter != entries.end() ? &iter->second : nullptr;
133 }
134
135 private:
136 std::unordered_map<std::string, EnumLegalValues> entries;
137};
138
140{
141 public:
143 kCODE /* from default code initialization */,
144 kCCDB /* overwritten from CCDB */,
145 kRT /* changed during runtime via API call setValue (for example command line) */
146 /* can add more modes here */
147 };
148
150 Changed, // param was successfully changed
151 Unchanged, // param was not changed: new value is the same as previous
152 Failed // failed to update param
153 };
154
155 static std::string toString(EParamProvenance p)
156 {
157 static std::array<std::string, 3> names = {"CODE", "CCDB", "RT"};
158 return names[(int)p];
159 }
160
161 // get the name of the configurable Parameter
162 virtual std::string getName() const = 0;
163
164 // print the current keys and values to screen (optionally with provenance information)
165 virtual void printKeyValues(bool showprov = true, bool useLogger = false) const = 0;
166
167 // get a single size_t hash_value of this parameter (can be used as a checksum to see
168 // if object changed or different)
169 virtual size_t getHash() const = 0;
170
171 // return the provenance of the member key
172 virtual EParamProvenance getMemberProvenance(const std::string& key) const = 0;
173
174 static EParamProvenance getProvenance(const std::string& key);
175
176 static void printAllRegisteredParamNames();
177 static void printAllKeyValuePairs(bool useLogger = false);
178
179 static const std::string& getOutputDir() { return sOutputDir; }
180
181 static void setOutputDir(const std::string& d) { sOutputDir = d; }
182
183 static bool configFileExists(std::string const& filepath);
184
185 // writes a human readable JSON file of all parameters
186 static void writeJSON(std::string const& filename, std::string const& keyOnly = "");
187 // writes a human readable INI file of all parameters
188 static void writeINI(std::string const& filename, std::string const& keyOnly = "");
189
190 // can be used instead of using API on concrete child classes
191 template <typename T>
192 static T getValueAs(std::string key)
193 {
194 return [](auto* tree, const std::string& key) -> T {
195 if (!sIsFullyInitialized) {
196 initialize();
197 }
198 return tree->template get<T>(key);
199 }(sPtree, key);
200 }
201
202 template <typename T>
203 static void setValue(std::string const& mainkey, std::string const& subkey, T x)
204 {
205 if (!sIsFullyInitialized) {
206 initialize();
207 }
208 return [&subkey, &x, &mainkey](auto* tree) -> void {
209 assert(tree);
210 try {
211 auto key = mainkey + "." + subkey;
212 if (tree->template get_optional<std::string>(key).is_initialized()) {
213 tree->put(key, x);
214 auto changed = updateThroughStorageMap(mainkey, subkey, typeid(T), (void*)&x);
215 if (changed != EParamUpdateStatus::Failed) {
216 sValueProvenanceMap->find(key)->second = kRT; // set to runtime
217 }
218 }
219 } catch (std::exception const& e) {
220 std::cerr << "Error in setValue (T) " << e.what() << "\n";
221 }
222 }(sPtree);
223 }
224
225 static void setProvenance(std::string const& mainkey, std::string const& subkey, EParamProvenance p)
226 {
227 if (!sIsFullyInitialized) {
228 std::cerr << "setProvenance was called on non-initialized ConfigurableParam\n";
229 return;
230 }
231 try {
232 auto key = mainkey + "." + subkey;
233 auto keyProv = sValueProvenanceMap->find(key);
234 if (keyProv != sValueProvenanceMap->end()) {
235 keyProv->second = p;
236 }
237 } catch (std::exception const& e) {
238 std::cerr << "Error in setProvenance (T) " << e.what() << "\n";
239 }
240 }
241
242 // specialized for std::string
243 // which means that the type will be converted internally
244 static void setValue(std::string const& key, std::string const& valuestring);
245 static void setEnumValue(const std::string&, const std::string&);
246 static void setArrayValue(const std::string&, const std::string&);
247
248 // update the storagemap from a vector of key/value pairs, calling setValue for each pair
249 static void setValues(std::vector<std::pair<std::string, std::string>> const& keyValues);
250
251 // initializes the parameter database
252 static void initialize();
253
254 // create CCDB snapsnot
255 static void toCCDB(std::string filename);
256 // load from (CCDB) snapshot
257 static void fromCCDB(std::string filename);
258
259 // allows to provide a string of key-values from which to update
260 // (certain) key-values
261 // propagates changes down to each registered configuration
262 // might be useful to get stuff from the command line
263 static void updateFromString(std::string const&);
264
265 // provide a path to a configuration file with ConfigurableParam key/values
266 // If nonempty comma-separated paramsList is provided, only those params will
267 // be updated, absence of data for any of requested params will lead to fatal
268 static void updateFromFile(std::string const&, std::string const& paramsList = "", bool unchangedOnly = false);
269
270 // interface for use from the CCDB API; allows to sync objects read from CCDB with the information
271 // stored in the registry; modifies given object as well as registry
272 virtual void syncCCDBandRegistry(void* obj) = 0;
273
274 protected:
275 // constructor is doing nothing else but
276 // registering the concrete parameters
278
279 friend std::ostream& operator<<(std::ostream& out, const ConfigurableParam& me);
280
281 static void initPropertyTree();
282 static EParamUpdateStatus updateThroughStorageMap(std::string, std::string, std::type_info const&, void*);
283 static EParamUpdateStatus updateThroughStorageMapWithConversion(std::string const&, std::string const&);
284
285 virtual ~ConfigurableParam() = default;
286
287 // fill property tree with the key-values from the sub-classes
288 virtual void putKeyValues(boost::property_tree::ptree*) = 0;
289 virtual void output(std::ostream& out) const = 0;
290
291 virtual void serializeTo(TFile*) const = 0;
292 virtual void initFrom(TFile*) = 0;
293
294 // static map keeping, for each configuration key, its memory location and type
295 // (internal use to easily sync updates, this is ok since parameter classes are singletons)
296 static std::map<std::string, std::pair<std::type_info const&, void*>>* sKeyToStorageMap;
297
298 // keep track of provenance of parameters and values
299 static std::map<std::string, ConfigurableParam::EParamProvenance>* sValueProvenanceMap;
300
301 // A registry of enum names and their allowed values
302 // (stored as a vector of pairs <enumValueLabel, enumValueInt>)
304
305 static std::string sOutputDir;
306
307 void setRegisterMode(bool b) { sRegisterMode = b; }
308 bool isInitialized() const { return sIsFullyInitialized; }
309
310 // friend class o2::ccdb::CcdbApi;
311 private:
312 // static registry for implementations of this type
313 static std::vector<ConfigurableParam*>* sRegisteredParamClasses;
314 // static property tree (stocking all key - value pairs from instances of type ConfigurableParam)
315 static boost::property_tree::ptree* sPtree;
316 static bool sIsFullyInitialized;
317 static bool sRegisterMode;
318};
319
320} // end namespace conf
321} // end namespace o2
322
323// a helper macro for boilerplate code in parameter classes
324#define O2ParamDef(classname, key) \
325 public: \
326 classname(TRootIOCtor*) {} \
327 classname(classname const&) = delete; \
328 \
329 private: \
330 static constexpr char const* const sKey = key; \
331 static classname sInstance; \
332 classname() = default; \
333 template <typename T> \
334 friend class o2::conf::ConfigurableParamHelper; \
335 template <typename T, typename P> \
336 friend class o2::conf::ConfigurableParamPromoter;
337
338// a helper macro to implement necessary symbols in source
339#define O2ParamImpl(classname) classname classname::sInstance;
340
341#endif /* COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAM_H_ */
StringRef key
friend std::ostream & operator<<(std::ostream &out, const ConfigurableParam &me)
virtual ~ConfigurableParam()=default
static EParamUpdateStatus updateThroughStorageMapWithConversion(std::string const &, std::string const &)
virtual void serializeTo(TFile *) const =0
virtual std::string getName() const =0
static void setEnumValue(const std::string &, const std::string &)
static void writeINI(std::string const &filename, std::string const &keyOnly="")
static void setValues(std::vector< std::pair< std::string, std::string > > const &keyValues)
static bool configFileExists(std::string const &filepath)
virtual void putKeyValues(boost::property_tree::ptree *)=0
static std::map< std::string, std::pair< std::type_info const &, void * > > * sKeyToStorageMap
static void setProvenance(std::string const &mainkey, std::string const &subkey, EParamProvenance p)
static void updateFromFile(std::string const &, std::string const &paramsList="", bool unchangedOnly=false)
static T getValueAs(std::string key)
virtual size_t getHash() const =0
virtual void initFrom(TFile *)=0
virtual EParamProvenance getMemberProvenance(const std::string &key) const =0
virtual void printKeyValues(bool showprov=true, bool useLogger=false) const =0
static void setArrayValue(const std::string &, const std::string &)
static void setValue(std::string const &mainkey, std::string const &subkey, T x)
static std::map< std::string, ConfigurableParam::EParamProvenance > * sValueProvenanceMap
static EParamProvenance getProvenance(const std::string &key)
static const std::string & getOutputDir()
static void printAllKeyValuePairs(bool useLogger=false)
static void toCCDB(std::string filename)
static void writeJSON(std::string const &filename, std::string const &keyOnly="")
static void setOutputDir(const std::string &d)
static std::string toString(EParamProvenance p)
static EParamUpdateStatus updateThroughStorageMap(std::string, std::string, std::type_info const &, void *)
static EnumRegistry * sEnumRegistry
virtual void output(std::ostream &out) const =0
virtual void syncCCDBandRegistry(void *obj)=0
static void fromCCDB(std::string filename)
static void updateFromString(std::string const &)
void add(const std::string &key, const TDataMember *dm)
std::string toString() const
bool contains(const std::string &key) const
const EnumLegalValues * operator[](const std::string &key) const
GLint GLenum GLint x
Definition glcorearb.h:403
const GLdouble * v
Definition glcorearb.h:832
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLsizei const GLfloat * value
Definition glcorearb.h:819
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string filename()
bool isLegal(int value) const
bool isLegal(const std::string &value) const
std::vector< std::pair< std::string, int > > vvalues
int getIntValue(const std::string &value) const
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))