Empirical
config.h
Go to the documentation of this file.
1 
37 #ifndef EMP_CONFIG_H
38 #define EMP_CONFIG_H
39 
40 
41 #include <map>
42 #include <ostream>
43 #include <fstream>
44 #include <functional>
45 #include <string>
46 #include <sstream>
47 #include <unordered_set>
48 
49 #include "../base/errors.h"
50 #include "../base/vector.h"
51 #include "../tools/functions.h"
52 #include "../tools/string_utils.h"
53 #include "ConfigManager.h"
54 
55 using namespace std::placeholders;
56 
57 namespace emp {
58 
60  class ConfigEntry {
61  protected:
62  std::string name;
63  std::string type;
64  std::string default_val;
65  std::string desc;
66 
67  std::unordered_set<std::string> alias_set;
68 
69  public:
70  ConfigEntry(const std::string _name, const std::string _type,
71  const std::string _d_val, const std::string _desc)
72  : name(_name), type(_type), default_val(_d_val), desc(_desc), alias_set()
73  { ; }
74  virtual ~ConfigEntry() { ; }
75 
76  const std::string & GetName() const { return name; }
77  const std::string & GetType() const { return type; }
78  const std::string & GetDefault() const { return default_val; }
79  const std::string & GetDescription() const { return desc; }
80 
81  ConfigEntry & SetName(const std::string & _in) { name = _in; return *this; }
82  ConfigEntry & SetType(const std::string & _in) { type = _in; return *this; }
83  ConfigEntry & SetDefault(const std::string & _in) { default_val = _in; return *this; }
84  ConfigEntry & SetDescription(const std::string & _in) { desc = _in; return *this; }
85 
87  ConfigEntry & AddAlias(const std::string & _in) { alias_set.insert(_in); return *this; }
88 
90  bool HasAlias(const std::string & _in) { return alias_set.find(_in) != alias_set.end(); }
91 
93  bool IsMatch(const std::string & _in) { return name == _in || HasAlias(_in); }
94 
96  const std::unordered_set<std::string> & GetAliases() { return alias_set; }
97 
99  virtual std::string GetValue() const = 0;
100 
102  virtual std::string GetLiteralValue() const = 0;
103 
105  virtual ConfigEntry & SetValue(const std::string & in_val, std::stringstream & warnings) = 0;
106 
108  virtual bool IsConst() const = 0;
109  };
110 
111 
113  class Config {
114  protected:
115 
117  template <class VAR_TYPE> class tConfigEntry : public ConfigEntry {
118  protected:
119  VAR_TYPE & entry_ref;
120  public:
121  tConfigEntry(const std::string _name, const std::string _type,
122  const std::string _d_val, const std::string _desc,
123  VAR_TYPE & _ref)
124  : ConfigEntry(_name, _type, _d_val, _desc), entry_ref(_ref) { ; }
126 
127  std::string GetValue() const { return emp::to_string(entry_ref); }
128  std::string GetLiteralValue() const { return to_literal(entry_ref); }
129  ConfigEntry & SetValue(const std::string & in_val, std::stringstream & /* warnings */) {
130  std::stringstream ss; ss << in_val; ss >> entry_ref; return *this;
131  }
132  bool IsConst() const { return false; }
133  };
134 
136  template <class VAR_TYPE> class tConfigConstEntry : public ConfigEntry {
137  protected:
138  const VAR_TYPE literal_val;
139  public:
140  tConfigConstEntry(const std::string _name, const std::string _type,
141  const std::string _d_val, const std::string _desc,
142  const VAR_TYPE & _literal_val)
143  : ConfigEntry(_name, _type, _d_val, _desc), literal_val(_literal_val) { ; }
145 
146  std::string GetValue() const { return default_val; }
147  std::string GetLiteralValue() const { return to_literal(literal_val); }
148  ConfigEntry & SetValue(const std::string & in_val, std::stringstream & warnings) {
149  // This is a constant setting. If we are actually trying to change it, give a warning.
150  if (in_val != GetValue()) {
151  warnings << "Trying to adjust locked setting '"
152  << name << "' from '" << GetValue()
153  << "' to '" << in_val << "'. Ignoring." << std::endl;
154  }
155  return *this;
156  }
157  bool IsConst() const { return true; }
158  };
159 
161  class ConfigLiveEntry : public ConfigEntry {
162  public:
163  ConfigLiveEntry(const std::string _name, const std::string _type,
164  const std::string _d_val, const std::string _desc)
165  : ConfigEntry(_name, _type, _d_val, _desc) { ; }
167 
168  std::string GetValue() const { return default_val; }
169  std::string GetLiteralValue() const { return to_literal(default_val); }
170  ConfigEntry & SetValue(const std::string & in_val, std::stringstream & warnings) {
171  (void) warnings;
172  default_val = in_val;
173  return *this;
174  }
175  bool IsConst() const { return false; }
176  };
177 
179  class ConfigGroup {
180  protected:
181  std::string name;
182  std::string desc;
184  public:
185  ConfigGroup(const std::string & _name, const std::string & _desc)
186  : name(_name), desc(_desc), entry_set()
187  { ; }
188  ~ConfigGroup() { ; }
189 
190  size_t GetSize() const { return entry_set.size(); }
191  ConfigEntry * GetEntry(size_t id) { return entry_set[id]; }
192  ConfigEntry * GetLastEntry() { emp_assert(GetSize() > 0); return entry_set.back(); }
193 
194  void Add(ConfigEntry * new_entry) { entry_set.push_back(new_entry); }
195 
196  void Write(std::ostream & out) {
197  // Print header information with the group name.
198  out << "### " << name << " ###" << std::endl;
199  // Print group description.
200  auto desc_lines = slice(desc);
201  for (size_t comment_line = 0; comment_line < desc_lines.size(); comment_line++) {
202  out << "# " << desc_lines[comment_line] << std::endl;
203  }
204  out << std::endl;
205 
206  const size_t entry_count = entry_set.size();
207  emp::vector<std::string> setting_info(entry_count);
208  size_t max_length = 0;
209 
210  // Loop through once to figure out non-comment output
211  for (size_t i = 0; i < entry_count; i++) {
212  setting_info[i] = "set ";
213  setting_info[i] += entry_set[i]->GetName();
214  setting_info[i] += " ";
215  setting_info[i] += entry_set[i]->GetValue();
216  if (max_length < setting_info[i].size()) max_length = setting_info[i].size();
217  }
218 
219  max_length += 2;
220  for (size_t i = 0; i < entry_count; i++) {
221  out << setting_info[i];
222 
223  // Break the description up over multiple lines.
224  auto desc_lines = emp::slice(entry_set[i]->GetDescription());
225 
226  size_t start_col = setting_info[i].size();
227  for (size_t comment_line = 0; comment_line < desc_lines.size(); comment_line++) {
228  for (size_t ws = start_col; ws < max_length; ws++) out << ' ';
229  out << "# " << desc_lines[comment_line] << std::endl;
230  start_col = 0;
231  }
232  }
233 
234  out << std::endl; // Skip a line after each group.
235  }
236 
237  void WriteMacros(std::ostream & out, bool as_const) {
238  // Print header information to register group.
239  out << " GROUP(" << name << ", \"" << desc << "\"),\n";
240 
241  // Loop through once to figure out non-comment output
242  for (ConfigEntry * cur_entry : entry_set) {
243  if (as_const || cur_entry->IsConst()) { out << " CONST("; }
244  else { out << " VALUE("; }
245 
246  out << cur_entry->GetName() << ", "
247  << cur_entry->GetType() << ", "
248  << cur_entry->GetLiteralValue() << ", "
249  << to_literal( cur_entry->GetDescription() )
250  << "),\n";
251 
252  // Output aliases.
253  const std::unordered_set<std::string> & alias_set = cur_entry->GetAliases();
254  for (const std::string & cur_alias : alias_set) {
255  out << " ALIAS(" << cur_alias << "),\n";
256  }
257  }
258 
259  out << std::endl; // Skip a line after each group.
260  }
261  };
262 
263  // === Helper Functions ===
265  if (group_set.size() == 0) {
266  group_set.push_back(new ConfigGroup("DEFAULT", "Default settings group"));
267  }
268  return group_set.back();
269  }
270 
272  ConfigGroup * group = GetActiveGroup();
273  emp_assert(group->GetSize() > 0);
274  return group->GetLastEntry();
275  }
276 
277  // Which characters can legally be part of a variable identifier?
278  bool IsVarChar(const char c) {
279  if (c >= 'a' && c <= 'z') return true;
280  if (c >= 'A' && c <= 'Z') return true;
281  if (c >= '0' && c <= '9') return true;
282  if (c == '_') return true;
283  return false;
284  }
285 
286  // Process a line by:
287  // * Remove excess whitespace
288  // * Expand all variables beginning with a $ in config line.
289  // * If wrap-around, move line to extras
290  void ProcessLine(std::string & cur_line, std::string & extras) {
291  size_t start_pos = extras.size(); // If there were extras last time, skip them.
292  if (extras.size()) cur_line.insert(0, extras);
293  extras.resize(0);
294  emp::left_justify(cur_line); // Clear out leading whitespace.
295 
296  for (size_t pos = start_pos; pos < cur_line.size(); pos++) {
297  const char cur_char = cur_line[pos];
298  // Check for escape characters and convert them appropriately.
299  if (cur_char == '\\') {
300  if (pos+1 == cur_line.size()) { // If backslash is at end of line...
301  extras = cur_line.substr(0, cur_line.size()-1); // ...move string to extras
302  cur_line.resize(0); // ...don't process current line
303  return; // ...since this is the line end, stop
304  }
305  // If we make it this far, we have a regular character being escaped. Make the swap!
306  const char esc_char = cur_line[pos+1];
307  switch (esc_char) {
308  case '$': cur_line.replace(pos, 2, "$"); break;
309  case '#': cur_line.replace(pos, 2, "#"); break;
310  case '\\': cur_line.replace(pos, 2, "\\"); break;
311  case 'n': cur_line.replace(pos, 2, "\n"); break;
312  case 'r': cur_line.replace(pos, 2, "\r"); break;
313  case 't': cur_line.replace(pos, 2, "\t"); break;
314  }
315  }
316  // A '#' indicates that a comment is starting that the rest of the line should be removed.
317  else if (cur_char == '#') {
318  cur_line.resize(pos);
319  }
320  // A '$' indicates that we should expand a variable in place.
321  else if (cur_char == '$' && expand_ok) {
322  size_t end_pos = pos+1;
323  while (end_pos < cur_line.size() && IsVarChar(cur_line[end_pos])) end_pos++;
324  const size_t var_size = end_pos - pos - 1;
325  std::string var_name(cur_line, pos+1, var_size);
326 
327  if (ResolveAlias(var_name)) {
328  std::string new_val = var_map[var_name]->GetValue(); // Lookup variable value.
329  cur_line.replace(pos, var_size+1, new_val); // Replace var name with value.
330  pos += new_val.size(); // Skip new text.
331  } else {
332  std::stringstream ss;
333  ss << "Unable to process config setting '$" << var_name << "'. Ignoring." << std::endl;
334  emp::NotifyError(ss.str());
335  }
336  // @CAO CONTINUE
337  }
338  }
339 
340  }
341 
342  // === Protected member variables ===
343  emp::vector<std::string> class_names; // Names in class heiarchy.
344  std::map<std::string, ConfigEntry *> var_map; // All variables across groups.
345  std::string version_id; // Unique version ID to ensure synced config.
346  emp::vector<ConfigGroup *> group_set; // All of the config groups.
347  std::stringstream warnings; // Aggrigate warnings for combined display.
348  int delay_warnings; // Count of delays to collect warnings for printing.
349  std::map<std::string, std::string> alias_map; // Map all aliases to original name.
350 
351  // Map new type names to the manager that handles them.
352  std::map<std::string, ConfigManager_Base *> type_manager_map;
353 
354  // Build a map of extra input commands to the function that they should call if triggered.
355  std::map<std::string, std::function<bool(std::string)> > command_map;
356  std::map<std::string, std::function<bool(std::string)> > new_map;
357  std::map<std::string, std::function<bool(std::string)> > use_map;
358 
359  // Instructions on how config should behave.
360  bool expand_ok; // Should we expand variables in the config file.
361 
362  public:
363  Config(const std::string & in_version = "")
364  : class_names(), var_map(), version_id(in_version), group_set(), warnings()
365  , delay_warnings(0), alias_map(), type_manager_map(), command_map()
366  , new_map(), use_map(), expand_ok(true)
367  {
368  class_names.push_back("emp::Config");
369  }
370 
372  // Delete all alocated memory!
373  for (auto & x : var_map) delete x.second;
374  for (auto & x : group_set) delete x;
375  for (auto & x : type_manager_map) delete x.second;
376  }
377 
378  ConfigEntry * operator[](const std::string & name) { return var_map[name]; }
379  auto begin() -> decltype(var_map.begin()) { return var_map.begin(); }
380  auto end() -> decltype(var_map.end()) { return var_map.end(); }
381 
382  Config & SetExpandOK(bool ok=true) { expand_ok = ok; return *this; }
383 
384  bool Has(const std::string & setting_name) const {
385  return (var_map.find(setting_name) != var_map.end()) ||
386  (alias_map.find(setting_name) != alias_map.end());
387  }
388 
389  bool ResolveAlias(std::string & setting_name) const {
390  if (var_map.find(setting_name) != var_map.end()) return true;
391  if (alias_map.find(setting_name) != alias_map.end()) {
392  setting_name = alias_map.find(setting_name)->second;
393  return true;
394  }
395  return false;
396  }
397 
398  std::string Get(std::string setting_name) {
399  if (!ResolveAlias(setting_name)) return ""; // @CAO Print warning?
400  return var_map[setting_name]->GetValue();
401  }
402 
403  Config & Set(std::string setting_name, const std::string & new_value,
404  const std::string & in_desc="") {
405  if (!ResolveAlias(setting_name)) {
406  // This setting is not currently in the map! We should put it in, but let user know.
407  warnings << "Unknown setting '" << setting_name << "'. Creating." << std::endl;
408  var_map[setting_name] = new ConfigLiveEntry(setting_name, "std::string", new_value, in_desc);
409  GetActiveGroup()->Add(var_map[setting_name]);
410  }
411  var_map[setting_name]->SetValue(new_value, warnings);
412  if (!delay_warnings && warnings.rdbuf()->in_avail()) {
413  emp::NotifyWarning(warnings.str());
414  warnings.str(std::string()); // Clear the warnings.
415  }
416  return *this;
417  }
418 
419  std::string operator()(const std::string & setting_name) { return Get(setting_name); }
420 
421  Config & operator()(const std::string & setting_name, const std::string & new_value) {
422  return Set(setting_name, new_value);
423  }
424 
425  void AddAlias(const std::string & base_name, const std::string & alias_name) {
426  emp_assert( var_map.find(base_name) != var_map.end() ); // Make sure base exists.
427  emp_assert( !Has(alias_name) ); // Make sure alias does not!
428  alias_map[alias_name] = base_name;
429  var_map[base_name]->AddAlias(alias_name);
430  }
431 
432  // Generate a text representation (typically a file) for the state of Config
433  void Write(std::ostream & out) {
434  // @CAO Start by printing some file header information?
435 
436  // Next print each group and it's information.
437  for (auto it = group_set.begin(); it != group_set.end(); it++) {
438  (*it)->Write(out);
439  }
440  }
441 
442  // If a string is passed into Write, treat it as a filename.
443  void Write(std::string filename) {
444  std::ofstream out(filename);
445  Write(out);
446  out.close();
447  }
448 
449  // Generate a text representation (typically a file) for the state of Config
450  void WriteMacros(std::ostream & out, bool as_const=false) {
451  out << "/////////////////////////////////////////////////////////////////////////////////\n"
452  << "// This is an auto-generated file that defines a set of configuration options.\n"
453  << "//\n"
454  << "// To create a new config from scratch, the format is:\n"
455  << "// EMP_BUILD_CONFIG( CLASS_NAME, OPTIONS... )\n"
456  << "//\n"
457  << "// To extend an existing config, simply use:\n"
458  << "// EMP_EXTEND_CONFIG( NEW_NAME, BASE_CLASS, OPTIONS... )\n"
459  << "//\n"
460  << "// The available OPTIONS are:\n"
461  << "//\n"
462  << "// GROUP(group name, group description string)\n"
463  << "// Start a new group of configuration options. Group structure is preserved\n"
464  << "// when user-accessible configuration options are generated.\n"
465  << "//\n"
466  << "// VALUE(variable name, type, default value, description string)\n"
467  << "// Create a new setting in the emp::Config object that can be easily accessed.\n"
468  << "//\n"
469  << "// CONST(variable name, type, fixed value, description string)\n"
470  << "// Create a new configuration constant that cannot be changed. In practice,\n"
471  << "// allows broader optimizations in the code.\n"
472  << "//\n"
473  << "// ALIAS(alias name)\n"
474  << "// Include an alias for the previous setting. This command is useful to\n"
475  << "// maintain backward compatibility if names change in newer software versions.\n"
476  << "\n"
477  << "EMP_BUILD_CONFIG(" << class_names.back() << ","
478  << std::endl;
479 
480  // Next print each group and it's information.
481  for (auto it = group_set.begin(); it != group_set.end(); it++) {
482  (*it)->WriteMacros(out, as_const);
483  }
484 
485  out << ")" << std::endl;
486  }
487 
488  // If a string is passed into Write, treat it as a filename.
489  void WriteMacros(std::string filename, bool as_const=false) {
490  std::ofstream out(filename);
491  WriteMacros(out, as_const);
492  out.close();
493  }
494 
495 
498  bool Read(std::istream & input) {
499  // Load in the file one line at a time and process each line.
500  std::string cur_line, extras;
501  delay_warnings++;
502 
503  // Loop through the file until eof is hit (does this work for other streams?)
504  while (!input.eof()) {
505  std::getline(input, cur_line); // Get the current input line.
506  ProcessLine(cur_line, extras); // Clean up line; act on aliases.
507 
508  if (cur_line == "") continue; // Skip empty lines.
509 
510  std::string command = emp::string_pop_word(cur_line);
511 
512  if (command == "include") {
513  // Recursively include another configuration file.
514  std::string filename = emp::string_pop_word(cur_line);
515  Read(filename);
516  }
517  else if (command == "new") {
518  std::string type_name = emp::string_pop_word(cur_line);
519  // @CAO Make sure type exists!
520  // @CAO Make sure remainder of line is a single identifier.
521  new_map[type_name](cur_line);
522  }
523  else if (command == "set") {
524  // Set a specific value.
525  std::string setting_name = emp::string_pop_word(cur_line);
526  Set(setting_name, cur_line);
527  }
528  else if (command == "use") {
529  std::string type_name = emp::string_pop_word(cur_line);
530  // @CAO Make sure type exists!
531  use_map[type_name](cur_line);
532  }
533  else if (command_map.find(command) != command_map.end()) {
534  // Run this custom command.
535  command_map[command](cur_line);
536  }
537  else {
538  // We don't know this command... give an error and move on.
539  std::stringstream ss;
540  ss << "Unknown configuration command '" << command << "'. Ignoring." << std::endl;
541  emp::NotifyError(ss.str());
542  }
543  }
544 
545  // Print out all accumulated warnings (if any).
546  if (warnings.rdbuf()->in_avail()) {
547  emp::NotifyWarning(warnings.str());
548  warnings.str(std::string()); // Clear the warnings.
549  }
550  delay_warnings--;
551 
552  return true;
553  }
554 
555  bool Read(std::string filename) {
556  std::ifstream in_file(filename);
557  if (in_file.fail()) {
558  std::stringstream ss;
559  ss << "Unable to open config file '" << filename << "'. Ignoring." << std::endl;
560  emp::NotifyError(ss.str());
561  return false;
562  }
563  bool success = Read(in_file);
564  in_file.close();
565  return success;
566  }
567 
568 
569  void AddCommand(const std::string & command_name, std::function<bool(std::string)> command_fun) {
570  // Give a warning if we are re-defining an existing command.
571  if (command_map.find(command_name) != command_map.end()) {
572  warnings << "Re-defining command '" << command_name << "'. Allowing." << std::endl;
573  if (!delay_warnings) {
574  emp::NotifyWarning(warnings.str());
575  warnings.str(std::string()); // Clear the warnings.
576  }
577  }
578  command_map[command_name] = command_fun;
579  }
580 
581  void AddNewCallback(const std::string & type_name, std::function<bool(std::string)> new_fun) {
582  // Give a warning if we are re-defining an existing command.
583  if (new_map.find(type_name) != new_map.end()) {
584  warnings << "Re-defining config type '" << type_name << "'. Allowing." << std::endl;
585  if (!delay_warnings) {
586  emp::NotifyWarning(warnings.str());
587  warnings.str(std::string()); // Clear the warnings.
588  }
589  }
590  new_map[type_name] = new_fun;
591  }
592 
593  void AddUseCallback(const std::string & type_name, std::function<bool(std::string)> use_fun) {
594  // Give a warning if we are re-defining an existing command.
595  if (use_map.find(type_name) != use_map.end()) {
596  warnings << "Re-defining config type '" << type_name << "'. Allowing." << std::endl;
597  if (!delay_warnings) {
598  emp::NotifyWarning(warnings.str());
599  warnings.str(std::string()); // Clear the warnings.
600  }
601  }
602  use_map[type_name] = use_fun;
603  }
604 
605 
606  template <class MANAGED_TYPE>
607  void AddManagedType(const std::string & type_keyword, const std::string & command_keyword,
608  std::function<bool(MANAGED_TYPE &, std::string)> fun_callback)
609  {
610  ConfigManager<MANAGED_TYPE> * new_manager = new ConfigManager<MANAGED_TYPE>(type_keyword, command_keyword, fun_callback);
611  type_manager_map[type_keyword] = new_manager;
612 
613  AddCommand(command_keyword,
614  std::bind(&ConfigManager<MANAGED_TYPE>::CommandCallback, new_manager, _1) );
615  AddNewCallback(type_keyword,
616  std::bind(&ConfigManager<MANAGED_TYPE>::NewObject, new_manager, _1) );
617  AddUseCallback(type_keyword,
618  std::bind(&ConfigManager<MANAGED_TYPE>::UseObject, new_manager, _1) );
619  }
620 
621  };
622 
623 }
624 
625 // Below are macros that help build the config classes.
626 
627 // Check that all of the commands are legal so that sensible errors can be produced.
628 // (legal commands convert to two arguments; illeagal ones stay as one, so second arg is error!)
629 #define EMP_CONFIG__ERROR_CHECK(CMD) EMP_GET_ARG(2, EMP_CONFIG__ARG_OKAY_ ## CMD, \
630  static_assert(false, "Unknown Config option: " #CMD);, ~)
631 #define EMP_CONFIG__ARG_OKAY_VALUE(...) ~,
632 #define EMP_CONFIG__ARG_OKAY_CONST(...) ~,
633 #define EMP_CONFIG__ARG_OKAY_GROUP(...) ~,
634 #define EMP_CONFIG__ARG_OKAY_ALIAS(...) ~,
635 #define EMP_CONFIG__ARG_OKAY_ ~,
636 
637 
638 // Macros to handle declaration of protected member variables.
639 // Note, unneeded macros defined to nothing, as is extra ending in '_' to allow trailing comma.
640 #define EMP_CONFIG__DECLARE(CMD) EMP_CONFIG__DECLARE_ ## CMD
641 #define EMP_CONFIG__DECLARE_VALUE(NAME, TYPE, DEFAULT, DESC) TYPE m_ ## NAME;
642 #define EMP_CONFIG__DECLARE_CONST(NAME, TYPE, DEFAULT, DESC)
643 #define EMP_CONFIG__DECLARE_GROUP(NAME, DESC)
644 #define EMP_CONFIG__DECLARE_ALIAS(NAME)
645 #define EMP_CONFIG__DECLARE_
646 
647 // Macros to handle construction of vars.
648 #define EMP_CONFIG__CONSTRUCT(CMD) EMP_CONFIG__CONSTRUCT_ ## CMD
649 #define EMP_CONFIG__CONSTRUCT_VALUE(NAME, TYPE, DEFAULT, DESC) , m_ ## NAME(DEFAULT)
650 #define EMP_CONFIG__CONSTRUCT_CONST(NAME, TYPE, DEFAULT, DESC)
651 #define EMP_CONFIG__CONSTRUCT_GROUP(NAME, DESC)
652 #define EMP_CONFIG__CONSTRUCT_ALIAS(NAME)
653 #define EMP_CONFIG__CONSTRUCT_
654 
655 // Macros to initialize internal representation of variables.
656 #define EMP_CONFIG__INIT(CMD) EMP_CONFIG__INIT_ ## CMD
657 #define EMP_CONFIG__INIT_VALUE(NAME, TYPE, DEFAULT, DESC) \
658  var_map[#NAME] = new tConfigEntry<TYPE>(#NAME, #TYPE, #DEFAULT, DESC, m_ ## NAME); \
659  GetActiveGroup()->Add(var_map[#NAME]);
660 #define EMP_CONFIG__INIT_CONST(NAME, TYPE, VALUE, DESC) \
661  var_map[#NAME] = new tConfigConstEntry<TYPE>(#NAME, #TYPE, #VALUE, DESC, VALUE); \
662  GetActiveGroup()->Add(var_map[#NAME]);
663 #define EMP_CONFIG__INIT_GROUP(NAME, DESC) \
664  group_set.push_back(new ConfigGroup(#NAME, DESC));
665 #define EMP_CONFIG__INIT_ALIAS(NAME) \
666  AddAlias(GetActiveEntry()->GetName(), #NAME);
667 #define EMP_CONFIG__INIT_
668 
669 // Build Get and Set Accessors, as well as const check
670 #define EMP_CONFIG__ACCESS(CMD) EMP_CONFIG__ACCESS_ ## CMD
671 #define EMP_CONFIG__ACCESS_VALUE(NAME, TYPE, DEFAULT, DESC) \
672  inline const TYPE & NAME() const { return m_ ## NAME; } \
673  const TYPE & NAME(const TYPE & _in) { m_ ## NAME = _in; return m_ ## NAME; } \
674  bool NAME ## _is_const() const { return false; }
675 #define EMP_CONFIG__ACCESS_CONST(NAME, TYPE, VALUE, DESC) \
676  constexpr static TYPE NAME() { return VALUE; } \
677  TYPE NAME(const TYPE & _in) { \
678  std::stringstream ss; \
679  ss << "Trying to set const '" << #NAME << "'. Ignoring." << std::endl; \
680  emp::NotifyWarning(ss.str()); \
681  return VALUE; \
682  } \
683  bool NAME ## _is_const() const { return true; }
684 #define EMP_CONFIG__ACCESS_GROUP(NAME, DESC)
685 #define EMP_CONFIG__ACCESS_ALIAS(NAME)
686 #define EMP_CONFIG__ACCESS_
687 
688 #define EMP_BUILD_CONFIG(CLASS_NAME, ...) EMP_EXTEND_CONFIG(CLASS_NAME, emp::Config, __VA_ARGS__)
689 
690 #define EMP_EXTEND_CONFIG(CLASS_NAME, BASE_NAME, ...) \
691  EMP_WRAP_EACH(EMP_CONFIG__ERROR_CHECK, __VA_ARGS__) \
692  class CLASS_NAME : public BASE_NAME { \
693  protected: \
694  bool is_ ## CLASS_NAME; \
695  EMP_WRAP_EACH(EMP_CONFIG__DECLARE, __VA_ARGS__) \
696  public: \
697  CLASS_NAME() : is_ ## CLASS_NAME(true) \
698  EMP_WRAP_EACH(EMP_CONFIG__CONSTRUCT, __VA_ARGS__) \
699  { \
700  class_names.push_back(#CLASS_NAME); \
701  EMP_WRAP_EACH(EMP_CONFIG__INIT, __VA_ARGS__) \
702  } \
703  EMP_WRAP_EACH(EMP_CONFIG__ACCESS, __VA_ARGS__) \
704  };
705 
706 #endif
std::string name
Definition: config.h:62
const VAR_TYPE literal_val
Definition: config.h:138
std::string left_justify(std::string &in_string)
Remove all whitespace at the beginning of a string. Return the whitespace removed.
Definition: string_utils.h:348
auto end() -> decltype(var_map.end())
Definition: config.h:380
ConfigGroup(const std::string &_name, const std::string &_desc)
Definition: config.h:185
bool IsConst() const
Identify if this setting is fixed at compile time.
Definition: config.h:175
std::string to_string(ALL_TYPES &&...all_values)
Definition: string_utils.h:511
emp::vector< ConfigEntry * > entry_set
Definition: config.h:183
iterator end() noexcept
Definition: vector.h:155
const std::string & GetType() const
Definition: config.h:77
Config & operator()(const std::string &setting_name, const std::string &new_value)
Definition: config.h:421
~tConfigConstEntry()
Definition: config.h:144
std::map< std::string, std::function< bool(std::string)> > command_map
Definition: config.h:355
Master configuration class that manages all of the settings.
Definition: config.h:113
std::string desc
Definition: config.h:182
size_t GetSize() const
Definition: config.h:190
ConfigEntry & AddAlias(const std::string &_in)
Alert this setting that it is aliased to alternate possible names.
Definition: config.h:87
ConfigLiveEntry(const std::string _name, const std::string _type, const std::string _d_val, const std::string _desc)
Definition: config.h:163
ConfigGroup * GetActiveGroup()
Definition: config.h:264
~ConfigLiveEntry()
Definition: config.h:166
Config & Set(std::string setting_name, const std::string &new_value, const std::string &in_desc="")
Definition: config.h:403
void AddNewCallback(const std::string &type_name, std::function< bool(std::string)> new_fun)
Definition: config.h:581
void Write(std::ostream &out)
Definition: config.h:196
std::unordered_set< std::string > alias_set
Definition: config.h:67
std::string GetValue() const
Retrieve the value of this setting as a string.
Definition: config.h:127
static void slice(const std::string &in_string, emp::vector< std::string > &out_set, char delim='\n')
Cut up a string based on the provided delimitor; fill them in to the provided vector.
Definition: string_utils.h:421
std::string Get(std::string setting_name)
Definition: config.h:398
void push_back(PB_Ts &&...args)
Definition: vector.h:189
void NotifyError(Ts &&...msg)
End user has done something resulting in an non-recoverable problem.
Definition: errors.h:145
tConfigEntry(const std::string _name, const std::string _type, const std::string _d_val, const std::string _desc, VAR_TYPE &_ref)
Definition: config.h:121
Base class for all configuration settings.
Definition: config.h:60
auto begin() -> decltype(var_map.begin())
Definition: config.h:379
size_t size() const
Definition: vector.h:151
bool IsConst() const
Identify if this setting is fixed at compile time.
Definition: config.h:157
std::string GetValue() const
Retrieve the value of this setting as a string.
Definition: config.h:168
ConfigEntry * GetLastEntry()
Definition: config.h:192
std::string desc
Definition: config.h:65
std::map< std::string, std::function< bool(std::string)> > use_map
Definition: config.h:357
const std::string & GetDefault() const
Definition: config.h:78
void Write(std::ostream &out)
Definition: config.h:433
virtual ~ConfigEntry()
Definition: config.h:74
void WriteMacros(std::ostream &out, bool as_const)
Definition: config.h:237
Information about a sub-group of settings.
Definition: config.h:179
bool Has(const std::string &setting_name) const
Definition: config.h:384
const std::string & GetDescription() const
Definition: config.h:79
bool Read(std::istream &input)
Definition: config.h:498
ConfigEntry * GetEntry(size_t id)
Definition: config.h:191
ConfigEntry & SetValue(const std::string &in_val, std::stringstream &warnings)
Use a string to set the value of this setting.
Definition: config.h:170
std::string GetValue() const
Retrieve the value of this setting as a string.
Definition: config.h:146
ConfigEntry & SetValue(const std::string &in_val, std::stringstream &)
Use a string to set the value of this setting.
Definition: config.h:129
bool IsConst() const
Identify if this setting is fixed at compile time.
Definition: config.h:132
Type-specific and CONST versions of ConfigEntry class to manage fixed settings.
Definition: config.h:136
bool Read(std::string filename)
Definition: config.h:555
~tConfigEntry()
Definition: config.h:125
static const PrintStr endl("<br>")
Pre-define emp::endl to insert a "<br>" and thus acting like a newline.
tConfigConstEntry(const std::string _name, const std::string _type, const std::string _d_val, const std::string _desc, const VAR_TYPE &_literal_val)
Definition: config.h:140
std::string to_literal(const LIT_TYPE &value)
Take a value and convert it to a C++-style literal.
Definition: string_utils.h:99
ConfigEntry & SetDescription(const std::string &_in)
Definition: config.h:84
std::string GetLiteralValue() const
Conver the value of this setting into a literal that C++ would recognize as its current value...
Definition: config.h:147
std::map< std::string, ConfigEntry * > var_map
Definition: config.h:344
std::map< std::string, std::string > alias_map
Definition: config.h:349
bool ResolveAlias(std::string &setting_name) const
Definition: config.h:389
bool IsMatch(const std::string &_in)
Will the provided name match this setting?
Definition: config.h:93
emp::vector< ConfigGroup * > group_set
Definition: config.h:346
std::string type
Definition: config.h:63
std::map< std::string, std::function< bool(std::string)> > new_map
Definition: config.h:356
void WriteMacros(std::string filename, bool as_const=false)
Definition: config.h:489
const std::unordered_set< std::string > & GetAliases()
Retrieve the full set of aliases.
Definition: config.h:96
void AddUseCallback(const std::string &type_name, std::function< bool(std::string)> use_fun)
Definition: config.h:593
bool expand_ok
Definition: config.h:360
~Config()
Definition: config.h:371
std::string string_pop_word(std::string &in_string)
Remove a prefix of a string, up to the first whitespace, and return it.
Definition: string_utils.h:326
bool Has(const MAP_T &in_map, const KEY_T &key)
Take any map type, and run find to determine if a key is present.
Definition: map_utils.h:21
void Write(std::string filename)
Definition: config.h:443
std::string name
Definition: config.h:181
ConfigEntry * GetActiveEntry()
Definition: config.h:271
void ProcessLine(std::string &cur_line, std::string &extras)
Definition: config.h:290
ConfigEntry & SetName(const std::string &_in)
Definition: config.h:81
void Add(ConfigEntry *new_entry)
Definition: config.h:194
iterator begin() noexcept
Definition: vector.h:153
ConfigEntry(const std::string _name, const std::string _type, const std::string _d_val, const std::string _desc)
Definition: config.h:70
std::string default_val
Definition: config.h:64
void AddCommand(const std::string &command_name, std::function< bool(std::string)> command_fun)
Definition: config.h:569
If we are in emscripten, make sure to include the header.
Definition: array.h:37
const std::string & GetName() const
Definition: config.h:76
void NotifyWarning(Ts &&...msg)
End user has done something possibly a problem.
Definition: errors.h:141
Build a debug wrapper emp::vector around std::vector.
Definition: vector.h:42
bool HasAlias(const std::string &_in)
Are there any alternate names for this setting?
Definition: config.h:90
#define emp_assert(...)
Definition: assert.h:199
T & back()
Definition: vector.h:183
ConfigEntry & SetDefault(const std::string &_in)
Definition: config.h:83
std::string GetLiteralValue() const
Conver the value of this setting into a literal that C++ would recognize as its current value...
Definition: config.h:128
ConfigEntry * operator[](const std::string &name)
Definition: config.h:378
std::string GetLiteralValue() const
Conver the value of this setting into a literal that C++ would recognize as its current value...
Definition: config.h:169
std::map< std::string, ConfigManager_Base * > type_manager_map
Definition: config.h:352
std::string operator()(const std::string &setting_name)
Definition: config.h:419
ConfigEntry & SetValue(const std::string &in_val, std::stringstream &warnings)
Use a string to set the value of this setting.
Definition: config.h:148
void AddManagedType(const std::string &type_keyword, const std::string &command_keyword, std::function< bool(MANAGED_TYPE &, std::string)> fun_callback)
Definition: config.h:607
void WriteMacros(std::ostream &out, bool as_const=false)
Definition: config.h:450
std::stringstream warnings
Definition: config.h:347
void AddAlias(const std::string &base_name, const std::string &alias_name)
Definition: config.h:425
ConfigEntry & SetType(const std::string &_in)
Definition: config.h:82
Config(const std::string &in_version="")
Definition: config.h:363
Definition: ConfigManager.h:51
int delay_warnings
Definition: config.h:348
std::string version_id
Definition: config.h:345
constexpr size_t GetSize(T(&)[N])
Determine the size of a built-in array.
Definition: functions.h:81
~ConfigGroup()
Definition: config.h:188
emp::vector< std::string > class_names
Definition: config.h:343
Type-specific versions of ConfigEntry class to manage settings.
Definition: config.h:117
bool IsVarChar(const char c)
Definition: config.h:278
VAR_TYPE & entry_ref
Definition: config.h:119
Special settings entry for settings created during the run (only accissibly dynamically) ...
Definition: config.h:161
Config & SetExpandOK(bool ok=true)
Definition: config.h:382