You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

484 lines
21 KiB

12 months ago
  1. // Copyright Toru Niina 2019.
  2. // Distributed under the MIT License.
  3. #ifndef TOML11_COMMENTS_HPP
  4. #define TOML11_COMMENTS_HPP
  5. #include <initializer_list>
  6. #include <iterator>
  7. #include <stdexcept>
  8. #include <string>
  9. #include <type_traits>
  10. #include <utility>
  11. #include <vector>
  12. #ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT
  13. # define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments
  14. #else
  15. # define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments
  16. #endif
  17. // This file provides mainly two classes, `preserve_comments` and `discard_comments`.
  18. // Those two are a container that have the same interface as `std::vector<std::string>`
  19. // but bahaves in the opposite way. `preserve_comments` is just the same as
  20. // `std::vector<std::string>` and each `std::string` corresponds to a comment line.
  21. // Conversely, `discard_comments` discards all the strings and ignores everything
  22. // assigned in it. `discard_comments` is always empty and you will encounter an
  23. // error whenever you access to the element.
  24. namespace toml
  25. {
  26. struct discard_comments; // forward decl
  27. // use it in the following way
  28. //
  29. // const toml::basic_value<toml::preserve_comments> data =
  30. // toml::parse<toml::preserve_comments>("example.toml");
  31. //
  32. // the interface is almost the same as std::vector<std::string>.
  33. struct preserve_comments
  34. {
  35. // `container_type` is not provided in discard_comments.
  36. // do not use this inner-type in a generic code.
  37. using container_type = std::vector<std::string>;
  38. using size_type = container_type::size_type;
  39. using difference_type = container_type::difference_type;
  40. using value_type = container_type::value_type;
  41. using reference = container_type::reference;
  42. using const_reference = container_type::const_reference;
  43. using pointer = container_type::pointer;
  44. using const_pointer = container_type::const_pointer;
  45. using iterator = container_type::iterator;
  46. using const_iterator = container_type::const_iterator;
  47. using reverse_iterator = container_type::reverse_iterator;
  48. using const_reverse_iterator = container_type::const_reverse_iterator;
  49. preserve_comments() = default;
  50. ~preserve_comments() = default;
  51. preserve_comments(preserve_comments const&) = default;
  52. preserve_comments(preserve_comments &&) = default;
  53. preserve_comments& operator=(preserve_comments const&) = default;
  54. preserve_comments& operator=(preserve_comments &&) = default;
  55. explicit preserve_comments(const std::vector<std::string>& c): comments(c){}
  56. explicit preserve_comments(std::vector<std::string>&& c)
  57. : comments(std::move(c))
  58. {}
  59. preserve_comments& operator=(const std::vector<std::string>& c)
  60. {
  61. comments = c;
  62. return *this;
  63. }
  64. preserve_comments& operator=(std::vector<std::string>&& c)
  65. {
  66. comments = std::move(c);
  67. return *this;
  68. }
  69. explicit preserve_comments(const discard_comments&) {}
  70. explicit preserve_comments(size_type n): comments(n) {}
  71. preserve_comments(size_type n, const std::string& x): comments(n, x) {}
  72. preserve_comments(std::initializer_list<std::string> x): comments(x) {}
  73. template<typename InputIterator>
  74. preserve_comments(InputIterator first, InputIterator last)
  75. : comments(first, last)
  76. {}
  77. template<typename InputIterator>
  78. void assign(InputIterator first, InputIterator last) {comments.assign(first, last);}
  79. void assign(std::initializer_list<std::string> ini) {comments.assign(ini);}
  80. void assign(size_type n, const std::string& val) {comments.assign(n, val);}
  81. // Related to the issue #97.
  82. //
  83. // It is known that `std::vector::insert` and `std::vector::erase` in
  84. // the standard library implementation included in GCC 4.8.5 takes
  85. // `std::vector::iterator` instead of `std::vector::const_iterator`.
  86. // Because of the const-correctness, we cannot convert a `const_iterator` to
  87. // an `iterator`. It causes compilation error in GCC 4.8.5.
  88. #if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__)
  89. # if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805
  90. # define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
  91. # endif
  92. #endif
  93. #ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
  94. iterator insert(iterator p, const std::string& x)
  95. {
  96. return comments.insert(p, x);
  97. }
  98. iterator insert(iterator p, std::string&& x)
  99. {
  100. return comments.insert(p, std::move(x));
  101. }
  102. void insert(iterator p, size_type n, const std::string& x)
  103. {
  104. return comments.insert(p, n, x);
  105. }
  106. template<typename InputIterator>
  107. void insert(iterator p, InputIterator first, InputIterator last)
  108. {
  109. return comments.insert(p, first, last);
  110. }
  111. void insert(iterator p, std::initializer_list<std::string> ini)
  112. {
  113. return comments.insert(p, ini);
  114. }
  115. template<typename ... Ts>
  116. iterator emplace(iterator p, Ts&& ... args)
  117. {
  118. return comments.emplace(p, std::forward<Ts>(args)...);
  119. }
  120. iterator erase(iterator pos) {return comments.erase(pos);}
  121. iterator erase(iterator first, iterator last)
  122. {
  123. return comments.erase(first, last);
  124. }
  125. #else
  126. iterator insert(const_iterator p, const std::string& x)
  127. {
  128. return comments.insert(p, x);
  129. }
  130. iterator insert(const_iterator p, std::string&& x)
  131. {
  132. return comments.insert(p, std::move(x));
  133. }
  134. iterator insert(const_iterator p, size_type n, const std::string& x)
  135. {
  136. return comments.insert(p, n, x);
  137. }
  138. template<typename InputIterator>
  139. iterator insert(const_iterator p, InputIterator first, InputIterator last)
  140. {
  141. return comments.insert(p, first, last);
  142. }
  143. iterator insert(const_iterator p, std::initializer_list<std::string> ini)
  144. {
  145. return comments.insert(p, ini);
  146. }
  147. template<typename ... Ts>
  148. iterator emplace(const_iterator p, Ts&& ... args)
  149. {
  150. return comments.emplace(p, std::forward<Ts>(args)...);
  151. }
  152. iterator erase(const_iterator pos) {return comments.erase(pos);}
  153. iterator erase(const_iterator first, const_iterator last)
  154. {
  155. return comments.erase(first, last);
  156. }
  157. #endif
  158. void swap(preserve_comments& other) {comments.swap(other.comments);}
  159. void push_back(const std::string& v) {comments.push_back(v);}
  160. void push_back(std::string&& v) {comments.push_back(std::move(v));}
  161. void pop_back() {comments.pop_back();}
  162. template<typename ... Ts>
  163. void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward<Ts>(args)...);}
  164. void clear() {comments.clear();}
  165. size_type size() const noexcept {return comments.size();}
  166. size_type max_size() const noexcept {return comments.max_size();}
  167. size_type capacity() const noexcept {return comments.capacity();}
  168. bool empty() const noexcept {return comments.empty();}
  169. void reserve(size_type n) {comments.reserve(n);}
  170. void resize(size_type n) {comments.resize(n);}
  171. void resize(size_type n, const std::string& c) {comments.resize(n, c);}
  172. void shrink_to_fit() {comments.shrink_to_fit();}
  173. reference operator[](const size_type n) noexcept {return comments[n];}
  174. const_reference operator[](const size_type n) const noexcept {return comments[n];}
  175. reference at(const size_type n) {return comments.at(n);}
  176. const_reference at(const size_type n) const {return comments.at(n);}
  177. reference front() noexcept {return comments.front();}
  178. const_reference front() const noexcept {return comments.front();}
  179. reference back() noexcept {return comments.back();}
  180. const_reference back() const noexcept {return comments.back();}
  181. pointer data() noexcept {return comments.data();}
  182. const_pointer data() const noexcept {return comments.data();}
  183. iterator begin() noexcept {return comments.begin();}
  184. iterator end() noexcept {return comments.end();}
  185. const_iterator begin() const noexcept {return comments.begin();}
  186. const_iterator end() const noexcept {return comments.end();}
  187. const_iterator cbegin() const noexcept {return comments.cbegin();}
  188. const_iterator cend() const noexcept {return comments.cend();}
  189. reverse_iterator rbegin() noexcept {return comments.rbegin();}
  190. reverse_iterator rend() noexcept {return comments.rend();}
  191. const_reverse_iterator rbegin() const noexcept {return comments.rbegin();}
  192. const_reverse_iterator rend() const noexcept {return comments.rend();}
  193. const_reverse_iterator crbegin() const noexcept {return comments.crbegin();}
  194. const_reverse_iterator crend() const noexcept {return comments.crend();}
  195. friend bool operator==(const preserve_comments&, const preserve_comments&);
  196. friend bool operator!=(const preserve_comments&, const preserve_comments&);
  197. friend bool operator< (const preserve_comments&, const preserve_comments&);
  198. friend bool operator<=(const preserve_comments&, const preserve_comments&);
  199. friend bool operator> (const preserve_comments&, const preserve_comments&);
  200. friend bool operator>=(const preserve_comments&, const preserve_comments&);
  201. friend void swap(preserve_comments&, std::vector<std::string>&);
  202. friend void swap(std::vector<std::string>&, preserve_comments&);
  203. private:
  204. container_type comments;
  205. };
  206. inline bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;}
  207. inline bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;}
  208. inline bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;}
  209. inline bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;}
  210. inline bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;}
  211. inline bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;}
  212. inline void swap(preserve_comments& lhs, preserve_comments& rhs)
  213. {
  214. lhs.swap(rhs);
  215. return;
  216. }
  217. inline void swap(preserve_comments& lhs, std::vector<std::string>& rhs)
  218. {
  219. lhs.comments.swap(rhs);
  220. return;
  221. }
  222. inline void swap(std::vector<std::string>& lhs, preserve_comments& rhs)
  223. {
  224. lhs.swap(rhs.comments);
  225. return;
  226. }
  227. template<typename charT, typename traits>
  228. std::basic_ostream<charT, traits>&
  229. operator<<(std::basic_ostream<charT, traits>& os, const preserve_comments& com)
  230. {
  231. for(const auto& c : com)
  232. {
  233. os << '#' << c << '\n';
  234. }
  235. return os;
  236. }
  237. namespace detail
  238. {
  239. // To provide the same interface with `preserve_comments`, `discard_comments`
  240. // should have an iterator. But it does not contain anything, so we need to
  241. // add an iterator that points nothing.
  242. //
  243. // It always points null, so DO NOT unwrap this iterator. It always crashes
  244. // your program.
  245. template<typename T, bool is_const>
  246. struct empty_iterator
  247. {
  248. using value_type = T;
  249. using reference_type = typename std::conditional<is_const, T const&, T&>::type;
  250. using pointer_type = typename std::conditional<is_const, T const*, T*>::type;
  251. using difference_type = std::ptrdiff_t;
  252. using iterator_category = std::random_access_iterator_tag;
  253. empty_iterator() = default;
  254. ~empty_iterator() = default;
  255. empty_iterator(empty_iterator const&) = default;
  256. empty_iterator(empty_iterator &&) = default;
  257. empty_iterator& operator=(empty_iterator const&) = default;
  258. empty_iterator& operator=(empty_iterator &&) = default;
  259. // DO NOT call these operators.
  260. reference_type operator*() const noexcept {std::terminate();}
  261. pointer_type operator->() const noexcept {return nullptr;}
  262. reference_type operator[](difference_type) const noexcept {return this->operator*();}
  263. // These operators do nothing.
  264. empty_iterator& operator++() noexcept {return *this;}
  265. empty_iterator operator++(int) noexcept {return *this;}
  266. empty_iterator& operator--() noexcept {return *this;}
  267. empty_iterator operator--(int) noexcept {return *this;}
  268. empty_iterator& operator+=(difference_type) noexcept {return *this;}
  269. empty_iterator& operator-=(difference_type) noexcept {return *this;}
  270. empty_iterator operator+(difference_type) const noexcept {return *this;}
  271. empty_iterator operator-(difference_type) const noexcept {return *this;}
  272. };
  273. template<typename T, bool C>
  274. bool operator==(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
  275. template<typename T, bool C>
  276. bool operator!=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
  277. template<typename T, bool C>
  278. bool operator< (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
  279. template<typename T, bool C>
  280. bool operator<=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
  281. template<typename T, bool C>
  282. bool operator> (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
  283. template<typename T, bool C>
  284. bool operator>=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
  285. template<typename T, bool C>
  286. typename empty_iterator<T, C>::difference_type
  287. operator-(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return 0;}
  288. template<typename T, bool C>
  289. empty_iterator<T, C>
  290. operator+(typename empty_iterator<T, C>::difference_type, const empty_iterator<T, C>& rhs) noexcept {return rhs;}
  291. template<typename T, bool C>
  292. empty_iterator<T, C>
  293. operator+(const empty_iterator<T, C>& lhs, typename empty_iterator<T, C>::difference_type) noexcept {return lhs;}
  294. } // detail
  295. // The default comment type. It discards all the comments. It requires only one
  296. // byte to contain, so the memory footprint is smaller than preserve_comments.
  297. //
  298. // It just ignores `push_back`, `insert`, `erase`, and any other modifications.
  299. // IT always returns size() == 0, the iterator taken by `begin()` is always the
  300. // same as that of `end()`, and accessing through `operator[]` or iterators
  301. // always causes a segmentation fault. DO NOT access to the element of this.
  302. //
  303. // Why this is chose as the default type is because the last version (2.x.y)
  304. // does not contain any comments in a value. To minimize the impact on the
  305. // efficiency, this is chosen as a default.
  306. //
  307. // To reduce the memory footprint, later we can try empty base optimization (EBO).
  308. struct discard_comments
  309. {
  310. using size_type = std::size_t;
  311. using difference_type = std::ptrdiff_t;
  312. using value_type = std::string;
  313. using reference = std::string&;
  314. using const_reference = std::string const&;
  315. using pointer = std::string*;
  316. using const_pointer = std::string const*;
  317. using iterator = detail::empty_iterator<std::string, false>;
  318. using const_iterator = detail::empty_iterator<std::string, true>;
  319. using reverse_iterator = detail::empty_iterator<std::string, false>;
  320. using const_reverse_iterator = detail::empty_iterator<std::string, true>;
  321. discard_comments() = default;
  322. ~discard_comments() = default;
  323. discard_comments(discard_comments const&) = default;
  324. discard_comments(discard_comments &&) = default;
  325. discard_comments& operator=(discard_comments const&) = default;
  326. discard_comments& operator=(discard_comments &&) = default;
  327. explicit discard_comments(const std::vector<std::string>&) noexcept {}
  328. explicit discard_comments(std::vector<std::string>&&) noexcept {}
  329. discard_comments& operator=(const std::vector<std::string>&) noexcept {return *this;}
  330. discard_comments& operator=(std::vector<std::string>&&) noexcept {return *this;}
  331. explicit discard_comments(const preserve_comments&) noexcept {}
  332. explicit discard_comments(size_type) noexcept {}
  333. discard_comments(size_type, const std::string&) noexcept {}
  334. discard_comments(std::initializer_list<std::string>) noexcept {}
  335. template<typename InputIterator>
  336. discard_comments(InputIterator, InputIterator) noexcept {}
  337. template<typename InputIterator>
  338. void assign(InputIterator, InputIterator) noexcept {}
  339. void assign(std::initializer_list<std::string>) noexcept {}
  340. void assign(size_type, const std::string&) noexcept {}
  341. iterator insert(const_iterator, const std::string&) {return iterator{};}
  342. iterator insert(const_iterator, std::string&&) {return iterator{};}
  343. iterator insert(const_iterator, size_type, const std::string&) {return iterator{};}
  344. template<typename InputIterator>
  345. iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};}
  346. iterator insert(const_iterator, std::initializer_list<std::string>) {return iterator{};}
  347. template<typename ... Ts>
  348. iterator emplace(const_iterator, Ts&& ...) {return iterator{};}
  349. iterator erase(const_iterator) {return iterator{};}
  350. iterator erase(const_iterator, const_iterator) {return iterator{};}
  351. void swap(discard_comments&) {return;}
  352. void push_back(const std::string&) {return;}
  353. void push_back(std::string&& ) {return;}
  354. void pop_back() {return;}
  355. template<typename ... Ts>
  356. void emplace_back(Ts&& ...) {return;}
  357. void clear() {return;}
  358. size_type size() const noexcept {return 0;}
  359. size_type max_size() const noexcept {return 0;}
  360. size_type capacity() const noexcept {return 0;}
  361. bool empty() const noexcept {return true;}
  362. void reserve(size_type) {return;}
  363. void resize(size_type) {return;}
  364. void resize(size_type, const std::string&) {return;}
  365. void shrink_to_fit() {return;}
  366. // DO NOT access to the element of this container. This container is always
  367. // empty, so accessing through operator[], front/back, data causes address
  368. // error.
  369. reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");}
  370. const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");}
  371. reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");}
  372. const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");}
  373. reference front() noexcept {never_call("toml::discard_comment::front");}
  374. const_reference front() const noexcept {never_call("toml::discard_comment::front");}
  375. reference back() noexcept {never_call("toml::discard_comment::back");}
  376. const_reference back() const noexcept {never_call("toml::discard_comment::back");}
  377. pointer data() noexcept {return nullptr;}
  378. const_pointer data() const noexcept {return nullptr;}
  379. iterator begin() noexcept {return iterator{};}
  380. iterator end() noexcept {return iterator{};}
  381. const_iterator begin() const noexcept {return const_iterator{};}
  382. const_iterator end() const noexcept {return const_iterator{};}
  383. const_iterator cbegin() const noexcept {return const_iterator{};}
  384. const_iterator cend() const noexcept {return const_iterator{};}
  385. reverse_iterator rbegin() noexcept {return iterator{};}
  386. reverse_iterator rend() noexcept {return iterator{};}
  387. const_reverse_iterator rbegin() const noexcept {return const_iterator{};}
  388. const_reverse_iterator rend() const noexcept {return const_iterator{};}
  389. const_reverse_iterator crbegin() const noexcept {return const_iterator{};}
  390. const_reverse_iterator crend() const noexcept {return const_iterator{};}
  391. private:
  392. [[noreturn]] static void never_call(const char *const this_function)
  393. {
  394. #ifdef __has_builtin
  395. # if __has_builtin(__builtin_unreachable)
  396. __builtin_unreachable();
  397. # endif
  398. #endif
  399. throw std::logic_error{this_function};
  400. }
  401. };
  402. inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;}
  403. inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;}
  404. inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;}
  405. inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;}
  406. inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;}
  407. inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;}
  408. inline void swap(const discard_comments&, const discard_comments&) noexcept {return;}
  409. template<typename charT, typename traits>
  410. std::basic_ostream<charT, traits>&
  411. operator<<(std::basic_ostream<charT, traits>& os, const discard_comments&)
  412. {
  413. return os;
  414. }
  415. } // toml11
  416. #endif// TOML11_COMMENTS_HPP