FlagBase.h
1 /*
2 
3 MIT License
4 
5 Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6 
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13 
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 SOFTWARE.
24 
25 */
26 /*
27 Part of Rack development has been done in the BALTRAD projects part-financed
28 by the European Union (European Regional Development Fund and European
29 Neighbourhood Partnership Instrument, Baltic Sea Region Programme 2007-2013)
30 */
31 
32 #ifndef DRAIN_FLAG_BASE
33 #define DRAIN_FLAG_BASE
34 
35 #include <iostream>
36 #include <list>
37 #include <iterator>
38 #include <iostream>
39 #include <list>
40 #include <sstream>
41 #include <stdexcept>
42 #include <typeinfo>
43 
44 #include <drain/Log.h>
45 #include <drain/String.h>
46 #include <drain/Type.h>
47 
48 #include "Dictionary.h"
49 
50 namespace drain {
51 
53 
57 class FlagResolver {
58 
59 public:
60 
61  // Definite
62  typedef std::string key_t;
63 
64  // "Recommended" storage type for both numeric and enumerated-type flags.
65  typedef unsigned long int ivalue_t;
66 
69 
70  static
71  const ivalue_t ALL; // = std::numeric_limits<FlagResolver::ivalue_t>::max();
72 
73 
75 
80  // Consider: lenient: if true, bypasses unknown keys silently, otherwise throws exception
81  // Consider: FLAG for keys only, and for numerics throw exception
82  template <typename T>
83  static
84  ivalue_t getIntValue(const drain::Dictionary<key_t,T> & dict, const std::string & keys, char separator=',');
85 
87  template <typename T>
88  static
89  std::string getKeys(const drain::Dictionary<key_t,T> & dict, ivalue_t, char separator=',');
90 
92  template <typename T>
93  static
94  std::ostream & keysToStream(const drain::Dictionary<key_t,T> &dict, ivalue_t value, std::ostream & ostr, char separator=',');
95 
97 
102  template <typename T, typename V>
103  static
104  void valuesToList(ivalue_t value, const drain::Dictionary<key_t,T> &dict, std::list<V> & container);
105 
107 
110  template <typename T>
111  static
112  ivalue_t addEntry(drain::Dictionary<key_t,T> & dict, const key_t & key, ivalue_t i=0);
113 
114 
116 
119  template <typename T>
120  static
121  ivalue_t getFreeBit(const drain::Dictionary<key_t,T> & dict);
122 
123 
124 };
125 
126 
127 template <typename T>
128 typename FlagResolver::ivalue_t FlagResolver::getFreeBit(const drain::Dictionary<key_t,T> & dict){
129 
130  drain::Logger mout(__FILE__, __FUNCTION__);
131 
132  // Bit mask containing all the values.
133  ivalue_t currentMask = 0;
134  for (const auto & entry: dict){
135  currentMask = currentMask | entry.second;
136  }
137 
138  mout.debug2("sum:" , currentMask );
139 
140  ivalue_t i = 1;
141  while ((i&currentMask) > 0){
142  mout.debug3("checking bit:" , i , " vs.\t" , currentMask );
143  //full = (full>>1);
144  i = (i<<1);
145  }
146  /*
147  while (full>0){
148  mout.debug2("checking bit:" , i , " vs.\t" , full );
149  full = (full>>1);
150  i = (i<<1);
151  }
152  */
153 
154  mout.debug("released: " , i );
155 
156  return i;
157 
158 }
159 
160 /*
161 template <typename T>
162 typename FlagResolver::ivalue_t FlagResolver::addEntry(drain::Dictionary<key_t,T> &dict, const typename dict_t::key_t & key){
163 
164  drain::Logger mout(__FILE__, __FUNCTION__);
165  if (dict.hasKey(key)){
166  mout.info(key , " already in dict: " , dict );
167  return dict.getValue(key);
168  }
169 
170  return addEntry(dict, key, )
171 }
172 */
173 
174 template <typename T>
175 typename FlagResolver::ivalue_t FlagResolver::addEntry(drain::Dictionary<key_t,T> &dict, const typename dict_t::key_t & key, ivalue_t i){
176 
177  drain::Logger mout(__FILE__, __FUNCTION__);
178 
179  if (dict.hasKey(key)){
180  mout.info(key , " already in dict: " , dict );
181  return dict.getValue(key);
182  }
183 
184  if (i==0)
185  i = getFreeBit(dict);
186 
187  if (i>0){
188  dict.add(key, i);
189  }
190  else {
191  mout.warn(key , " could not get a valid (non-zero) bit flag for dict: " , dict );
192  }
193 
194  return i;
195 };
196 
197 
198 template <typename T>
199 typename FlagResolver::ivalue_t FlagResolver::getIntValue(const drain::Dictionary<key_t,T> & dict, const std::string & args, char separator){
200 
201  drain::Logger mout(__FILE__, __FUNCTION__);
202 
203  FlagResolver::ivalue_t v = 0;
204 
205  std::list<key_t> keys;
206 
207  if (!separator){
208  separator = dict.separator;
209  }
210 
211  if (separator){
212  drain::StringTools::split(args, keys, separator);
213  }
214 
215 
216  for (const key_t & key: keys){
217 
218  if (key == ""){
219  continue;
220  }
221  else if (key == "0"){
222  v = 0; // TODO if resets and sets?
223  continue;
224  }
225  // mout.warn(" '" , *it , "'" );
226 
227  //typename dict_t::const_iterator dit = dict.findByKey(key);
228  typename drain::Dictionary<key_t,T>::const_iterator dit = dict.findByKey(key);
229 
230  if (dit != dict.end()){ // String key match,
231  // Numeric value for an alphabetic key was found
232  v = (v | static_cast<FlagResolver::ivalue_t>(dit->second) );
233  }
234  else {
235  // Numeric value
236  FlagResolver::ivalue_t x = 0;
237  std::stringstream sstr(key);
238  sstr >> x; // FIX if empty
239  if (x == 0){
240  // String "0" handled already above
241  // Consider: could advice keys only: sprinter(dict.getKeys()
242  // Or key-value pairs: sprinter(dict)
243  // static const SprinterLayout cmdArgLayout = {",", "?", "=", ""};
244  // mout.error("key '", key, "' not found in: ", sprinter(dict, Sprinter::cmdLineLayout));
245  // mout.error("key '", key, "' not föund in: ", sprinter(dict.getContainer(), cmdArgLayout) );
246  mout.error("key '", key, "' not found in: ", dict);
247  //throw std::runtime_error(key, ": key not found in Flags, dict: ", dict);
248  }
249  v = v | x;
250  // Nice to know
251  /*
252  dict_t::const_iterator vit = dictionaryRef.findByValue(v);
253  if (vit != dictionaryRef.end()){
254  std::cout << "(assigned key '" << vit->second << "')\n"; // or vit->first?
255  }
256  */
257  }
258  }
259 
260  return v;
261 }
262 
263 
265 template <typename T>
266 std::string FlagResolver::getKeys(const drain::Dictionary<key_t,T> &dict, ivalue_t v, char separator){
267  std::stringstream sstr;
268  keysToStream(dict, v, sstr, separator);
269  return sstr.str();
270 }
271 
272 
273 
274 //
275 
277 template <typename T>
278 std::ostream & FlagResolver::keysToStream(const drain::Dictionary<FlagResolver::key_t,T> &dict, ivalue_t value, std::ostream & ostr, char separator) {
279 
280  /* note: instead of shifting bits of this->value, traverses the dictionary which can contain
281  - combined values
282  - repeated values (aliases)
283  */
284 
285  if (!separator)
286  separator = dict.separator;
287 
288  char sep = 0;
289 
290  for (const auto & entry: dict){
291  const ivalue_t v = (entry.second); // static_cast<ivalue_t>
292  //if ((entry.second > 0) && ((entry.second & value) == entry.second)){ // fully covered in value
293  //if ((entry.second == 0) && ((entry.second & value) == entry.second)){ // fully covered in value
294  if ((v > 0) && ((v & value) == v)){ // fully covered in value
295  if (sep)
296  ostr << sep;// "{" << (int)(sep) << "}" <<
297  else
298  sep = separator;
299  ostr << entry.first;
300  }
301  else {
302 
303  }
304  }
305 
306  return ostr;
307 }
308 
309 
310 
311 
312 template <typename T, typename V>
313 void FlagResolver::valuesToList(ivalue_t value, const drain::Dictionary<key_t,T> &dict, std::list<V> & container){
314  for (const auto & entry: dict){
315  if ((entry.second > 0) && ((entry.second & value) == entry.second)){ // fully covered in value
316  // ostr << entry.first;
317  container.push_back(static_cast<V>(entry.second));
318  // container.push_back(entry.first);
319  }
320  }
321 }
322 
323 
324 
325 // NEW 2022/2023
326 
327 
328 // typedef drain::Dictionary<std::string,unsigned long> FlaggerDict;
329 
330 // #define FlaggerDict FlagResolver::dict_t
331 
360 template <typename E=std::size_t, typename T=E> //, typename D=drain::Dictionary<std::string,unsigned long> >
361 class FlaggerBase {
362 
363 public:
364 
365  //static
366  //const SprinterLayout dictLayout = {",", "?", "=", ""};
367 
368  // Manifested numeric type (enum or unsigned integer)
369  typedef E value_t;
370  typedef T storage_t;
372  typedef typename dict_t::key_t key_t; // ~string
373 
374  // Practical "storage" value
375  // typedef typename dict_t::value_t dict_value_t; // TODO: xould be same as value_t ?
376 
377  // Rember to add an initialized unit: template<> SingleFlagger<...>::dict = {{...,...}, ...}
378  // static const dict_t dict;
379 
380  // Default constructor. Local value \c ownValue will be used.
384  inline
385  FlaggerBase(char separator=','): value(ownValue), separator(','), ownValue((storage_t)0){ // drain::NEUTRAL VALUE!
386  }
387 
389 
393  inline
394  FlaggerBase(storage_t & v, char separator=','): value(v), separator(','){
395  }
396 
397  /* Risky? (Ambiguous)
398  inline
399  FlaggerBase(const dict_value_t & v): value(ownValue), ownValue(v) {
400  }
401  */
402 
403  virtual inline
404  ~FlaggerBase(){
405  }
406 
408 
413  virtual
414  const dict_t & getDict() const = 0;
415 
416  virtual inline
417  void reset(){
418  this->value = storage_t(0); // ALERT! enums need neutral value.
419  };
420 
422  virtual inline
423  void set(const key_t & key){
424  if (key.empty())
425  return; // needed?
426  assign(key);
427  }
428  /*
429 
430  if (key.empty())
431  return;
432 
433  if ((key.find(',')!= std::string::npos)||(key.find('|')!= std::string::npos)){
434  Logger mout(__FILE__, __FUNCTION__);
435  mout.unimplemented<LOG_ERR>(__FILE__, ':', TypeName<E>::str(), ": multi-key arg: '"+ key, "', for single-flagger: ", sprinter(this->getDict().getKeys()));
436  }
437 
438  if (this->getDict().hasKey(key)){
439  this->value = static_cast<storage_t>(this->getDict().getValue(key)); // Cast needed (only) if MultiFlagger converts enum value_t -> storage_t
440  }
441  else {
442  // this->value = FlagResolver::getIntValue(this->getDict(), key); // SingleFlagger does not like this.
443  Logger mout(__FILE__, __FUNCTION__);
444  mout.error(__FILE__, ':', TypeName<E>::str(), ": no such key: '"+ key, "', keys=", sprinter(this->getDict().getKeys()));
445  // throw std::runtime_error(std::string("Dictionary[") + typeid(dict_value_t).name()+ "]: no such key: "+ key);
446  }
447  }
448  */
449 
451  virtual inline
452  void set(const value_t & value){
453  this->value = value;
454  };
455 
456  virtual inline
457  void set(const FlaggerBase<E> & flagger){
458  this->value = flagger.value;
459  }
460 
462 
466  virtual
467  bool isSet(const storage_t & x) const = 0;
468 
469  /*
470  inline
471  bool isSet(const value_t & x) const {
472  return (value & x) != 0; // enums...
473  };
474  */
475 
476  inline
477  bool isSet(const key_t & key) const {
478  return isSet(getDict().getValue(key));
479  };
480 
481  inline
482  const storage_t & getValue() const {
483  return value;
484  }
485 
487  /*
488  inline
489  std::string getKeys(char separator=0) const {
490  return FlagResolver::getKeys(dictionary, value, separator ? separator : this->separator);
491  }
492  */
493 
494 
496  inline
497  operator const storage_t & () const { // should be ivalue ?
498  return this->value;
499  }
500 
501  inline
502  operator storage_t & () { // should be ivalue ?
503  return this->value;
504  }
505 
506  inline
507  operator bool() const {
508  return static_cast<FlagResolver::ivalue_t>(this->value) != 0;
509  }
510 
511 
513  virtual
514  const key_t & str() const = 0;
515 
516  // String
517  operator const key_t & () const {
518  return str();
519  }
520 
521  // Own or external value.
522  storage_t & value;
523 
524  char separator;
525 
526  void debug(std::ostream & ostr) const;
527 
528 private:
529 
530  // protect?
531  virtual
532  void assign(const std::string & s) = 0;
533 
534 
535  // Own value, discarded if external value referenced.
536  storage_t ownValue = 0; // NEUTRAL
537 
538 };
539 
540 
541 template <typename E,typename T>
542 void drain::FlaggerBase<E,T>::debug(std::ostream & ostr) const {
543  //ostr << typeid(drain::FlaggerBase<E>).name() << ": value=" << getValue() << ", ";
544  ostr << drain::TypeName<E>::str() << ": value=" << getValue() << ", ";
545  FlagResolver::keysToStream(getDict(), getValue(), ostr) << ", ";
546  ostr << " dict: " << getDict();
547 }
548 
549 template <typename E,typename T>
550 inline
551 std::ostream & operator<<(std::ostream & ostr, const drain::FlaggerBase<E,T> & flagger) {
552  return ostr << flagger.str(); // flags.keysToStream(ostr);
553 }
554 
555 /*
556 template <typename E>
557 const SprinterLayout drain::FlaggerBase<E>::dictLayout = {",", "?", "=", ""};
558 */
559 
560 
561 
562 
563 } // drain::
564 
565 
566 #endif
const V & getValue(const K &key, bool lenient=true) const
Given a key, return the first value associated with it.
Definition: Dictionary.h:149
Referencing a dictionary of binary values: {"A",1: "B":2, "C": 4, "D": 8, ...} resolves two-way mappi...
Definition: FlagBase.h:57
static ivalue_t getIntValue(const drain::Dictionary< key_t, T > &dict, const std::string &keys, char separator=',')
Computes bitwise OR function on the numeric or alphabetic value(s) presented by a string.
Definition: FlagBase.h:199
drain::Dictionary< key_t, ivalue_t > dict_t
"Recommended" dictionary type. All the methods are templates, however.
Definition: FlagBase.h:68
static std::ostream & keysToStream(const drain::Dictionary< key_t, T > &dict, ivalue_t value, std::ostream &ostr, char separator=',')
Write keys in a stream, in numeric order.
Definition: FlagBase.h:278
static void valuesToList(ivalue_t value, const drain::Dictionary< key_t, T > &dict, std::list< V > &container)
Given a bit vector (integer value), extracts separate flag values to a list.
Definition: FlagBase.h:313
static ivalue_t addEntry(drain::Dictionary< key_t, T > &dict, const key_t &key, ivalue_t i=0)
Add a new entry in the dictionary.
static ivalue_t getFreeBit(const drain::Dictionary< key_t, T > &dict)
Return an interger (bit vector) with a new, previously unused value.
Definition: FlagBase.h:128
static std::string getKeys(const drain::Dictionary< key_t, T > &dict, ivalue_t, char separator=',')
Given an integer, retrieves dictionary keys corresponding to each index of set bits.
Definition: FlagBase.h:266
Definition: FlagBase.h:361
virtual bool isSet(const storage_t &x) const =0
Checks if a given bit, or any of given bits, is set.
virtual void set(const value_t &value)
Set a single flag.
Definition: FlagBase.h:452
virtual const key_t & str() const =0
String corresponding the current value. Returns empty, if not found.
virtual void set(const key_t &key)
Sets one or several flags.
Definition: FlagBase.h:423
FlaggerBase(storage_t &v, char separator=',')
Value-referencing constructor.
Definition: FlagBase.h:394
virtual const dict_t & getDict() const =0
Returns the static dictionary created for this value_t .
LogSourc e is the means for a function or any program segment to "connect" to a Log.
Definition: Log.h:310
Logger & error(const TT &... args)
Echoes.
Definition: Log.h:414
Logger & debug(const TT &... args)
Public, yet typically used "internally", when TIMING=true.
Definition: Log.h:678
Logger & debug2(const TT &... args)
Debug information.
Definition: Log.h:688
static void split(const std::string &s, T &sequence, const C &separators, const std::string &trimChars=" \t\n")
Splits and trims a given std::string to a std Sequence.
Definition: String.h:318
Definition: DataSelector.cpp:1277