zep
cpptoml.h
Go to the documentation of this file.
1 
7 #ifndef CPPTOML_H
8 #define CPPTOML_H
9 
10 #include <algorithm>
11 #include <cassert>
12 #include <clocale>
13 #include <cstdint>
14 #include <cstring>
15 #include <fstream>
16 #include <iomanip>
17 #include <map>
18 #include <memory>
19 #include <sstream>
20 #include <stdexcept>
21 #include <string>
22 #include <unordered_map>
23 #include <vector>
24 
25 #if __cplusplus > 201103L
26 #define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]]
27 #elif defined(__clang__)
28 #define CPPTOML_DEPRECATED(reason) __attribute__((deprecated(reason)))
29 #elif defined(__GNUG__)
30 #define CPPTOML_DEPRECATED(reason) __attribute__((deprecated))
31 #elif defined(_MSC_VER)
32 #if _MSC_VER < 1910
33 #define CPPTOML_DEPRECATED(reason) __declspec(deprecated)
34 #else
35 #define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]]
36 #endif
37 #endif
38 
39 namespace cpptoml
40 {
41 class writer; // forward declaration
42 class base; // forward declaration
43 #if defined(CPPTOML_USE_MAP)
44 // a std::map will ensure that entries a sorted, albeit at a slight
45 // performance penalty relative to the (default) unordered_map
46 using string_to_base_map = std::map<std::string, std::shared_ptr<base>>;
47 #else
48 // by default an unordered_map is used for best performance as the
49 // toml specification does not require entries to be sorted
50 using string_to_base_map
51  = std::unordered_map<std::string, std::shared_ptr<base>>;
52 #endif
53 
54 // if defined, `base` will retain type information in form of an enum class
55 // such that static_cast can be used instead of dynamic_cast
56 // #define CPPTOML_NO_RTTI
57 
58 template <class T>
59 class option
60 {
61  public:
62  option() : empty_{true}, value_()
63  {
64  // nothing
65  }
66 
67  option(T value) : empty_{false}, value_(std::move(value))
68  {
69  // nothing
70  }
71 
72  explicit operator bool() const
73  {
74  return !empty_;
75  }
76 
77  const T& operator*() const
78  {
79  return value_;
80  }
81 
82  const T* operator->() const
83  {
84  return &value_;
85  }
86 
87  template <class U>
88  T value_or(U&& alternative) const
89  {
90  if (!empty_)
91  return value_;
92  return static_cast<T>(std::forward<U>(alternative));
93  }
94 
95  private:
96  bool empty_;
97  T value_;
98 };
99 
101 {
102  int year = 0;
103  int month = 0;
104  int day = 0;
105 };
106 
108 {
109  int hour = 0;
110  int minute = 0;
111  int second = 0;
112  int microsecond = 0;
113 };
114 
116 {
117  int hour_offset = 0;
118  int minute_offset = 0;
119 };
120 
122 {
123 };
124 
126 {
127  static inline struct offset_datetime from_zoned(const struct tm& t)
128  {
129  offset_datetime dt;
130  dt.year = t.tm_year + 1900;
131  dt.month = t.tm_mon + 1;
132  dt.day = t.tm_mday;
133  dt.hour = t.tm_hour;
134  dt.minute = t.tm_min;
135  dt.second = t.tm_sec;
136 
137  char buf[16];
138  strftime(buf, 16, "%z", &t);
139 
140  int offset = std::stoi(buf);
141  dt.hour_offset = offset / 100;
142  dt.minute_offset = offset % 100;
143  return dt;
144  }
145 
146  CPPTOML_DEPRECATED("from_local has been renamed to from_zoned")
147  static inline struct offset_datetime from_local(const struct tm& t)
148  {
149  return from_zoned(t);
150  }
151 
152  static inline struct offset_datetime from_utc(const struct tm& t)
153  {
154  offset_datetime dt;
155  dt.year = t.tm_year + 1900;
156  dt.month = t.tm_mon + 1;
157  dt.day = t.tm_mday;
158  dt.hour = t.tm_hour;
159  dt.minute = t.tm_min;
160  dt.second = t.tm_sec;
161  return dt;
162  }
163 };
164 
165 CPPTOML_DEPRECATED("datetime has been renamed to offset_datetime")
166 typedef offset_datetime datetime;
167 
169 {
170  public:
171  fill_guard(std::ostream& os) : os_(os), fill_{os.fill()}
172  {
173  // nothing
174  }
175 
176  ~fill_guard()
177  {
178  os_.fill(fill_);
179  }
180 
181  private:
182  std::ostream& os_;
183  std::ostream::char_type fill_;
184 };
185 
186 inline std::ostream& operator<<(std::ostream& os, const local_date& dt)
187 {
188  fill_guard g{os};
189  os.fill('0');
190 
191  using std::setw;
192  os << setw(4) << dt.year << "-" << setw(2) << dt.month << "-" << setw(2)
193  << dt.day;
194 
195  return os;
196 }
197 
198 inline std::ostream& operator<<(std::ostream& os, const local_time& ltime)
199 {
200  fill_guard g{os};
201  os.fill('0');
202 
203  using std::setw;
204  os << setw(2) << ltime.hour << ":" << setw(2) << ltime.minute << ":"
205  << setw(2) << ltime.second;
206 
207  if (ltime.microsecond > 0)
208  {
209  os << ".";
210  int power = 100000;
211  for (int curr_us = ltime.microsecond; curr_us; power /= 10)
212  {
213  auto num = curr_us / power;
214  os << num;
215  curr_us -= num * power;
216  }
217  }
218 
219  return os;
220 }
221 
222 inline std::ostream& operator<<(std::ostream& os, const zone_offset& zo)
223 {
224  fill_guard g{os};
225  os.fill('0');
226 
227  using std::setw;
228 
229  if (zo.hour_offset != 0 || zo.minute_offset != 0)
230  {
231  if (zo.hour_offset > 0)
232  {
233  os << "+";
234  }
235  else
236  {
237  os << "-";
238  }
239  os << setw(2) << std::abs(zo.hour_offset) << ":" << setw(2)
240  << std::abs(zo.minute_offset);
241  }
242  else
243  {
244  os << "Z";
245  }
246 
247  return os;
248 }
249 
250 inline std::ostream& operator<<(std::ostream& os, const local_datetime& dt)
251 {
252  return os << static_cast<const local_date&>(dt) << "T"
253  << static_cast<const local_time&>(dt);
254 }
255 
256 inline std::ostream& operator<<(std::ostream& os, const offset_datetime& dt)
257 {
258  return os << static_cast<const local_datetime&>(dt)
259  << static_cast<const zone_offset&>(dt);
260 }
261 
262 template <class T, class... Ts>
263 struct is_one_of;
264 
265 template <class T, class V>
266 struct is_one_of<T, V> : std::is_same<T, V>
267 {
268 };
269 
270 template <class T, class V, class... Ts>
271 struct is_one_of<T, V, Ts...>
272 {
273  const static bool value
274  = std::is_same<T, V>::value || is_one_of<T, Ts...>::value;
275 };
276 
277 template <class T>
278 class value;
279 
280 template <class T>
282  : is_one_of<T, std::string, int64_t, double, bool, local_date, local_time,
283  local_datetime, offset_datetime>
284 {
285 };
286 
287 template <class T, class Enable = void>
289 
290 template <class T>
292 {
293 
295  || std::is_convertible<T, std::string>::value;
296 };
297 
298 template <class T>
299 struct value_traits<T, typename std::enable_if<
300  valid_value_or_string_convertible<T>::value>::type>
301 {
302  using value_type = typename std::conditional<
304  typename std::decay<T>::type, std::string>::type;
305 
306  using type = value<value_type>;
307 
308  static value_type construct(T&& val)
309  {
310  return value_type(val);
311  }
312 };
313 
314 template <class T>
316  T,
317  typename std::enable_if<
318  !valid_value_or_string_convertible<T>::value
319  && std::is_floating_point<typename std::decay<T>::type>::value>::type>
320 {
321  using value_type = typename std::decay<T>::type;
322 
323  using type = value<double>;
324 
325  static value_type construct(T&& val)
326  {
327  return value_type(val);
328  }
329 };
330 
331 template <class T>
333  T, typename std::enable_if<
334  !valid_value_or_string_convertible<T>::value
335  && !std::is_floating_point<typename std::decay<T>::type>::value
336  && std::is_signed<typename std::decay<T>::type>::value>::type>
337 {
338  using value_type = int64_t;
339 
340  using type = value<int64_t>;
341 
342  static value_type construct(T&& val)
343  {
344  if (val < (std::numeric_limits<int64_t>::min)())
345  throw std::underflow_error{"constructed value cannot be "
346  "represented by a 64-bit signed "
347  "integer"};
348 
349  if (val > (std::numeric_limits<int64_t>::max)())
350  throw std::overflow_error{"constructed value cannot be represented "
351  "by a 64-bit signed integer"};
352 
353  return static_cast<int64_t>(val);
354  }
355 };
356 
357 template <class T>
359  T, typename std::enable_if<
360  !valid_value_or_string_convertible<T>::value
361  && std::is_unsigned<typename std::decay<T>::type>::value>::type>
362 {
363  using value_type = int64_t;
364 
365  using type = value<int64_t>;
366 
367  static value_type construct(T&& val)
368  {
369  if (val > static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
370  throw std::overflow_error{"constructed value cannot be represented "
371  "by a 64-bit signed integer"};
372 
373  return static_cast<int64_t>(val);
374  }
375 };
376 
377 class array;
378 class table;
379 class table_array;
380 
381 template <class T>
383 {
385 };
386 
387 template <>
389 {
391 };
392 
393 template <class T>
394 inline std::shared_ptr<typename value_traits<T>::type> make_value(T&& val);
395 inline std::shared_ptr<array> make_array();
396 
397 namespace detail
398 {
399 template <class T>
400 inline std::shared_ptr<T> make_element();
401 }
402 
403 inline std::shared_ptr<table> make_table();
404 inline std::shared_ptr<table_array> make_table_array(bool is_inline = false);
405 
406 #if defined(CPPTOML_NO_RTTI)
407 enum class base_type
409 {
410  NONE,
411  STRING,
412  LOCAL_TIME,
413  LOCAL_DATE,
414  LOCAL_DATETIME,
415  OFFSET_DATETIME,
416  INT,
417  FLOAT,
418  BOOL,
419  TABLE,
420  ARRAY,
421  TABLE_ARRAY
422 };
423 
425 template <class T>
426 struct base_type_traits;
427 
428 template <>
429 struct base_type_traits<std::string>
430 {
431  static const base_type type = base_type::STRING;
432 };
433 
434 template <>
435 struct base_type_traits<local_time>
436 {
437  static const base_type type = base_type::LOCAL_TIME;
438 };
439 
440 template <>
441 struct base_type_traits<local_date>
442 {
443  static const base_type type = base_type::LOCAL_DATE;
444 };
445 
446 template <>
447 struct base_type_traits<local_datetime>
448 {
449  static const base_type type = base_type::LOCAL_DATETIME;
450 };
451 
452 template <>
453 struct base_type_traits<offset_datetime>
454 {
455  static const base_type type = base_type::OFFSET_DATETIME;
456 };
457 
458 template <>
459 struct base_type_traits<int64_t>
460 {
461  static const base_type type = base_type::INT;
462 };
463 
464 template <>
465 struct base_type_traits<double>
466 {
467  static const base_type type = base_type::FLOAT;
468 };
469 
470 template <>
471 struct base_type_traits<bool>
472 {
473  static const base_type type = base_type::BOOL;
474 };
475 
476 template <>
477 struct base_type_traits<table>
478 {
479  static const base_type type = base_type::TABLE;
480 };
481 
482 template <>
483 struct base_type_traits<array>
484 {
485  static const base_type type = base_type::ARRAY;
486 };
487 
488 template <>
489 struct base_type_traits<table_array>
490 {
491  static const base_type type = base_type::TABLE_ARRAY;
492 };
493 #endif
494 
498 class base : public std::enable_shared_from_this<base>
499 {
500  public:
501  virtual ~base() = default;
502 
503  virtual std::shared_ptr<base> clone() const = 0;
504 
508  virtual bool is_value() const
509  {
510  return false;
511  }
512 
516  virtual bool is_table() const
517  {
518  return false;
519  }
520 
524  std::shared_ptr<table> as_table()
525  {
526  if (is_table())
527  return std::static_pointer_cast<table>(shared_from_this());
528  return nullptr;
529  }
533  virtual bool is_array() const
534  {
535  return false;
536  }
537 
541  std::shared_ptr<array> as_array()
542  {
543  if (is_array())
544  return std::static_pointer_cast<array>(shared_from_this());
545  return nullptr;
546  }
547 
551  virtual bool is_table_array() const
552  {
553  return false;
554  }
555 
559  std::shared_ptr<table_array> as_table_array()
560  {
561  if (is_table_array())
562  return std::static_pointer_cast<table_array>(shared_from_this());
563  return nullptr;
564  }
565 
570  template <class T>
571  std::shared_ptr<value<T>> as();
572 
573  template <class T>
574  std::shared_ptr<const value<T>> as() const;
575 
576  template <class Visitor, class... Args>
577  void accept(Visitor&& visitor, Args&&... args) const;
578 
579 #if defined(CPPTOML_NO_RTTI)
580  base_type type() const
581  {
582  return type_;
583  }
584 
585  protected:
586  base(const base_type t) : type_(t)
587  {
588  // nothing
589  }
590 
591  private:
592  const base_type type_ = base_type::NONE;
593 
594 #else
595  protected:
596  base()
597  {
598  // nothing
599  }
600 #endif
601 };
602 
606 template <class T>
607 class value : public base
608 {
609  struct make_shared_enabler
610  {
611  // nothing; this is a private key accessible only to friends
612  };
613 
614  template <class U>
615  friend std::shared_ptr<typename value_traits<U>::type>
616  cpptoml::make_value(U&& val);
617 
618  public:
619  static_assert(valid_value<T>::value, "invalid value type");
620 
621  std::shared_ptr<base> clone() const override;
622 
623  value(const make_shared_enabler&, const T& val) : value(val)
624  {
625  // nothing; note that users cannot actually invoke this function
626  // because they lack access to the make_shared_enabler.
627  }
628 
629  bool is_value() const override
630  {
631  return true;
632  }
633 
637  T& get()
638  {
639  return data_;
640  }
641 
645  const T& get() const
646  {
647  return data_;
648  }
649 
650  private:
651  T data_;
652 
656 #if defined(CPPTOML_NO_RTTI)
657  value(const T& val) : base(base_type_traits<T>::type), data_(val)
658  {
659  }
660 #else
661  value(const T& val) : data_(val)
662  {
663  }
664 #endif
665 
666  value(const value& val) = delete;
667  value& operator=(const value& val) = delete;
668 };
669 
670 template <class T>
671 std::shared_ptr<typename value_traits<T>::type> make_value(T&& val)
672 {
673  using value_type = typename value_traits<T>::type;
674  using enabler = typename value_type::make_shared_enabler;
675  return std::make_shared<value_type>(
676  enabler{}, value_traits<T>::construct(std::forward<T>(val)));
677 }
678 
679 template <class T>
680 inline std::shared_ptr<value<T>> base::as()
681 {
682 #if defined(CPPTOML_NO_RTTI)
683  if (type() == base_type_traits<T>::type)
684  return std::static_pointer_cast<value<T>>(shared_from_this());
685  else
686  return nullptr;
687 #else
688  return std::dynamic_pointer_cast<value<T>>(shared_from_this());
689 #endif
690 }
691 
692 // special case value<double> to allow getting an integer parameter as a
693 // double value
694 template <>
695 inline std::shared_ptr<value<double>> base::as()
696 {
697 #if defined(CPPTOML_NO_RTTI)
698  if (type() == base_type::FLOAT)
699  return std::static_pointer_cast<value<double>>(shared_from_this());
700 
701  if (type() == base_type::INT)
702  {
703  auto v = std::static_pointer_cast<value<int64_t>>(shared_from_this());
704  return make_value<double>(static_cast<double>(v->get()));
705  }
706 #else
707  if (auto v = std::dynamic_pointer_cast<value<double>>(shared_from_this()))
708  return v;
709 
710  if (auto v = std::dynamic_pointer_cast<value<int64_t>>(shared_from_this()))
711  return make_value<double>(static_cast<double>(v->get()));
712 #endif
713 
714  return nullptr;
715 }
716 
717 template <class T>
718 inline std::shared_ptr<const value<T>> base::as() const
719 {
720 #if defined(CPPTOML_NO_RTTI)
721  if (type() == base_type_traits<T>::type)
722  return std::static_pointer_cast<const value<T>>(shared_from_this());
723  else
724  return nullptr;
725 #else
726  return std::dynamic_pointer_cast<const value<T>>(shared_from_this());
727 #endif
728 }
729 
730 // special case value<double> to allow getting an integer parameter as a
731 // double value
732 template <>
733 inline std::shared_ptr<const value<double>> base::as() const
734 {
735 #if defined(CPPTOML_NO_RTTI)
736  if (type() == base_type::FLOAT)
737  return std::static_pointer_cast<const value<double>>(
738  shared_from_this());
739 
740  if (type() == base_type::INT)
741  {
742  auto v = as<int64_t>();
743  // the below has to be a non-const value<double> due to a bug in
744  // libc++: https://llvm.org/bugs/show_bug.cgi?id=18843
745  return make_value<double>(static_cast<double>(v->get()));
746  }
747 #else
748  if (auto v
749  = std::dynamic_pointer_cast<const value<double>>(shared_from_this()))
750  return v;
751 
752  if (auto v = as<int64_t>())
753  {
754  // the below has to be a non-const value<double> due to a bug in
755  // libc++: https://llvm.org/bugs/show_bug.cgi?id=18843
756  return make_value<double>(static_cast<double>(v->get()));
757  }
758 #endif
759 
760  return nullptr;
761 }
762 
766 class array_exception : public std::runtime_error
767 {
768  public:
769  array_exception(const std::string& err) : std::runtime_error{err}
770  {
771  }
772 };
773 
774 class array : public base
775 {
776  public:
777  friend std::shared_ptr<array> make_array();
778 
779  std::shared_ptr<base> clone() const override;
780 
781  virtual bool is_array() const override
782  {
783  return true;
784  }
785 
786  using size_type = std::size_t;
787 
791  using iterator = std::vector<std::shared_ptr<base>>::iterator;
792 
796  using const_iterator = std::vector<std::shared_ptr<base>>::const_iterator;
797 
798  iterator begin()
799  {
800  return values_.begin();
801  }
802 
803  const_iterator begin() const
804  {
805  return values_.begin();
806  }
807 
808  iterator end()
809  {
810  return values_.end();
811  }
812 
813  const_iterator end() const
814  {
815  return values_.end();
816  }
817 
821  std::vector<std::shared_ptr<base>>& get()
822  {
823  return values_;
824  }
825 
829  const std::vector<std::shared_ptr<base>>& get() const
830  {
831  return values_;
832  }
833 
834  std::shared_ptr<base> at(size_t idx_source) const
835  {
836  return values_.at(idx_source);
837  }
838 
843  template <class T>
844  std::vector<std::shared_ptr<value<T>>> array_of() const
845  {
846  std::vector<std::shared_ptr<value<T>>> result(values_.size());
847 
848  std::transform(values_.begin(), values_.end(), result.begin(),
849  [&](std::shared_ptr<base> v) { return v->as<T>(); });
850 
851  return result;
852  }
853 
858  template <class T>
860  {
861  std::vector<T> result;
862  result.reserve(values_.size());
863 
864  for (const auto& val : values_)
865  {
866  if (auto v = val->as<T>())
867  result.push_back(v->get());
868  else
869  return {};
870  }
871 
872  return {std::move(result)};
873  }
874 
879  std::vector<std::shared_ptr<array>> nested_array() const
880  {
881  std::vector<std::shared_ptr<array>> result(values_.size());
882 
883  std::transform(values_.begin(), values_.end(), result.begin(),
884  [&](std::shared_ptr<base> v) -> std::shared_ptr<array> {
885  if (v->is_array())
886  return std::static_pointer_cast<array>(v);
887  return std::shared_ptr<array>{};
888  });
889 
890  return result;
891  }
892 
896  template <class T>
897  void push_back(const std::shared_ptr<value<T>>& val)
898  {
899  if (values_.empty() || values_[0]->as<T>())
900  {
901  values_.push_back(val);
902  }
903  else
904  {
905  throw array_exception{"Arrays must be homogenous."};
906  }
907  }
908 
912  void push_back(const std::shared_ptr<array>& val)
913  {
914  if (values_.empty() || values_[0]->is_array())
915  {
916  values_.push_back(val);
917  }
918  else
919  {
920  throw array_exception{"Arrays must be homogenous."};
921  }
922  }
923 
928  template <class T>
929  void push_back(T&& val, typename value_traits<T>::type* = 0)
930  {
931  push_back(make_value(std::forward<T>(val)));
932  }
933 
937  template <class T>
938  iterator insert(iterator position, const std::shared_ptr<value<T>>& value)
939  {
940  if (values_.empty() || values_[0]->as<T>())
941  {
942  return values_.insert(position, value);
943  }
944  else
945  {
946  throw array_exception{"Arrays must be homogenous."};
947  }
948  }
949 
953  iterator insert(iterator position, const std::shared_ptr<array>& value)
954  {
955  if (values_.empty() || values_[0]->is_array())
956  {
957  return values_.insert(position, value);
958  }
959  else
960  {
961  throw array_exception{"Arrays must be homogenous."};
962  }
963  }
964 
968  template <class T>
969  iterator insert(iterator position, T&& val,
970  typename value_traits<T>::type* = 0)
971  {
972  return insert(position, make_value(std::forward<T>(val)));
973  }
974 
979  {
980  return values_.erase(position);
981  }
982 
986  void clear()
987  {
988  values_.clear();
989  }
990 
994  void reserve(size_type n)
995  {
996  values_.reserve(n);
997  }
998 
999  private:
1000 #if defined(CPPTOML_NO_RTTI)
1001  array() : base(base_type::ARRAY)
1002  {
1003  // empty
1004  }
1005 #else
1006  array() = default;
1007 #endif
1008 
1009  template <class InputIterator>
1010  array(InputIterator begin, InputIterator end) : values_{begin, end}
1011  {
1012  // nothing
1013  }
1014 
1015  array(const array& obj) = delete;
1016  array& operator=(const array& obj) = delete;
1017 
1018  std::vector<std::shared_ptr<base>> values_;
1019 };
1020 
1021 inline std::shared_ptr<array> make_array()
1022 {
1023  struct make_shared_enabler : public array
1024  {
1025  make_shared_enabler()
1026  {
1027  // nothing
1028  }
1029  };
1030 
1031  return std::make_shared<make_shared_enabler>();
1032 }
1033 
1034 namespace detail
1035 {
1036 template <>
1037 inline std::shared_ptr<array> make_element<array>()
1038 {
1039  return make_array();
1040 }
1041 } // namespace detail
1042 
1047 template <>
1048 inline typename array_of_trait<array>::return_type
1049 array::get_array_of<array>() const
1050 {
1051  std::vector<std::shared_ptr<array>> result;
1052  result.reserve(values_.size());
1053 
1054  for (const auto& val : values_)
1055  {
1056  if (auto v = val->as_array())
1057  result.push_back(v);
1058  else
1059  return {};
1060  }
1061 
1062  return {std::move(result)};
1063 }
1064 
1065 class table;
1066 
1067 class table_array : public base
1068 {
1069  friend class table;
1070  friend std::shared_ptr<table_array> make_table_array(bool);
1071 
1072  public:
1073  std::shared_ptr<base> clone() const override;
1074 
1075  using size_type = std::size_t;
1076 
1080  using iterator = std::vector<std::shared_ptr<table>>::iterator;
1081 
1085  using const_iterator = std::vector<std::shared_ptr<table>>::const_iterator;
1086 
1087  iterator begin()
1088  {
1089  return array_.begin();
1090  }
1091 
1092  const_iterator begin() const
1093  {
1094  return array_.begin();
1095  }
1096 
1097  iterator end()
1098  {
1099  return array_.end();
1100  }
1101 
1102  const_iterator end() const
1103  {
1104  return array_.end();
1105  }
1106 
1107  virtual bool is_table_array() const override
1108  {
1109  return true;
1110  }
1111 
1112  std::vector<std::shared_ptr<table>>& get()
1113  {
1114  return array_;
1115  }
1116 
1117  const std::vector<std::shared_ptr<table>>& get() const
1118  {
1119  return array_;
1120  }
1121 
1125  void push_back(const std::shared_ptr<table>& val)
1126  {
1127  array_.push_back(val);
1128  }
1129 
1133  iterator insert(iterator position, const std::shared_ptr<table>& value)
1134  {
1135  return array_.insert(position, value);
1136  }
1137 
1142  {
1143  return array_.erase(position);
1144  }
1145 
1149  void clear()
1150  {
1151  array_.clear();
1152  }
1153 
1157  void reserve(size_type n)
1158  {
1159  array_.reserve(n);
1160  }
1161 
1167  bool is_inline() const
1168  {
1169  return is_inline_;
1170  }
1171 
1172  private:
1173 #if defined(CPPTOML_NO_RTTI)
1174  table_array(bool is_inline = false)
1175  : base(base_type::TABLE_ARRAY), is_inline_(is_inline)
1176  {
1177  // nothing
1178  }
1179 #else
1180  table_array(bool is_inline = false) : is_inline_(is_inline)
1181  {
1182  // nothing
1183  }
1184 #endif
1185 
1186  table_array(const table_array& obj) = delete;
1187  table_array& operator=(const table_array& rhs) = delete;
1188 
1189  std::vector<std::shared_ptr<table>> array_;
1190  const bool is_inline_ = false;
1191 };
1192 
1193 inline std::shared_ptr<table_array> make_table_array(bool is_inline)
1194 {
1195  struct make_shared_enabler : public table_array
1196  {
1197  make_shared_enabler(bool mse_is_inline) : table_array(mse_is_inline)
1198  {
1199  // nothing
1200  }
1201  };
1202 
1203  return std::make_shared<make_shared_enabler>(is_inline);
1204 }
1205 
1206 namespace detail
1207 {
1208 template <>
1209 inline std::shared_ptr<table_array> make_element<table_array>()
1210 {
1211  return make_table_array(true);
1212 }
1213 } // namespace detail
1214 
1215 // The below are overloads for fetching specific value types out of a value
1216 // where special casting behavior (like bounds checking) is desired
1217 
1218 template <class T>
1219 typename std::enable_if<!std::is_floating_point<T>::value
1220  && std::is_signed<T>::value,
1221  option<T>>::type
1222 get_impl(const std::shared_ptr<base>& elem)
1223 {
1224  if (auto v = elem->as<int64_t>())
1225  {
1226  if (v->get() < (std::numeric_limits<T>::min)())
1227  throw std::underflow_error{
1228  "T cannot represent the value requested in get"};
1229 
1230  if (v->get() > (std::numeric_limits<T>::max)())
1231  throw std::overflow_error{
1232  "T cannot represent the value requested in get"};
1233 
1234  return {static_cast<T>(v->get())};
1235  }
1236  else
1237  {
1238  return {};
1239  }
1240 }
1241 
1242 template <class T>
1243 typename std::enable_if<!std::is_same<T, bool>::value
1244  && std::is_unsigned<T>::value,
1245  option<T>>::type
1246 get_impl(const std::shared_ptr<base>& elem)
1247 {
1248  if (auto v = elem->as<int64_t>())
1249  {
1250  if (v->get() < 0)
1251  throw std::underflow_error{"T cannot store negative value in get"};
1252 
1253  if (static_cast<uint64_t>(v->get()) > (std::numeric_limits<T>::max)())
1254  throw std::overflow_error{
1255  "T cannot represent the value requested in get"};
1256 
1257  return {static_cast<T>(v->get())};
1258  }
1259  else
1260  {
1261  return {};
1262  }
1263 }
1264 
1265 template <class T>
1266 typename std::enable_if<!std::is_integral<T>::value
1267  || std::is_same<T, bool>::value,
1268  option<T>>::type
1269 get_impl(const std::shared_ptr<base>& elem)
1270 {
1271  if (auto v = elem->as<T>())
1272  {
1273  return {v->get()};
1274  }
1275  else
1276  {
1277  return {};
1278  }
1279 }
1280 
1284 class table : public base
1285 {
1286  public:
1287  friend class table_array;
1288  friend std::shared_ptr<table> make_table();
1289 
1290  std::shared_ptr<base> clone() const override;
1291 
1295  using iterator = string_to_base_map::iterator;
1296 
1300  using const_iterator = string_to_base_map::const_iterator;
1301 
1302  iterator begin()
1303  {
1304  return map_.begin();
1305  }
1306 
1307  const_iterator begin() const
1308  {
1309  return map_.begin();
1310  }
1311 
1312  iterator end()
1313  {
1314  return map_.end();
1315  }
1316 
1317  const_iterator end() const
1318  {
1319  return map_.end();
1320  }
1321 
1322  bool is_table() const override
1323  {
1324  return true;
1325  }
1326 
1327  bool empty() const
1328  {
1329  return map_.empty();
1330  }
1331 
1335  bool contains(const std::string& key) const
1336  {
1337  return map_.find(key) != map_.end();
1338  }
1339 
1345  bool contains_qualified(const std::string& key) const
1346  {
1347  return resolve_qualified(key);
1348  }
1349 
1354  std::shared_ptr<base> get(const std::string& key) const
1355  {
1356  return map_.at(key);
1357  }
1358 
1366  std::shared_ptr<base> get_qualified(const std::string& key) const
1367  {
1368  std::shared_ptr<base> p;
1369  resolve_qualified(key, &p);
1370  return p;
1371  }
1372 
1376  std::shared_ptr<table> get_table(const std::string& key) const
1377  {
1378  if (contains(key) && get(key)->is_table())
1379  return std::static_pointer_cast<table>(get(key));
1380  return nullptr;
1381  }
1382 
1387  std::shared_ptr<table> get_table_qualified(const std::string& key) const
1388  {
1389  if (contains_qualified(key) && get_qualified(key)->is_table())
1390  return std::static_pointer_cast<table>(get_qualified(key));
1391  return nullptr;
1392  }
1393 
1397  std::shared_ptr<array> get_array(const std::string& key) const
1398  {
1399  if (!contains(key))
1400  return nullptr;
1401  return get(key)->as_array();
1402  }
1403 
1407  std::shared_ptr<array> get_array_qualified(const std::string& key) const
1408  {
1409  if (!contains_qualified(key))
1410  return nullptr;
1411  return get_qualified(key)->as_array();
1412  }
1413 
1417  std::shared_ptr<table_array> get_table_array(const std::string& key) const
1418  {
1419  if (!contains(key))
1420  return nullptr;
1421  return get(key)->as_table_array();
1422  }
1423 
1428  std::shared_ptr<table_array>
1429  get_table_array_qualified(const std::string& key) const
1430  {
1431  if (!contains_qualified(key))
1432  return nullptr;
1433  return get_qualified(key)->as_table_array();
1434  }
1435 
1440  template <class T>
1441  option<T> get_as(const std::string& key) const
1442  {
1443  try
1444  {
1445  return get_impl<T>(get(key));
1446  }
1447  catch (const std::out_of_range&)
1448  {
1449  return {};
1450  }
1451  }
1452 
1458  template <class T>
1459  option<T> get_qualified_as(const std::string& key) const
1460  {
1461  try
1462  {
1463  return get_impl<T>(get_qualified(key));
1464  }
1465  catch (const std::out_of_range&)
1466  {
1467  return {};
1468  }
1469  }
1470 
1480  template <class T>
1481  inline typename array_of_trait<T>::return_type
1482  get_array_of(const std::string& key) const
1483  {
1484  if (auto v = get_array(key))
1485  {
1486  std::vector<T> result;
1487  result.reserve(v->get().size());
1488 
1489  for (const auto& b : v->get())
1490  {
1491  if (auto val = b->as<T>())
1492  result.push_back(val->get());
1493  else
1494  return {};
1495  }
1496  return {std::move(result)};
1497  }
1498 
1499  return {};
1500  }
1501 
1512  template <class T>
1513  inline typename array_of_trait<T>::return_type
1514  get_qualified_array_of(const std::string& key) const
1515  {
1516  if (auto v = get_array_qualified(key))
1517  {
1518  std::vector<T> result;
1519  result.reserve(v->get().size());
1520 
1521  for (const auto& b : v->get())
1522  {
1523  if (auto val = b->as<T>())
1524  result.push_back(val->get());
1525  else
1526  return {};
1527  }
1528  return {std::move(result)};
1529  }
1530 
1531  return {};
1532  }
1533 
1537  void insert(const std::string& key, const std::shared_ptr<base>& value)
1538  {
1539  map_[key] = value;
1540  }
1541 
1546  template <class T>
1547  void insert(const std::string& key, T&& val,
1548  typename value_traits<T>::type* = 0)
1549  {
1550  insert(key, make_value(std::forward<T>(val)));
1551  }
1552 
1556  void erase(const std::string& key)
1557  {
1558  map_.erase(key);
1559  }
1560 
1561  private:
1562 #if defined(CPPTOML_NO_RTTI)
1563  table() : base(base_type::TABLE)
1564  {
1565  // nothing
1566  }
1567 #else
1568  table()
1569  {
1570  // nothing
1571  }
1572 #endif
1573 
1574  table(const table& obj) = delete;
1575  table& operator=(const table& rhs) = delete;
1576 
1577  std::vector<std::string> split(const std::string& value,
1578  char separator) const
1579  {
1580  std::vector<std::string> result;
1581  std::string::size_type p = 0;
1582  std::string::size_type q;
1583  while ((q = value.find(separator, p)) != std::string::npos)
1584  {
1585  result.emplace_back(value, p, q - p);
1586  p = q + 1;
1587  }
1588  result.emplace_back(value, p);
1589  return result;
1590  }
1591 
1592  // If output parameter p is specified, fill it with the pointer to the
1593  // specified entry and throw std::out_of_range if it couldn't be found.
1594  //
1595  // Otherwise, just return true if the entry could be found or false
1596  // otherwise and do not throw.
1597  bool resolve_qualified(const std::string& key,
1598  std::shared_ptr<base>* p = nullptr) const
1599  {
1600  auto parts = split(key, '.');
1601  auto last_key = parts.back();
1602  parts.pop_back();
1603 
1604  auto cur_table = this;
1605  for (const auto& part : parts)
1606  {
1607  cur_table = cur_table->get_table(part).get();
1608  if (!cur_table)
1609  {
1610  if (!p)
1611  return false;
1612 
1613  throw std::out_of_range{key + " is not a valid key"};
1614  }
1615  }
1616 
1617  if (!p)
1618  return cur_table->map_.count(last_key) != 0;
1619 
1620  *p = cur_table->map_.at(last_key);
1621  return true;
1622  }
1623 
1624  string_to_base_map map_;
1625 };
1626 
1636 template <>
1637 inline typename array_of_trait<array>::return_type
1638 table::get_array_of<array>(const std::string& key) const
1639 {
1640  if (auto v = get_array(key))
1641  {
1642  std::vector<std::shared_ptr<array>> result;
1643  result.reserve(v->get().size());
1644 
1645  for (const auto& b : v->get())
1646  {
1647  if (auto val = b->as_array())
1648  result.push_back(val);
1649  else
1650  return {};
1651  }
1652 
1653  return {std::move(result)};
1654  }
1655 
1656  return {};
1657 }
1658 
1668 template <>
1669 inline typename array_of_trait<array>::return_type
1670 table::get_qualified_array_of<array>(const std::string& key) const
1671 {
1672  if (auto v = get_array_qualified(key))
1673  {
1674  std::vector<std::shared_ptr<array>> result;
1675  result.reserve(v->get().size());
1676 
1677  for (const auto& b : v->get())
1678  {
1679  if (auto val = b->as_array())
1680  result.push_back(val);
1681  else
1682  return {};
1683  }
1684 
1685  return {std::move(result)};
1686  }
1687 
1688  return {};
1689 }
1690 
1691 std::shared_ptr<table> make_table()
1692 {
1693  struct make_shared_enabler : public table
1694  {
1695  make_shared_enabler()
1696  {
1697  // nothing
1698  }
1699  };
1700 
1701  return std::make_shared<make_shared_enabler>();
1702 }
1703 
1704 namespace detail
1705 {
1706 template <>
1707 inline std::shared_ptr<table> make_element<table>()
1708 {
1709  return make_table();
1710 }
1711 } // namespace detail
1712 
1713 template <class T>
1714 std::shared_ptr<base> value<T>::clone() const
1715 {
1716  return make_value(data_);
1717 }
1718 
1719 inline std::shared_ptr<base> array::clone() const
1720 {
1721  auto result = make_array();
1722  result->reserve(values_.size());
1723  for (const auto& ptr : values_)
1724  result->values_.push_back(ptr->clone());
1725  return result;
1726 }
1727 
1728 inline std::shared_ptr<base> table_array::clone() const
1729 {
1730  auto result = make_table_array(is_inline());
1731  result->reserve(array_.size());
1732  for (const auto& ptr : array_)
1733  result->array_.push_back(ptr->clone()->as_table());
1734  return result;
1735 }
1736 
1737 inline std::shared_ptr<base> table::clone() const
1738 {
1739  auto result = make_table();
1740  for (const auto& pr : map_)
1741  result->insert(pr.first, pr.second->clone());
1742  return result;
1743 }
1744 
1748 class parse_exception : public std::runtime_error
1749 {
1750  public:
1751  parse_exception(const std::string& err) : std::runtime_error{err}
1752  {
1753  }
1754 
1755  parse_exception(const std::string& err, std::size_t line_number)
1756  : std::runtime_error{err + " at line " + std::to_string(line_number)}
1757  {
1758  }
1759 };
1760 
1761 inline bool is_number(char c)
1762 {
1763  return c >= '0' && c <= '9';
1764 }
1765 
1766 inline bool is_hex(char c)
1767 {
1768  return is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
1769 }
1770 
1774 template <class OnError>
1776 {
1777  public:
1778  consumer(std::string::iterator& it, const std::string::iterator& end,
1779  OnError&& on_error)
1780  : it_(it), end_(end), on_error_(std::forward<OnError>(on_error))
1781  {
1782  // nothing
1783  }
1784 
1785  void operator()(char c)
1786  {
1787  if (it_ == end_ || *it_ != c)
1788  on_error_();
1789  ++it_;
1790  }
1791 
1792  template <std::size_t N>
1793  void operator()(const char (&str)[N])
1794  {
1795  std::for_each(std::begin(str), std::end(str) - 1,
1796  [&](char c) { (*this)(c); });
1797  }
1798 
1799  void eat_or(char a, char b)
1800  {
1801  if (it_ == end_ || (*it_ != a && *it_ != b))
1802  on_error_();
1803  ++it_;
1804  }
1805 
1806  int eat_digits(int len)
1807  {
1808  int val = 0;
1809  for (int i = 0; i < len; ++i)
1810  {
1811  if (!is_number(*it_) || it_ == end_)
1812  on_error_();
1813  val = 10 * val + (*it_++ - '0');
1814  }
1815  return val;
1816  }
1817 
1818  void error()
1819  {
1820  on_error_();
1821  }
1822 
1823  private:
1824  std::string::iterator& it_;
1825  const std::string::iterator& end_;
1826  OnError on_error_;
1827 };
1828 
1829 template <class OnError>
1830 consumer<OnError> make_consumer(std::string::iterator& it,
1831  const std::string::iterator& end,
1832  OnError&& on_error)
1833 {
1834  return consumer<OnError>(it, end, std::forward<OnError>(on_error));
1835 }
1836 
1837 // replacement for std::getline to handle incorrectly line-ended files
1838 // https://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf
1839 namespace detail
1840 {
1841 inline std::istream& getline(std::istream& input, std::string& line)
1842 {
1843  line.clear();
1844 
1845  std::istream::sentry sentry{input, true};
1846  auto sb = input.rdbuf();
1847 
1848  while (true)
1849  {
1850  auto c = sb->sbumpc();
1851  if (c == '\r')
1852  {
1853  if (sb->sgetc() == '\n')
1854  c = sb->sbumpc();
1855  }
1856 
1857  if (c == '\n')
1858  return input;
1859 
1860  if (c == std::istream::traits_type::eof())
1861  {
1862  if (line.empty())
1863  input.setstate(std::ios::eofbit);
1864  return input;
1865  }
1866 
1867  line.push_back(static_cast<char>(c));
1868  }
1869 }
1870 } // namespace detail
1871 
1875 class parser
1876 {
1877  public:
1881  parser(std::istream& stream) : input_(stream)
1882  {
1883  // nothing
1884  }
1885 
1886  parser& operator=(const parser& parser) = delete;
1887 
1892  std::shared_ptr<table> parse()
1893  {
1894  std::shared_ptr<table> root = make_table();
1895 
1896  table* curr_table = root.get();
1897 
1898  while (detail::getline(input_, line_))
1899  {
1900  line_number_++;
1901  auto it = line_.begin();
1902  auto end = line_.end();
1903  consume_whitespace(it, end);
1904 
1905  if (it != end &&
1906  *it == 0)
1907  {
1908  it++;
1909  }
1910 
1911  if (it == end || *it == '#')
1912  continue;
1913  if (*it == '[')
1914  {
1915  curr_table = root.get();
1916  parse_table(it, end, curr_table);
1917  }
1918  else
1919  {
1920  parse_key_value(it, end, curr_table);
1921  consume_whitespace(it, end);
1922  eol_or_comment(it, end);
1923  }
1924  }
1925  return root;
1926  }
1927 
1928  private:
1929 #if defined _MSC_VER
1930  __declspec(noreturn)
1931 #elif defined __GNUC__
1932  __attribute__((noreturn))
1933 #endif
1934  void throw_parse_exception(const std::string& err)
1935  {
1936  throw parse_exception{err, line_number_};
1937  }
1938 
1939  void parse_table(std::string::iterator& it,
1940  const std::string::iterator& end, table*& curr_table)
1941  {
1942  // remove the beginning keytable marker
1943  ++it;
1944  if (it == end)
1945  throw_parse_exception("Unexpected end of table");
1946  if (*it == '[')
1947  parse_table_array(it, end, curr_table);
1948  else
1949  parse_single_table(it, end, curr_table);
1950  }
1951 
1952  void parse_single_table(std::string::iterator& it,
1953  const std::string::iterator& end,
1954  table*& curr_table)
1955  {
1956  if (it == end || *it == ']')
1957  throw_parse_exception("Table name cannot be empty");
1958 
1959  std::string full_table_name;
1960  bool inserted = false;
1961 
1962  auto key_end = [](char c) { return c == ']'; };
1963 
1964  auto key_part_handler = [&](const std::string& part) {
1965  if (part.empty())
1966  throw_parse_exception("Empty component of table name");
1967 
1968  if (!full_table_name.empty())
1969  full_table_name += '.';
1970  full_table_name += part;
1971 
1972  if (curr_table->contains(part))
1973  {
1974 #if !defined(__PGI)
1975  auto b = curr_table->get(part);
1976 #else
1977  // Workaround for PGI compiler
1978  std::shared_ptr<base> b = curr_table->get(part);
1979 #endif
1980  if (b->is_table())
1981  curr_table = static_cast<table*>(b.get());
1982  else if (b->is_table_array())
1983  curr_table = std::static_pointer_cast<table_array>(b)
1984  ->get()
1985  .back()
1986  .get();
1987  else
1988  throw_parse_exception("Key " + full_table_name
1989  + "already exists as a value");
1990  }
1991  else
1992  {
1993  inserted = true;
1994  curr_table->insert(part, make_table());
1995  curr_table = static_cast<table*>(curr_table->get(part).get());
1996  }
1997  };
1998 
1999  key_part_handler(parse_key(it, end, key_end, key_part_handler));
2000 
2001  if (it == end)
2002  throw_parse_exception(
2003  "Unterminated table declaration; did you forget a ']'?");
2004 
2005  if (*it != ']')
2006  {
2007  std::string errmsg{"Unexpected character in table definition: "};
2008  errmsg += '"';
2009  errmsg += *it;
2010  errmsg += '"';
2011  throw_parse_exception(errmsg);
2012  }
2013 
2014  // table already existed
2015  if (!inserted)
2016  {
2017  auto is_value
2018  = [](const std::pair<const std::string&,
2019  const std::shared_ptr<base>&>& p) {
2020  return p.second->is_value();
2021  };
2022 
2023  // if there are any values, we can't add values to this table
2024  // since it has already been defined. If there aren't any
2025  // values, then it was implicitly created by something like
2026  // [a.b]
2027  if (curr_table->empty()
2028  || std::any_of(curr_table->begin(), curr_table->end(),
2029  is_value))
2030  {
2031  throw_parse_exception("Redefinition of table "
2032  + full_table_name);
2033  }
2034  }
2035 
2036  ++it;
2037  consume_whitespace(it, end);
2038  eol_or_comment(it, end);
2039  }
2040 
2041  void parse_table_array(std::string::iterator& it,
2042  const std::string::iterator& end, table*& curr_table)
2043  {
2044  ++it;
2045  if (it == end || *it == ']')
2046  throw_parse_exception("Table array name cannot be empty");
2047 
2048  auto key_end = [](char c) { return c == ']'; };
2049 
2050  std::string full_ta_name;
2051  auto key_part_handler = [&](const std::string& part) {
2052  if (part.empty())
2053  throw_parse_exception("Empty component of table array name");
2054 
2055  if (!full_ta_name.empty())
2056  full_ta_name += '.';
2057  full_ta_name += part;
2058 
2059  if (curr_table->contains(part))
2060  {
2061 #if !defined(__PGI)
2062  auto b = curr_table->get(part);
2063 #else
2064  // Workaround for PGI compiler
2065  std::shared_ptr<base> b = curr_table->get(part);
2066 #endif
2067 
2068  // if this is the end of the table array name, add an
2069  // element to the table array that we just looked up,
2070  // provided it was not declared inline
2071  if (it != end && *it == ']')
2072  {
2073  if (!b->is_table_array())
2074  {
2075  throw_parse_exception("Key " + full_ta_name
2076  + " is not a table array");
2077  }
2078 
2079  auto v = b->as_table_array();
2080 
2081  if (v->is_inline())
2082  {
2083  throw_parse_exception("Static array " + full_ta_name
2084  + " cannot be appended to");
2085  }
2086 
2087  v->get().push_back(make_table());
2088  curr_table = v->get().back().get();
2089  }
2090  // otherwise, just keep traversing down the key name
2091  else
2092  {
2093  if (b->is_table())
2094  curr_table = static_cast<table*>(b.get());
2095  else if (b->is_table_array())
2096  curr_table = std::static_pointer_cast<table_array>(b)
2097  ->get()
2098  .back()
2099  .get();
2100  else
2101  throw_parse_exception("Key " + full_ta_name
2102  + " already exists as a value");
2103  }
2104  }
2105  else
2106  {
2107  // if this is the end of the table array name, add a new
2108  // table array and a new table inside that array for us to
2109  // add keys to next
2110  if (it != end && *it == ']')
2111  {
2112  curr_table->insert(part, make_table_array());
2113  auto arr = std::static_pointer_cast<table_array>(
2114  curr_table->get(part));
2115  arr->get().push_back(make_table());
2116  curr_table = arr->get().back().get();
2117  }
2118  // otherwise, create the implicitly defined table and move
2119  // down to it
2120  else
2121  {
2122  curr_table->insert(part, make_table());
2123  curr_table
2124  = static_cast<table*>(curr_table->get(part).get());
2125  }
2126  }
2127  };
2128 
2129  key_part_handler(parse_key(it, end, key_end, key_part_handler));
2130 
2131  // consume the last "]]"
2132  auto eat = make_consumer(it, end, [this]() {
2133  throw_parse_exception("Unterminated table array name");
2134  });
2135  eat(']');
2136  eat(']');
2137 
2138  consume_whitespace(it, end);
2139  eol_or_comment(it, end);
2140  }
2141 
2142  void parse_key_value(std::string::iterator& it, std::string::iterator& end,
2143  table* curr_table)
2144  {
2145  auto key_end = [](char c) { return c == '='; };
2146 
2147  auto key_part_handler = [&](const std::string& part) {
2148  // two cases: this key part exists already, in which case it must
2149  // be a table, or it doesn't exist in which case we must create
2150  // an implicitly defined table
2151  if (curr_table->contains(part))
2152  {
2153  auto val = curr_table->get(part);
2154  if (val->is_table())
2155  {
2156  curr_table = static_cast<table*>(val.get());
2157  }
2158  else
2159  {
2160  throw_parse_exception("Key " + part
2161  + " already exists as a value");
2162  }
2163  }
2164  else
2165  {
2166  auto newtable = make_table();
2167  curr_table->insert(part, newtable);
2168  curr_table = newtable.get();
2169  }
2170  };
2171 
2172  auto key = parse_key(it, end, key_end, key_part_handler);
2173 
2174  if (curr_table->contains(key))
2175  throw_parse_exception("Key " + key + " already present");
2176  if (it == end || *it != '=')
2177  throw_parse_exception("Value must follow after a '='");
2178  ++it;
2179  consume_whitespace(it, end);
2180  curr_table->insert(key, parse_value(it, end));
2181  consume_whitespace(it, end);
2182  }
2183 
2184  template <class KeyEndFinder, class KeyPartHandler>
2185  std::string
2186  parse_key(std::string::iterator& it, const std::string::iterator& end,
2187  KeyEndFinder&& key_end, KeyPartHandler&& key_part_handler)
2188  {
2189  // parse the key as a series of one or more simple-keys joined with '.'
2190  while (it != end && !key_end(*it))
2191  {
2192  auto part = parse_simple_key(it, end);
2193  consume_whitespace(it, end);
2194 
2195  if (it == end || key_end(*it))
2196  {
2197  return part;
2198  }
2199 
2200  if (*it != '.')
2201  {
2202  std::string errmsg{"Unexpected character in key: "};
2203  errmsg += '"';
2204  errmsg += *it;
2205  errmsg += '"';
2206  throw_parse_exception(errmsg);
2207  }
2208 
2209  key_part_handler(part);
2210 
2211  // consume the dot
2212  ++it;
2213  }
2214 
2215  throw_parse_exception("Unexpected end of key");
2216  }
2217 
2218  std::string parse_simple_key(std::string::iterator& it,
2219  const std::string::iterator& end)
2220  {
2221  consume_whitespace(it, end);
2222 
2223  if (it == end)
2224  throw_parse_exception("Unexpected end of key (blank key?)");
2225 
2226  if (*it == '"' || *it == '\'')
2227  {
2228  return string_literal(it, end, *it);
2229  }
2230  else
2231  {
2232  auto bke = std::find_if(it, end, [](char c) {
2233  return c == '.' || c == '=' || c == ']';
2234  });
2235  return parse_bare_key(it, bke);
2236  }
2237  }
2238 
2239  std::string parse_bare_key(std::string::iterator& it,
2240  const std::string::iterator& end)
2241  {
2242  if (it == end)
2243  {
2244  throw_parse_exception("Bare key missing name");
2245  }
2246 
2247  auto key_end = end;
2248  --key_end;
2249  consume_backwards_whitespace(key_end, it);
2250  ++key_end;
2251  std::string key{it, key_end};
2252 
2253  if (std::find(it, key_end, '#') != key_end)
2254  {
2255  throw_parse_exception("Bare key " + key + " cannot contain #");
2256  }
2257 
2258  if (std::find_if(it, key_end,
2259  [](char c) { return c == ' ' || c == '\t'; })
2260  != key_end)
2261  {
2262  throw_parse_exception("Bare key " + key
2263  + " cannot contain whitespace");
2264  }
2265 
2266  if (std::find_if(it, key_end,
2267  [](char c) { return c == '[' || c == ']'; })
2268  != key_end)
2269  {
2270  throw_parse_exception("Bare key " + key
2271  + " cannot contain '[' or ']'");
2272  }
2273 
2274  it = end;
2275  return key;
2276  }
2277 
2278  enum class parse_type
2279  {
2280  STRING = 1,
2281  LOCAL_TIME,
2282  LOCAL_DATE,
2283  LOCAL_DATETIME,
2284  OFFSET_DATETIME,
2285  INT,
2286  FLOAT,
2287  BOOL,
2288  ARRAY,
2289  INLINE_TABLE
2290  };
2291 
2292  std::shared_ptr<base> parse_value(std::string::iterator& it,
2293  std::string::iterator& end)
2294  {
2295  parse_type type = determine_value_type(it, end);
2296  switch (type)
2297  {
2298  case parse_type::STRING:
2299  return parse_string(it, end);
2300  case parse_type::LOCAL_TIME:
2301  return parse_time(it, end);
2302  case parse_type::LOCAL_DATE:
2303  case parse_type::LOCAL_DATETIME:
2304  case parse_type::OFFSET_DATETIME:
2305  return parse_date(it, end);
2306  case parse_type::INT:
2307  case parse_type::FLOAT:
2308  return parse_number(it, end);
2309  case parse_type::BOOL:
2310  return parse_bool(it, end);
2311  case parse_type::ARRAY:
2312  return parse_array(it, end);
2313  case parse_type::INLINE_TABLE:
2314  return parse_inline_table(it, end);
2315  default:
2316  throw_parse_exception("Failed to parse value");
2317  }
2318  }
2319 
2320  parse_type determine_value_type(const std::string::iterator& it,
2321  const std::string::iterator& end)
2322  {
2323  if (it == end)
2324  {
2325  throw_parse_exception("Failed to parse value type");
2326  }
2327  if (*it == '"' || *it == '\'')
2328  {
2329  return parse_type::STRING;
2330  }
2331  else if (is_time(it, end))
2332  {
2333  return parse_type::LOCAL_TIME;
2334  }
2335  else if (auto dtype = date_type(it, end))
2336  {
2337  return *dtype;
2338  }
2339  else if (is_number(*it) || *it == '-' || *it == '+'
2340  || (*it == 'i' && it + 1 != end && it[1] == 'n'
2341  && it + 2 != end && it[2] == 'f')
2342  || (*it == 'n' && it + 1 != end && it[1] == 'a'
2343  && it + 2 != end && it[2] == 'n'))
2344  {
2345  return determine_number_type(it, end);
2346  }
2347  else if (*it == 't' || *it == 'f')
2348  {
2349  return parse_type::BOOL;
2350  }
2351  else if (*it == '[')
2352  {
2353  return parse_type::ARRAY;
2354  }
2355  else if (*it == '{')
2356  {
2357  return parse_type::INLINE_TABLE;
2358  }
2359  throw_parse_exception("Failed to parse value type");
2360  }
2361 
2362  parse_type determine_number_type(const std::string::iterator& it,
2363  const std::string::iterator& end)
2364  {
2365  // determine if we are an integer or a float
2366  auto check_it = it;
2367  if (*check_it == '-' || *check_it == '+')
2368  ++check_it;
2369 
2370  if (check_it == end)
2371  throw_parse_exception("Malformed number");
2372 
2373  if (*check_it == 'i' || *check_it == 'n')
2374  return parse_type::FLOAT;
2375 
2376  while (check_it != end && is_number(*check_it))
2377  ++check_it;
2378  if (check_it != end && *check_it == '.')
2379  {
2380  ++check_it;
2381  while (check_it != end && is_number(*check_it))
2382  ++check_it;
2383  return parse_type::FLOAT;
2384  }
2385  else
2386  {
2387  return parse_type::INT;
2388  }
2389  }
2390 
2391  std::shared_ptr<value<std::string>> parse_string(std::string::iterator& it,
2392  std::string::iterator& end)
2393  {
2394  auto delim = *it;
2395  assert(delim == '"' || delim == '\'');
2396 
2397  // end is non-const here because we have to be able to potentially
2398  // parse multiple lines in a string, not just one
2399  auto check_it = it;
2400  ++check_it;
2401  if (check_it != end && *check_it == delim)
2402  {
2403  ++check_it;
2404  if (check_it != end && *check_it == delim)
2405  {
2406  it = ++check_it;
2407  return parse_multiline_string(it, end, delim);
2408  }
2409  }
2410  return make_value<std::string>(string_literal(it, end, delim));
2411  }
2412 
2413  std::shared_ptr<value<std::string>>
2414  parse_multiline_string(std::string::iterator& it,
2415  std::string::iterator& end, char delim)
2416  {
2417  std::stringstream ss;
2418 
2419  auto is_ws = [](char c) { return c == ' ' || c == '\t'; };
2420 
2421  bool consuming = false;
2422  std::shared_ptr<value<std::string>> ret;
2423 
2424  auto handle_line = [&](std::string::iterator& local_it,
2425  std::string::iterator& local_end) {
2426  if (consuming)
2427  {
2428  local_it = std::find_if_not(local_it, local_end, is_ws);
2429 
2430  // whole line is whitespace
2431  if (local_it == local_end)
2432  return;
2433  }
2434 
2435  consuming = false;
2436 
2437  while (local_it != local_end)
2438  {
2439  // handle escaped characters
2440  if (delim == '"' && *local_it == '\\')
2441  {
2442  auto check = local_it;
2443  // check if this is an actual escape sequence or a
2444  // whitespace escaping backslash
2445  ++check;
2446  consume_whitespace(check, local_end);
2447  if (check == local_end)
2448  {
2449  consuming = true;
2450  break;
2451  }
2452 
2453  ss << parse_escape_code(local_it, local_end);
2454  continue;
2455  }
2456 
2457  // if we can end the string
2458  if (std::distance(local_it, local_end) >= 3)
2459  {
2460  auto check = local_it;
2461  // check for """
2462  if (*check++ == delim && *check++ == delim
2463  && *check++ == delim)
2464  {
2465  local_it = check;
2466  ret = make_value<std::string>(ss.str());
2467  break;
2468  }
2469  }
2470 
2471  ss << *local_it++;
2472  }
2473  };
2474 
2475  // handle the remainder of the current line
2476  handle_line(it, end);
2477  if (ret)
2478  return ret;
2479 
2480  // start eating lines
2481  while (detail::getline(input_, line_))
2482  {
2483  ++line_number_;
2484 
2485  it = line_.begin();
2486  end = line_.end();
2487 
2488  handle_line(it, end);
2489 
2490  if (ret)
2491  return ret;
2492 
2493  if (!consuming)
2494  ss << std::endl;
2495  }
2496 
2497  throw_parse_exception("Unterminated multi-line basic string");
2498  }
2499 
2500  std::string string_literal(std::string::iterator& it,
2501  const std::string::iterator& end, char delim)
2502  {
2503  ++it;
2504  std::string val;
2505  while (it != end)
2506  {
2507  // handle escaped characters
2508  if (delim == '"' && *it == '\\')
2509  {
2510  val += parse_escape_code(it, end);
2511  }
2512  else if (*it == delim)
2513  {
2514  ++it;
2515  consume_whitespace(it, end);
2516  return val;
2517  }
2518  else
2519  {
2520  val += *it++;
2521  }
2522  }
2523  throw_parse_exception("Unterminated string literal");
2524  }
2525 
2526  std::string parse_escape_code(std::string::iterator& it,
2527  const std::string::iterator& end)
2528  {
2529  ++it;
2530  if (it == end)
2531  throw_parse_exception("Invalid escape sequence");
2532  char value;
2533  if (*it == 'b')
2534  {
2535  value = '\b';
2536  }
2537  else if (*it == 't')
2538  {
2539  value = '\t';
2540  }
2541  else if (*it == 'n')
2542  {
2543  value = '\n';
2544  }
2545  else if (*it == 'f')
2546  {
2547  value = '\f';
2548  }
2549  else if (*it == 'r')
2550  {
2551  value = '\r';
2552  }
2553  else if (*it == '"')
2554  {
2555  value = '"';
2556  }
2557  else if (*it == '\\')
2558  {
2559  value = '\\';
2560  }
2561  else if (*it == 'u' || *it == 'U')
2562  {
2563  return parse_unicode(it, end);
2564  }
2565  else
2566  {
2567  throw_parse_exception("Invalid escape sequence");
2568  }
2569  ++it;
2570  return std::string(1, value);
2571  }
2572 
2573  std::string parse_unicode(std::string::iterator& it,
2574  const std::string::iterator& end)
2575  {
2576  bool large = *it++ == 'U';
2577  auto codepoint = parse_hex(it, end, large ? 0x10000000 : 0x1000);
2578 
2579  if ((codepoint > 0xd7ff && codepoint < 0xe000) || codepoint > 0x10ffff)
2580  {
2581  throw_parse_exception(
2582  "Unicode escape sequence is not a Unicode scalar value");
2583  }
2584 
2585  std::string result;
2586  // See Table 3-6 of the Unicode standard
2587  if (codepoint <= 0x7f)
2588  {
2589  // 1-byte codepoints: 00000000 0xxxxxxx
2590  // repr: 0xxxxxxx
2591  result += static_cast<char>(codepoint & 0x7f);
2592  }
2593  else if (codepoint <= 0x7ff)
2594  {
2595  // 2-byte codepoints: 00000yyy yyxxxxxx
2596  // repr: 110yyyyy 10xxxxxx
2597  //
2598  // 0x1f = 00011111
2599  // 0xc0 = 11000000
2600  //
2601  result += static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f));
2602  //
2603  // 0x80 = 10000000
2604  // 0x3f = 00111111
2605  //
2606  result += static_cast<char>(0x80 | (codepoint & 0x3f));
2607  }
2608  else if (codepoint <= 0xffff)
2609  {
2610  // 3-byte codepoints: zzzzyyyy yyxxxxxx
2611  // repr: 1110zzzz 10yyyyyy 10xxxxxx
2612  //
2613  // 0xe0 = 11100000
2614  // 0x0f = 00001111
2615  //
2616  result += static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f));
2617  result += static_cast<char>(0x80 | ((codepoint >> 6) & 0x1f));
2618  result += static_cast<char>(0x80 | (codepoint & 0x3f));
2619  }
2620  else
2621  {
2622  // 4-byte codepoints: 000uuuuu zzzzyyyy yyxxxxxx
2623  // repr: 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
2624  //
2625  // 0xf0 = 11110000
2626  // 0x07 = 00000111
2627  //
2628  result += static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07));
2629  result += static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f));
2630  result += static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f));
2631  result += static_cast<char>(0x80 | (codepoint & 0x3f));
2632  }
2633  return result;
2634  }
2635 
2636  uint32_t parse_hex(std::string::iterator& it,
2637  const std::string::iterator& end, uint32_t place)
2638  {
2639  uint32_t value = 0;
2640  while (place > 0)
2641  {
2642  if (it == end)
2643  throw_parse_exception("Unexpected end of unicode sequence");
2644 
2645  if (!is_hex(*it))
2646  throw_parse_exception("Invalid unicode escape sequence");
2647 
2648  value += place * hex_to_digit(*it++);
2649  place /= 16;
2650  }
2651  return value;
2652  }
2653 
2654  uint32_t hex_to_digit(char c)
2655  {
2656  if (is_number(c))
2657  return static_cast<uint32_t>(c - '0');
2658  return 10
2659  + static_cast<uint32_t>(c
2660  - ((c >= 'a' && c <= 'f') ? 'a' : 'A'));
2661  }
2662 
2663  std::shared_ptr<base> parse_number(std::string::iterator& it,
2664  const std::string::iterator& end)
2665  {
2666  auto check_it = it;
2667  auto check_end = find_end_of_number(it, end);
2668 
2669  auto eat_sign = [&]() {
2670  if (check_it != end && (*check_it == '-' || *check_it == '+'))
2671  ++check_it;
2672  };
2673 
2674  auto check_no_leading_zero = [&]() {
2675  if (check_it != end && *check_it == '0' && check_it + 1 != check_end
2676  && check_it[1] != '.')
2677  {
2678  throw_parse_exception("Numbers may not have leading zeros");
2679  }
2680  };
2681 
2682  auto eat_digits = [&](bool (*check_char)(char)) {
2683  auto beg = check_it;
2684  while (check_it != end && check_char(*check_it))
2685  {
2686  ++check_it;
2687  if (check_it != end && *check_it == '_')
2688  {
2689  ++check_it;
2690  if (check_it == end || !check_char(*check_it))
2691  throw_parse_exception("Malformed number");
2692  }
2693  }
2694 
2695  if (check_it == beg)
2696  throw_parse_exception("Malformed number");
2697  };
2698 
2699  auto eat_hex = [&]() { eat_digits(&is_hex); };
2700 
2701  auto eat_numbers = [&]() { eat_digits(&is_number); };
2702 
2703  if (check_it != end && *check_it == '0' && check_it + 1 != check_end
2704  && (check_it[1] == 'x' || check_it[1] == 'o' || check_it[1] == 'b'))
2705  {
2706  ++check_it;
2707  char base = *check_it;
2708  ++check_it;
2709  if (base == 'x')
2710  {
2711  eat_hex();
2712  return parse_int(it, check_it, 16);
2713  }
2714  else if (base == 'o')
2715  {
2716  auto start = check_it;
2717  eat_numbers();
2718  auto val = parse_int(start, check_it, 8, "0");
2719  it = start;
2720  return val;
2721  }
2722  else // if (base == 'b')
2723  {
2724  auto start = check_it;
2725  eat_numbers();
2726  auto val = parse_int(start, check_it, 2);
2727  it = start;
2728  return val;
2729  }
2730  }
2731 
2732  eat_sign();
2733  check_no_leading_zero();
2734 
2735  if (check_it != end && check_it + 1 != end && check_it + 2 != end)
2736  {
2737  if (check_it[0] == 'i' && check_it[1] == 'n' && check_it[2] == 'f')
2738  {
2739  auto val = std::numeric_limits<double>::infinity();
2740  if (*it == '-')
2741  val = -val;
2742  it = check_it + 3;
2743  return make_value(val);
2744  }
2745  else if (check_it[0] == 'n' && check_it[1] == 'a'
2746  && check_it[2] == 'n')
2747  {
2748  auto val = std::numeric_limits<double>::quiet_NaN();
2749  if (*it == '-')
2750  val = -val;
2751  it = check_it + 3;
2752  return make_value(val);
2753  }
2754  }
2755 
2756  eat_numbers();
2757 
2758  if (check_it != end
2759  && (*check_it == '.' || *check_it == 'e' || *check_it == 'E'))
2760  {
2761  bool is_exp = *check_it == 'e' || *check_it == 'E';
2762 
2763  ++check_it;
2764  if (check_it == end)
2765  throw_parse_exception("Floats must have trailing digits");
2766 
2767  auto eat_exp = [&]() {
2768  eat_sign();
2769  check_no_leading_zero();
2770  eat_numbers();
2771  };
2772 
2773  if (is_exp)
2774  eat_exp();
2775  else
2776  eat_numbers();
2777 
2778  if (!is_exp && check_it != end
2779  && (*check_it == 'e' || *check_it == 'E'))
2780  {
2781  ++check_it;
2782  eat_exp();
2783  }
2784 
2785  return parse_float(it, check_it);
2786  }
2787  else
2788  {
2789  return parse_int(it, check_it);
2790  }
2791  }
2792 
2793  std::shared_ptr<value<int64_t>> parse_int(std::string::iterator& it,
2794  const std::string::iterator& end,
2795  int base = 10,
2796  const char* prefix = "")
2797  {
2798  std::string v{it, end};
2799  v = prefix + v;
2800  v.erase(std::remove(v.begin(), v.end(), '_'), v.end());
2801  it = end;
2802  try
2803  {
2804  return make_value<int64_t>(std::stoll(v, nullptr, base));
2805  }
2806  catch (const std::invalid_argument& ex)
2807  {
2808  throw_parse_exception("Malformed number (invalid argument: "
2809  + std::string{ex.what()} + ")");
2810  }
2811  catch (const std::out_of_range& ex)
2812  {
2813  throw_parse_exception("Malformed number (out of range: "
2814  + std::string{ex.what()} + ")");
2815  }
2816  }
2817 
2818  std::shared_ptr<value<double>> parse_float(std::string::iterator& it,
2819  const std::string::iterator& end)
2820  {
2821  std::string v{it, end};
2822  v.erase(std::remove(v.begin(), v.end(), '_'), v.end());
2823  it = end;
2824  char decimal_point = std::localeconv()->decimal_point[0];
2825  std::replace(v.begin(), v.end(), '.', decimal_point);
2826  try
2827  {
2828  return make_value<double>(std::stod(v));
2829  }
2830  catch (const std::invalid_argument& ex)
2831  {
2832  throw_parse_exception("Malformed number (invalid argument: "
2833  + std::string{ex.what()} + ")");
2834  }
2835  catch (const std::out_of_range& ex)
2836  {
2837  throw_parse_exception("Malformed number (out of range: "
2838  + std::string{ex.what()} + ")");
2839  }
2840  }
2841 
2842  std::shared_ptr<value<bool>> parse_bool(std::string::iterator& it,
2843  const std::string::iterator& end)
2844  {
2845  auto eat = make_consumer(it, end, [this]() {
2846  throw_parse_exception("Attempted to parse invalid boolean value");
2847  });
2848 
2849  if (*it == 't')
2850  {
2851  eat("true");
2852  return make_value<bool>(true);
2853  }
2854  else if (*it == 'f')
2855  {
2856  eat("false");
2857  return make_value<bool>(false);
2858  }
2859 
2860  eat.error();
2861  return nullptr;
2862  }
2863 
2864  std::string::iterator find_end_of_number(std::string::iterator it,
2865  std::string::iterator end)
2866  {
2867  auto ret = std::find_if(it, end, [](char c) {
2868  return !is_number(c) && c != '_' && c != '.' && c != 'e' && c != 'E'
2869  && c != '-' && c != '+' && c != 'x' && c != 'o' && c != 'b';
2870  });
2871  if (ret != end && ret + 1 != end && ret + 2 != end)
2872  {
2873  if ((ret[0] == 'i' && ret[1] == 'n' && ret[2] == 'f')
2874  || (ret[0] == 'n' && ret[1] == 'a' && ret[2] == 'n'))
2875  {
2876  ret = ret + 3;
2877  }
2878  }
2879  return ret;
2880  }
2881 
2882  std::string::iterator find_end_of_date(std::string::iterator it,
2883  std::string::iterator end)
2884  {
2885  auto end_of_date = std::find_if(it, end, [](char c) {
2886  return !is_number(c) && c != '-';
2887  });
2888  if (end_of_date != end && *end_of_date == ' ' && end_of_date + 1 != end
2889  && is_number(end_of_date[1]))
2890  end_of_date++;
2891  return std::find_if(end_of_date, end, [](char c) {
2892  return !is_number(c) && c != 'T' && c != 'Z' && c != ':'
2893  && c != '-' && c != '+' && c != '.';
2894  });
2895  }
2896 
2897  std::string::iterator find_end_of_time(std::string::iterator it,
2898  std::string::iterator end)
2899  {
2900  return std::find_if(it, end, [](char c) {
2901  return !is_number(c) && c != ':' && c != '.';
2902  });
2903  }
2904 
2905  local_time read_time(std::string::iterator& it,
2906  const std::string::iterator& end)
2907  {
2908  auto time_end = find_end_of_time(it, end);
2909 
2910  auto eat = make_consumer(
2911  it, time_end, [&]() { throw_parse_exception("Malformed time"); });
2912 
2913  local_time ltime;
2914 
2915  ltime.hour = eat.eat_digits(2);
2916  eat(':');
2917  ltime.minute = eat.eat_digits(2);
2918  eat(':');
2919  ltime.second = eat.eat_digits(2);
2920 
2921  int power = 100000;
2922  if (it != time_end && *it == '.')
2923  {
2924  ++it;
2925  while (it != time_end && is_number(*it))
2926  {
2927  ltime.microsecond += power * (*it++ - '0');
2928  power /= 10;
2929  }
2930  }
2931 
2932  if (it != time_end)
2933  throw_parse_exception("Malformed time");
2934 
2935  return ltime;
2936  }
2937 
2938  std::shared_ptr<value<local_time>>
2939  parse_time(std::string::iterator& it, const std::string::iterator& end)
2940  {
2941  return make_value(read_time(it, end));
2942  }
2943 
2944  std::shared_ptr<base> parse_date(std::string::iterator& it,
2945  const std::string::iterator& end)
2946  {
2947  auto date_end = find_end_of_date(it, end);
2948 
2949  auto eat = make_consumer(
2950  it, date_end, [&]() { throw_parse_exception("Malformed date"); });
2951 
2952  local_date ldate;
2953  ldate.year = eat.eat_digits(4);
2954  eat('-');
2955  ldate.month = eat.eat_digits(2);
2956  eat('-');
2957  ldate.day = eat.eat_digits(2);
2958 
2959  if (it == date_end)
2960  return make_value(ldate);
2961 
2962  eat.eat_or('T', ' ');
2963 
2964  local_datetime ldt;
2965  static_cast<local_date&>(ldt) = ldate;
2966  static_cast<local_time&>(ldt) = read_time(it, date_end);
2967 
2968  if (it == date_end)
2969  return make_value(ldt);
2970 
2971  offset_datetime dt;
2972  static_cast<local_datetime&>(dt) = ldt;
2973 
2974  int hoff = 0;
2975  int moff = 0;
2976  if (*it == '+' || *it == '-')
2977  {
2978  auto plus = *it == '+';
2979  ++it;
2980 
2981  hoff = eat.eat_digits(2);
2982  dt.hour_offset = (plus) ? hoff : -hoff;
2983  eat(':');
2984  moff = eat.eat_digits(2);
2985  dt.minute_offset = (plus) ? moff : -moff;
2986  }
2987  else if (*it == 'Z')
2988  {
2989  ++it;
2990  }
2991 
2992  if (it != date_end)
2993  throw_parse_exception("Malformed date");
2994 
2995  return make_value(dt);
2996  }
2997 
2998  std::shared_ptr<base> parse_array(std::string::iterator& it,
2999  std::string::iterator& end)
3000  {
3001  // this gets ugly because of the "homogeneity" restriction:
3002  // arrays can either be of only one type, or contain arrays
3003  // (each of those arrays could be of different types, though)
3004  //
3005  // because of the latter portion, we don't really have a choice
3006  // but to represent them as arrays of base values...
3007  ++it;
3008 
3009  // ugh---have to read the first value to determine array type...
3010  skip_whitespace_and_comments(it, end);
3011 
3012  // edge case---empty array
3013  if (*it == ']')
3014  {
3015  ++it;
3016  return make_array();
3017  }
3018 
3019  auto val_end = std::find_if(
3020  it, end, [](char c) { return c == ',' || c == ']' || c == '#'; });
3021  parse_type type = determine_value_type(it, val_end);
3022  switch (type)
3023  {
3024  case parse_type::STRING:
3025  return parse_value_array<std::string>(it, end);
3026  case parse_type::LOCAL_TIME:
3027  return parse_value_array<local_time>(it, end);
3028  case parse_type::LOCAL_DATE:
3029  return parse_value_array<local_date>(it, end);
3030  case parse_type::LOCAL_DATETIME:
3031  return parse_value_array<local_datetime>(it, end);
3032  case parse_type::OFFSET_DATETIME:
3033  return parse_value_array<offset_datetime>(it, end);
3034  case parse_type::INT:
3035  return parse_value_array<int64_t>(it, end);
3036  case parse_type::FLOAT:
3037  return parse_value_array<double>(it, end);
3038  case parse_type::BOOL:
3039  return parse_value_array<bool>(it, end);
3040  case parse_type::ARRAY:
3041  return parse_object_array<array>(&parser::parse_array, '[', it,
3042  end);
3043  case parse_type::INLINE_TABLE:
3044  return parse_object_array<table_array>(
3045  &parser::parse_inline_table, '{', it, end);
3046  default:
3047  throw_parse_exception("Unable to parse array");
3048  }
3049  }
3050 
3051  template <class Value>
3052  std::shared_ptr<array> parse_value_array(std::string::iterator& it,
3053  std::string::iterator& end)
3054  {
3055  auto arr = make_array();
3056  while (it != end && *it != ']')
3057  {
3058  auto val = parse_value(it, end);
3059  if (auto v = val->as<Value>())
3060  arr->get().push_back(val);
3061  else
3062  throw_parse_exception("Arrays must be homogeneous");
3063  skip_whitespace_and_comments(it, end);
3064  if (*it != ',')
3065  break;
3066  ++it;
3067  skip_whitespace_and_comments(it, end);
3068  }
3069  if (it != end)
3070  ++it;
3071  return arr;
3072  }
3073 
3074  template <class Object, class Function>
3075  std::shared_ptr<Object> parse_object_array(Function&& fun, char delim,
3076  std::string::iterator& it,
3077  std::string::iterator& end)
3078  {
3079  auto arr = detail::make_element<Object>();
3080 
3081  while (it != end && *it != ']')
3082  {
3083  if (*it != delim)
3084  throw_parse_exception("Unexpected character in array");
3085 
3086  arr->get().push_back(((*this).*fun)(it, end));
3087  skip_whitespace_and_comments(it, end);
3088 
3089  if (it == end || *it != ',')
3090  break;
3091 
3092  ++it;
3093  skip_whitespace_and_comments(it, end);
3094  }
3095 
3096  if (it == end || *it != ']')
3097  throw_parse_exception("Unterminated array");
3098 
3099  ++it;
3100  return arr;
3101  }
3102 
3103  std::shared_ptr<table> parse_inline_table(std::string::iterator& it,
3104  std::string::iterator& end)
3105  {
3106  auto tbl = make_table();
3107  do
3108  {
3109  ++it;
3110  if (it == end)
3111  throw_parse_exception("Unterminated inline table");
3112 
3113  consume_whitespace(it, end);
3114  if (it != end && *it != '}')
3115  {
3116  parse_key_value(it, end, tbl.get());
3117  consume_whitespace(it, end);
3118  }
3119  } while (*it == ',');
3120 
3121  if (it == end || *it != '}')
3122  throw_parse_exception("Unterminated inline table");
3123 
3124  ++it;
3125  consume_whitespace(it, end);
3126 
3127  return tbl;
3128  }
3129 
3130  void skip_whitespace_and_comments(std::string::iterator& start,
3131  std::string::iterator& end)
3132  {
3133  consume_whitespace(start, end);
3134  while (start == end || *start == '#')
3135  {
3136  if (!detail::getline(input_, line_))
3137  throw_parse_exception("Unclosed array");
3138  line_number_++;
3139  start = line_.begin();
3140  end = line_.end();
3141  consume_whitespace(start, end);
3142  }
3143  }
3144 
3145  void consume_whitespace(std::string::iterator& it,
3146  const std::string::iterator& end)
3147  {
3148  while (it != end && (*it == ' ' || *it == '\t'))
3149  ++it;
3150  }
3151 
3152  void consume_backwards_whitespace(std::string::iterator& back,
3153  const std::string::iterator& front)
3154  {
3155  while (back != front && (*back == ' ' || *back == '\t'))
3156  --back;
3157  }
3158 
3159  void eol_or_comment(const std::string::iterator& it,
3160  const std::string::iterator& end)
3161  {
3162  if (it != end && *it != '#')
3163  throw_parse_exception("Unidentified trailing character '"
3164  + std::string{*it}
3165  + "'---did you forget a '#'?");
3166  }
3167 
3168  bool is_time(const std::string::iterator& it,
3169  const std::string::iterator& end)
3170  {
3171  auto time_end = find_end_of_time(it, end);
3172  auto len = std::distance(it, time_end);
3173 
3174  if (len < 8)
3175  return false;
3176 
3177  if (it[2] != ':' || it[5] != ':')
3178  return false;
3179 
3180  if (len > 8)
3181  return it[8] == '.' && len > 9;
3182 
3183  return true;
3184  }
3185 
3186  option<parse_type> date_type(const std::string::iterator& it,
3187  const std::string::iterator& end)
3188  {
3189  auto date_end = find_end_of_date(it, end);
3190  auto len = std::distance(it, date_end);
3191 
3192  if (len < 10)
3193  return {};
3194 
3195  if (it[4] != '-' || it[7] != '-')
3196  return {};
3197 
3198  if (len >= 19 && (it[10] == 'T' || it[10] == ' ')
3199  && is_time(it + 11, date_end))
3200  {
3201  // datetime type
3202  auto time_end = find_end_of_time(it + 11, date_end);
3203  if (time_end == date_end)
3204  return {parse_type::LOCAL_DATETIME};
3205  else
3206  return {parse_type::OFFSET_DATETIME};
3207  }
3208  else if (len == 10)
3209  {
3210  // just a regular date
3211  return {parse_type::LOCAL_DATE};
3212  }
3213 
3214  return {};
3215  }
3216 
3217  std::istream& input_;
3218  std::string line_;
3219  std::size_t line_number_ = 0;
3220 };
3221 
3226 inline std::shared_ptr<table> parse_file(const std::string& filename)
3227 {
3228 #if defined(BOOST_NOWIDE_FSTREAM_INCLUDED_HPP)
3229  boost::nowide::ifstream file{filename.c_str()};
3230 #elif defined(NOWIDE_FSTREAM_INCLUDED_HPP)
3231  nowide::ifstream file{filename.c_str()};
3232 #else
3233  std::ifstream file{filename};
3234 #endif
3235  if (!file.is_open())
3236  throw parse_exception{filename + " could not be opened for parsing"};
3237  parser p{file};
3238  return p.parse();
3239 }
3240 
3241 template <class... Ts>
3243 
3244 template <>
3246 {
3247  template <class Visitor, class... Args>
3248  static void accept(const base&, Visitor&&, Args&&...)
3249  {
3250  // nothing
3251  }
3252 };
3253 
3254 template <class T, class... Ts>
3255 struct value_accept<T, Ts...>
3256 {
3257  template <class Visitor, class... Args>
3258  static void accept(const base& b, Visitor&& visitor, Args&&... args)
3259  {
3260  if (auto v = b.as<T>())
3261  {
3262  visitor.visit(*v, std::forward<Args>(args)...);
3263  }
3264  else
3265  {
3266  value_accept<Ts...>::accept(b, std::forward<Visitor>(visitor),
3267  std::forward<Args>(args)...);
3268  }
3269  }
3270 };
3271 
3276 template <class Visitor, class... Args>
3277 void base::accept(Visitor&& visitor, Args&&... args) const
3278 {
3279  if (is_value())
3280  {
3281  using value_acceptor
3282  = value_accept<std::string, int64_t, double, bool, local_date,
3283  local_time, local_datetime, offset_datetime>;
3284  value_acceptor::accept(*this, std::forward<Visitor>(visitor),
3285  std::forward<Args>(args)...);
3286  }
3287  else if (is_table())
3288  {
3289  visitor.visit(static_cast<const table&>(*this),
3290  std::forward<Args>(args)...);
3291  }
3292  else if (is_array())
3293  {
3294  visitor.visit(static_cast<const array&>(*this),
3295  std::forward<Args>(args)...);
3296  }
3297  else if (is_table_array())
3298  {
3299  visitor.visit(static_cast<const table_array&>(*this),
3300  std::forward<Args>(args)...);
3301  }
3302 }
3303 
3309 {
3310  public:
3314  toml_writer(std::ostream& s, const std::string& indent_space = "\t")
3315  : stream_(s), indent_(indent_space), has_naked_endline_(false)
3316  {
3317  // nothing
3318  }
3319 
3320  public:
3324  template <class T>
3325  void visit(const value<T>& v, bool = false)
3326  {
3327  write(v);
3328  }
3329 
3333  void visit(const table& t, bool in_array = false)
3334  {
3335  write_table_header(in_array);
3336  std::vector<std::string> values;
3337  std::vector<std::string> tables;
3338 
3339  for (const auto& i : t)
3340  {
3341  if (i.second->is_table() || i.second->is_table_array())
3342  {
3343  tables.push_back(i.first);
3344  }
3345  else
3346  {
3347  values.push_back(i.first);
3348  }
3349  }
3350 
3351  for (unsigned int i = 0; i < values.size(); ++i)
3352  {
3353  path_.push_back(values[i]);
3354 
3355  if (i > 0)
3356  endline();
3357 
3358  write_table_item_header(*t.get(values[i]));
3359  t.get(values[i])->accept(*this, false);
3360  path_.pop_back();
3361  }
3362 
3363  for (unsigned int i = 0; i < tables.size(); ++i)
3364  {
3365  path_.push_back(tables[i]);
3366 
3367  if (values.size() > 0 || i > 0)
3368  endline();
3369 
3370  write_table_item_header(*t.get(tables[i]));
3371  t.get(tables[i])->accept(*this, false);
3372  path_.pop_back();
3373  }
3374 
3375  endline();
3376  }
3377 
3381  void visit(const array& a, bool = false)
3382  {
3383  write("[");
3384 
3385  for (unsigned int i = 0; i < a.get().size(); ++i)
3386  {
3387  if (i > 0)
3388  write(", ");
3389 
3390  if (a.get()[i]->is_array())
3391  {
3392  a.get()[i]->as_array()->accept(*this, true);
3393  }
3394  else
3395  {
3396  a.get()[i]->accept(*this, true);
3397  }
3398  }
3399 
3400  write("]");
3401  }
3402 
3406  void visit(const table_array& t, bool = false)
3407  {
3408  for (unsigned int j = 0; j < t.get().size(); ++j)
3409  {
3410  if (j > 0)
3411  endline();
3412 
3413  t.get()[j]->accept(*this, true);
3414  }
3415 
3416  endline();
3417  }
3418 
3422  static std::string escape_string(const std::string& str)
3423  {
3424  std::string res;
3425  for (auto it = str.begin(); it != str.end(); ++it)
3426  {
3427  if (*it == '\b')
3428  {
3429  res += "\\b";
3430  }
3431  else if (*it == '\t')
3432  {
3433  res += "\\t";
3434  }
3435  else if (*it == '\n')
3436  {
3437  res += "\\n";
3438  }
3439  else if (*it == '\f')
3440  {
3441  res += "\\f";
3442  }
3443  else if (*it == '\r')
3444  {
3445  res += "\\r";
3446  }
3447  else if (*it == '"')
3448  {
3449  res += "\\\"";
3450  }
3451  else if (*it == '\\')
3452  {
3453  res += "\\\\";
3454  }
3455  else if (static_cast<uint32_t>(*it) <= UINT32_C(0x001f))
3456  {
3457  res += "\\u";
3458  std::stringstream ss;
3459  ss << std::hex << static_cast<uint32_t>(*it);
3460  res += ss.str();
3461  }
3462  else
3463  {
3464  res += *it;
3465  }
3466  }
3467  return res;
3468  }
3469 
3470  protected:
3474  void write(const value<std::string>& v)
3475  {
3476  write("\"");
3477  write(escape_string(v.get()));
3478  write("\"");
3479  }
3480 
3484  void write(const value<double>& v)
3485  {
3486  std::stringstream ss;
3487  ss << std::showpoint
3488  << std::setprecision(std::numeric_limits<double>::max_digits10)
3489  << v.get();
3490 
3491  auto double_str = ss.str();
3492  auto pos = double_str.find("e0");
3493  if (pos != std::string::npos)
3494  double_str.replace(pos, 2, "e");
3495  pos = double_str.find("e-0");
3496  if (pos != std::string::npos)
3497  double_str.replace(pos, 3, "e-");
3498 
3499  stream_ << double_str;
3500  has_naked_endline_ = false;
3501  }
3502 
3507  template <class T>
3508  typename std::enable_if<
3509  is_one_of<T, int64_t, local_date, local_time, local_datetime,
3510  offset_datetime>::value>::type
3511  write(const value<T>& v)
3512  {
3513  write(v.get());
3514  }
3515 
3519  void write(const value<bool>& v)
3520  {
3521  write((v.get() ? "true" : "false"));
3522  }
3523 
3527  void write_table_header(bool in_array = false)
3528  {
3529  if (!path_.empty())
3530  {
3531  indent();
3532 
3533  write("[");
3534 
3535  if (in_array)
3536  {
3537  write("[");
3538  }
3539 
3540  for (unsigned int i = 0; i < path_.size(); ++i)
3541  {
3542  if (i > 0)
3543  {
3544  write(".");
3545  }
3546 
3547  if (path_[i].find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
3548  "fghijklmnopqrstuvwxyz0123456789"
3549  "_-")
3550  == std::string::npos)
3551  {
3552  write(path_[i]);
3553  }
3554  else
3555  {
3556  write("\"");
3557  write(escape_string(path_[i]));
3558  write("\"");
3559  }
3560  }
3561 
3562  if (in_array)
3563  {
3564  write("]");
3565  }
3566 
3567  write("]");
3568  endline();
3569  }
3570  }
3571 
3576  {
3577  if (!b.is_table() && !b.is_table_array())
3578  {
3579  indent();
3580 
3581  if (path_.back().find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
3582  "fghijklmnopqrstuvwxyz0123456789"
3583  "_-")
3584  == std::string::npos)
3585  {
3586  write(path_.back());
3587  }
3588  else
3589  {
3590  write("\"");
3591  write(escape_string(path_.back()));
3592  write("\"");
3593  }
3594 
3595  write(" = ");
3596  }
3597  }
3598 
3599  private:
3604  void indent()
3605  {
3606  for (std::size_t i = 1; i < path_.size(); ++i)
3607  write(indent_);
3608  }
3609 
3613  template <class T>
3614  void write(const T& v)
3615  {
3616  stream_ << v;
3617  has_naked_endline_ = false;
3618  }
3619 
3623  void endline()
3624  {
3625  if (!has_naked_endline_)
3626  {
3627  stream_ << "\n";
3628  has_naked_endline_ = true;
3629  }
3630  }
3631 
3632  private:
3633  std::ostream& stream_;
3634  const std::string indent_;
3635  std::vector<std::string> path_;
3636  bool has_naked_endline_;
3637 };
3638 
3639 inline std::ostream& operator<<(std::ostream& stream, const base& b)
3640 {
3641  toml_writer writer{stream};
3642  b.accept(writer);
3643  return stream;
3644 }
3645 
3646 template <class T>
3647 std::ostream& operator<<(std::ostream& stream, const value<T>& v)
3648 {
3649  toml_writer writer{stream};
3650  v.accept(writer);
3651  return stream;
3652 }
3653 
3654 inline std::ostream& operator<<(std::ostream& stream, const table& t)
3655 {
3656  toml_writer writer{stream};
3657  t.accept(writer);
3658  return stream;
3659 }
3660 
3661 inline std::ostream& operator<<(std::ostream& stream, const table_array& t)
3662 {
3663  toml_writer writer{stream};
3664  t.accept(writer);
3665  return stream;
3666 }
3667 
3668 inline std::ostream& operator<<(std::ostream& stream, const array& a)
3669 {
3670  toml_writer writer{stream};
3671  a.accept(writer);
3672  return stream;
3673 }
3674 } // namespace cpptoml
3675 #endif // CPPTOML_H
std::shared_ptr< base > get_qualified(const std::string &key) const
Obtains the base for a given key.
Definition: cpptoml.h:1366
Definition: cpptoml.h:774
iterator insert(iterator position, const std::shared_ptr< value< T >> &value)
Insert a value into the array.
Definition: cpptoml.h:938
void insert(const std::string &key, const std::shared_ptr< base > &value)
Adds an element to the keytable.
Definition: cpptoml.h:1537
std::shared_ptr< value< T > > as()
Attempts to coerce the TOML element into a concrete TOML value of type T.
Definition: cpptoml.h:680
iterator insert(iterator position, const std::shared_ptr< array > &value)
Insert an array into the array.
Definition: cpptoml.h:953
void reserve(size_type n)
Reserve space for n tables.
Definition: cpptoml.h:1157
void push_back(const std::shared_ptr< value< T >> &val)
Add a value to the end of the array.
Definition: cpptoml.h:897
array_of_trait< T >::return_type get_qualified_array_of(const std::string &key) const
Helper function that attempts to get an array of values of a given type corresponding to the template...
Definition: cpptoml.h:1514
std::shared_ptr< table_array > as_table_array()
Converts the TOML element into a table array.
Definition: cpptoml.h:559
std::shared_ptr< array > get_array_qualified(const std::string &key) const
Obtains an array for a given key.
Definition: cpptoml.h:1407
Definition: cpptoml.h:288
virtual bool is_table_array() const override
Determines if the given TOML element is an array of tables.
Definition: cpptoml.h:1107
std::enable_if< is_one_of< T, int64_t, local_date, local_time, local_datetime, offset_datetime >::value >::type write(const value< T > &v)
Write out an integer, local_date, local_time, local_datetime, or offset_datetime. ...
Definition: cpptoml.h:3511
void visit(const value< T > &v, bool=false)
Output a base value of the TOML tree.
Definition: cpptoml.h:3325
Definition: cpptoml.h:382
void write(const value< std::string > &v)
Write out a string.
Definition: cpptoml.h:3474
void visit(const table &t, bool in_array=false)
Output a table element of the TOML tree.
Definition: cpptoml.h:3333
std::vector< std::shared_ptr< table >>::const_iterator const_iterator
arrays can be iterated over.
Definition: cpptoml.h:1085
static std::string escape_string(const std::string &str)
Escape a string for output.
Definition: cpptoml.h:3422
iterator erase(iterator position)
Erase an element from the array.
Definition: cpptoml.h:1141
std::shared_ptr< base > get(const std::string &key) const
Obtains the base for a given key.
Definition: cpptoml.h:1354
A generic base TOML value used for type erasure.
Definition: cpptoml.h:498
Definition: stringutils.h:172
std::vector< std::shared_ptr< base >>::const_iterator const_iterator
arrays can be iterated over.
Definition: cpptoml.h:796
bool contains(const std::string &key) const
Determines if this key table contains the given key.
Definition: cpptoml.h:1335
std::shared_ptr< array > as_array()
Converts the TOML element to an array.
Definition: cpptoml.h:541
virtual bool is_table() const
Determines if the given TOML element is a table.
Definition: cpptoml.h:516
void clear()
Clear the array.
Definition: cpptoml.h:986
std::shared_ptr< table_array > get_table_array(const std::string &key) const
Obtains a table_array for a given key, if possible.
Definition: cpptoml.h:1417
Writer that can be passed to accept() functions of cpptoml objects and will output valid TOML to a st...
Definition: cpptoml.h:3308
virtual bool is_array() const override
Determines if the TOML element is an array of "leaf" elements.
Definition: cpptoml.h:781
T & get()
Gets the data associated with this value.
Definition: cpptoml.h:637
std::vector< std::shared_ptr< table >>::iterator iterator
arrays can be iterated over
Definition: cpptoml.h:1080
virtual bool is_array() const
Determines if the TOML element is an array of "leaf" elements.
Definition: cpptoml.h:533
string_to_base_map::const_iterator const_iterator
tables can be iterated over.
Definition: cpptoml.h:1300
Definition: cpptoml.h:100
void write(const value< double > &v)
Write out a double.
Definition: cpptoml.h:3484
Helper object for consuming expected characters.
Definition: cpptoml.h:1775
Exception class for all TOML parsing errors.
Definition: cpptoml.h:1748
The parser class.
Definition: cpptoml.h:1875
Definition: cpptoml.h:107
std::shared_ptr< table > get_table_qualified(const std::string &key) const
Obtains a table for a given key, if possible.
Definition: cpptoml.h:1387
Represents a TOML keytable.
Definition: cpptoml.h:1284
std::shared_ptr< table > parse()
Parses the stream this parser was created on until EOF.
Definition: cpptoml.h:1892
virtual bool is_value() const
Determines if the given TOML element is a value.
Definition: cpptoml.h:508
Definition: cpptoml.h:1067
void push_back(T &&val, typename value_traits< T >::type *=0)
Convenience function for adding a simple element to the end of the array.
Definition: cpptoml.h:929
parser(std::istream &stream)
Parsers are constructed from streams.
Definition: cpptoml.h:1881
std::vector< std::shared_ptr< base >>::iterator iterator
arrays can be iterated over
Definition: cpptoml.h:791
bool is_inline() const
Whether or not the table array is declared inline.
Definition: cpptoml.h:1167
void write(const value< bool > &v)
Write out a boolean.
Definition: cpptoml.h:3519
std::shared_ptr< array > get_array(const std::string &key) const
Obtains an array for a given key.
Definition: cpptoml.h:1397
std::shared_ptr< table > get_table(const std::string &key) const
Obtains a table for a given key, if possible.
Definition: cpptoml.h:1376
std::shared_ptr< table_array > get_table_array_qualified(const std::string &key) const
Obtains a table_array for a given key, if possible.
Definition: cpptoml.h:1429
void visit(const table_array &t, bool=false)
Output a table_array element of the TOML tree.
Definition: cpptoml.h:3406
void reserve(size_type n)
Reserve space for n values.
Definition: cpptoml.h:994
Definition: cpptoml.h:121
void accept(Visitor &&visitor, Args &&... args) const
base implementation of accept() that calls visitor.visit() on the concrete class. ...
Definition: cpptoml.h:3277
void erase(const std::string &key)
Removes an element from the table.
Definition: cpptoml.h:1556
Definition: cpptoml.h:281
array_of_trait< T >::return_type get_array_of() const
Obtains a option<vector<T>>.
Definition: cpptoml.h:859
bool contains_qualified(const std::string &key) const
Determines if this key table contains the given key.
Definition: cpptoml.h:1345
iterator erase(iterator position)
Erase an element from the array.
Definition: cpptoml.h:978
toml_writer(std::ostream &s, const std::string &indent_space="\)
Construct a toml_writer that will write to the given stream.
Definition: cpptoml.h:3314
Definition: cpptoml.h:263
void insert(const std::string &key, T &&val, typename value_traits< T >::type *=0)
Convenience shorthand for adding a simple element to the keytable.
Definition: cpptoml.h:1547
iterator insert(iterator position, const std::shared_ptr< table > &value)
Insert a table into the array.
Definition: cpptoml.h:1133
array_of_trait< T >::return_type get_array_of(const std::string &key) const
Helper function that attempts to get an array of values of a given type corresponding to the template...
Definition: cpptoml.h:1482
Definition: cpptoml.h:168
Exception class for array insertion errors.
Definition: cpptoml.h:766
bool is_table() const override
Determines if the given TOML element is a table.
Definition: cpptoml.h:1322
void visit(const array &a, bool=false)
Output an array element of the TOML tree.
Definition: cpptoml.h:3381
string_to_base_map::iterator iterator
tables can be iterated over.
Definition: cpptoml.h:1295
void write_table_header(bool in_array=false)
Write out the header of a table.
Definition: cpptoml.h:3527
std::vector< std::shared_ptr< base > > & get()
Obtains the array (vector) of base values.
Definition: cpptoml.h:821
void push_back(const std::shared_ptr< array > &val)
Add an array to the end of the array.
Definition: cpptoml.h:912
Definition: cpptoml.h:115
Definition: cpptoml.h:125
std::shared_ptr< table > parse_file(const std::string &filename)
Utility function to parse a file as a TOML file.
Definition: cpptoml.h:3226
Definition: cpptoml.h:3242
void write_table_item_header(const base &b)
Write out the identifier for an item in a table.
Definition: cpptoml.h:3575
iterator insert(iterator position, T &&val, typename value_traits< T >::type *=0)
Convenience function for inserting a simple element in the array.
Definition: cpptoml.h:969
std::vector< std::shared_ptr< array > > nested_array() const
Obtains an array of arrays.
Definition: cpptoml.h:879
option< T > get_qualified_as(const std::string &key) const
Helper function that attempts to get a value corresponding to the template parameter from a given key...
Definition: cpptoml.h:1459
A concrete TOML value representing the "leaves" of the "tree".
Definition: cpptoml.h:278
Definition: cpptoml.h:59
virtual bool is_table_array() const
Determines if the given TOML element is an array of tables.
Definition: cpptoml.h:551
void clear()
Clear the array.
Definition: cpptoml.h:1149
std::vector< std::shared_ptr< value< T > > > array_of() const
Obtains an array of value<T>s.
Definition: cpptoml.h:844
Definition: cpptoml.h:39
std::shared_ptr< table > as_table()
Converts the TOML element into a table.
Definition: cpptoml.h:524
option< T > get_as(const std::string &key) const
Helper function that attempts to get a value corresponding to the template parameter from a given key...
Definition: cpptoml.h:1441
void push_back(const std::shared_ptr< table > &val)
Add a table to the end of the array.
Definition: cpptoml.h:1125
bool is_value() const override
Determines if the given TOML element is a value.
Definition: cpptoml.h:629