// Copyright Toru Niina 2019. // Distributed under the MIT License. #ifndef TOML11_COMMENTS_HPP #define TOML11_COMMENTS_HPP #include #include #include #include #include #include #include // This file provides mainly two classes, `preserve_comments` and `discard_comments`. // Those two are a container that have the same interface as `std::vector` // but bahaves in the opposite way. `preserve_comments` is just the same as // `std::vector` and each `std::string` corresponds to a comment line. // Conversely, `discard_comments` discards all the strings and ignores everything // assigned in it. `discard_comments` is always empty and you will encounter an // error whenever you access to the element. namespace toml { struct discard_comments; // forward decl // use it in the following way // // const toml::basic_value data = // toml::parse("example.toml"); // // the interface is almost the same as std::vector. struct preserve_comments { // `container_type` is not provided in discard_comments. // do not use this inner-type in a generic code. using container_type = std::vector; using size_type = container_type::size_type; using difference_type = container_type::difference_type; using value_type = container_type::value_type; using reference = container_type::reference; using const_reference = container_type::const_reference; using pointer = container_type::pointer; using const_pointer = container_type::const_pointer; using iterator = container_type::iterator; using const_iterator = container_type::const_iterator; using reverse_iterator = container_type::reverse_iterator; using const_reverse_iterator = container_type::const_reverse_iterator; preserve_comments() = default; ~preserve_comments() = default; preserve_comments(preserve_comments const&) = default; preserve_comments(preserve_comments &&) = default; preserve_comments& operator=(preserve_comments const&) = default; preserve_comments& operator=(preserve_comments &&) = default; explicit preserve_comments(const std::vector& c): comments(c){} explicit preserve_comments(std::vector&& c) : comments(std::move(c)) {} preserve_comments& operator=(const std::vector& c) { comments = c; return *this; } preserve_comments& operator=(std::vector&& c) { comments = std::move(c); return *this; } explicit preserve_comments(const discard_comments&) {} explicit preserve_comments(size_type n): comments(n) {} preserve_comments(size_type n, const std::string& x): comments(n, x) {} preserve_comments(std::initializer_list x): comments(x) {} template preserve_comments(InputIterator first, InputIterator last) : comments(first, last) {} template void assign(InputIterator first, InputIterator last) {comments.assign(first, last);} void assign(std::initializer_list ini) {comments.assign(ini);} void assign(size_type n, const std::string& val) {comments.assign(n, val);} // Related to the issue #97. // // It is known that `std::vector::insert` and `std::vector::erase` in // the standard library implementation included in GCC 4.8.5 takes // `std::vector::iterator` instead of `std::vector::const_iterator`. // Because of the const-correctness, we cannot convert a `const_iterator` to // an `iterator`. It causes compilation error in GCC 4.8.5. #if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__) # if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805 # define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION # endif #endif #ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION iterator insert(iterator p, const std::string& x) { return comments.insert(p, x); } iterator insert(iterator p, std::string&& x) { return comments.insert(p, std::move(x)); } void insert(iterator p, size_type n, const std::string& x) { return comments.insert(p, n, x); } template void insert(iterator p, InputIterator first, InputIterator last) { return comments.insert(p, first, last); } void insert(iterator p, std::initializer_list ini) { return comments.insert(p, ini); } template iterator emplace(iterator p, Ts&& ... args) { return comments.emplace(p, std::forward(args)...); } iterator erase(iterator pos) {return comments.erase(pos);} iterator erase(iterator first, iterator last) { return comments.erase(first, last); } #else iterator insert(const_iterator p, const std::string& x) { return comments.insert(p, x); } iterator insert(const_iterator p, std::string&& x) { return comments.insert(p, std::move(x)); } iterator insert(const_iterator p, size_type n, const std::string& x) { return comments.insert(p, n, x); } template iterator insert(const_iterator p, InputIterator first, InputIterator last) { return comments.insert(p, first, last); } iterator insert(const_iterator p, std::initializer_list ini) { return comments.insert(p, ini); } template iterator emplace(const_iterator p, Ts&& ... args) { return comments.emplace(p, std::forward(args)...); } iterator erase(const_iterator pos) {return comments.erase(pos);} iterator erase(const_iterator first, const_iterator last) { return comments.erase(first, last); } #endif void swap(preserve_comments& other) {comments.swap(other.comments);} void push_back(const std::string& v) {comments.push_back(v);} void push_back(std::string&& v) {comments.push_back(std::move(v));} void pop_back() {comments.pop_back();} template void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward(args)...);} void clear() {comments.clear();} size_type size() const noexcept {return comments.size();} size_type max_size() const noexcept {return comments.max_size();} size_type capacity() const noexcept {return comments.capacity();} bool empty() const noexcept {return comments.empty();} void reserve(size_type n) {comments.reserve(n);} void resize(size_type n) {comments.resize(n);} void resize(size_type n, const std::string& c) {comments.resize(n, c);} void shrink_to_fit() {comments.shrink_to_fit();} reference operator[](const size_type n) noexcept {return comments[n];} const_reference operator[](const size_type n) const noexcept {return comments[n];} reference at(const size_type n) {return comments.at(n);} const_reference at(const size_type n) const {return comments.at(n);} reference front() noexcept {return comments.front();} const_reference front() const noexcept {return comments.front();} reference back() noexcept {return comments.back();} const_reference back() const noexcept {return comments.back();} pointer data() noexcept {return comments.data();} const_pointer data() const noexcept {return comments.data();} iterator begin() noexcept {return comments.begin();} iterator end() noexcept {return comments.end();} const_iterator begin() const noexcept {return comments.begin();} const_iterator end() const noexcept {return comments.end();} const_iterator cbegin() const noexcept {return comments.cbegin();} const_iterator cend() const noexcept {return comments.cend();} reverse_iterator rbegin() noexcept {return comments.rbegin();} reverse_iterator rend() noexcept {return comments.rend();} const_reverse_iterator rbegin() const noexcept {return comments.rbegin();} const_reverse_iterator rend() const noexcept {return comments.rend();} const_reverse_iterator crbegin() const noexcept {return comments.crbegin();} const_reverse_iterator crend() const noexcept {return comments.crend();} friend bool operator==(const preserve_comments&, const preserve_comments&); friend bool operator!=(const preserve_comments&, const preserve_comments&); friend bool operator< (const preserve_comments&, const preserve_comments&); friend bool operator<=(const preserve_comments&, const preserve_comments&); friend bool operator> (const preserve_comments&, const preserve_comments&); friend bool operator>=(const preserve_comments&, const preserve_comments&); friend void swap(preserve_comments&, std::vector&); friend void swap(std::vector&, preserve_comments&); private: container_type comments; }; inline bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} inline bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} inline bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} inline bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} inline bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;} inline bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;} inline void swap(preserve_comments& lhs, preserve_comments& rhs) { lhs.swap(rhs); return; } inline void swap(preserve_comments& lhs, std::vector& rhs) { lhs.comments.swap(rhs); return; } inline void swap(std::vector& lhs, preserve_comments& rhs) { lhs.swap(rhs.comments); return; } template std::basic_ostream& operator<<(std::basic_ostream& os, const preserve_comments& com) { for(const auto& c : com) { os << '#' << c << '\n'; } return os; } namespace detail { // To provide the same interface with `preserve_comments`, `discard_comments` // should have an iterator. But it does not contain anything, so we need to // add an iterator that points nothing. // // It always points null, so DO NOT unwrap this iterator. It always crashes // your program. template struct empty_iterator { using value_type = T; using reference_type = typename std::conditional::type; using pointer_type = typename std::conditional::type; using difference_type = std::ptrdiff_t; using iterator_category = std::random_access_iterator_tag; empty_iterator() = default; ~empty_iterator() = default; empty_iterator(empty_iterator const&) = default; empty_iterator(empty_iterator &&) = default; empty_iterator& operator=(empty_iterator const&) = default; empty_iterator& operator=(empty_iterator &&) = default; // DO NOT call these operators. reference_type operator*() const noexcept {std::terminate();} pointer_type operator->() const noexcept {return nullptr;} reference_type operator[](difference_type) const noexcept {return this->operator*();} // These operators do nothing. empty_iterator& operator++() noexcept {return *this;} empty_iterator operator++(int) noexcept {return *this;} empty_iterator& operator--() noexcept {return *this;} empty_iterator operator--(int) noexcept {return *this;} empty_iterator& operator+=(difference_type) noexcept {return *this;} empty_iterator& operator-=(difference_type) noexcept {return *this;} empty_iterator operator+(difference_type) const noexcept {return *this;} empty_iterator operator-(difference_type) const noexcept {return *this;} }; template bool operator==(const empty_iterator&, const empty_iterator&) noexcept {return true;} template bool operator!=(const empty_iterator&, const empty_iterator&) noexcept {return false;} template bool operator< (const empty_iterator&, const empty_iterator&) noexcept {return false;} template bool operator<=(const empty_iterator&, const empty_iterator&) noexcept {return true;} template bool operator> (const empty_iterator&, const empty_iterator&) noexcept {return false;} template bool operator>=(const empty_iterator&, const empty_iterator&) noexcept {return true;} template typename empty_iterator::difference_type operator-(const empty_iterator&, const empty_iterator&) noexcept {return 0;} template empty_iterator operator+(typename empty_iterator::difference_type, const empty_iterator& rhs) noexcept {return rhs;} template empty_iterator operator+(const empty_iterator& lhs, typename empty_iterator::difference_type) noexcept {return lhs;} } // detail // The default comment type. It discards all the comments. It requires only one // byte to contain, so the memory footprint is smaller than preserve_comments. // // It just ignores `push_back`, `insert`, `erase`, and any other modifications. // IT always returns size() == 0, the iterator taken by `begin()` is always the // same as that of `end()`, and accessing through `operator[]` or iterators // always causes a segmentation fault. DO NOT access to the element of this. // // Why this is chose as the default type is because the last version (2.x.y) // does not contain any comments in a value. To minimize the impact on the // efficiency, this is choosed as a default. // // To reduce the memory footprint, later we can try empty base optimization (EBO). struct discard_comments { using size_type = std::size_t; using difference_type = std::ptrdiff_t; using value_type = std::string; using reference = std::string&; using const_reference = std::string const&; using pointer = std::string*; using const_pointer = std::string const*; using iterator = detail::empty_iterator; using const_iterator = detail::empty_iterator; using reverse_iterator = detail::empty_iterator; using const_reverse_iterator = detail::empty_iterator; discard_comments() = default; ~discard_comments() = default; discard_comments(discard_comments const&) = default; discard_comments(discard_comments &&) = default; discard_comments& operator=(discard_comments const&) = default; discard_comments& operator=(discard_comments &&) = default; explicit discard_comments(const std::vector&) noexcept {} explicit discard_comments(std::vector&&) noexcept {} discard_comments& operator=(const std::vector&) noexcept {return *this;} discard_comments& operator=(std::vector&&) noexcept {return *this;} explicit discard_comments(const preserve_comments&) noexcept {} explicit discard_comments(size_type) noexcept {} discard_comments(size_type, const std::string&) noexcept {} discard_comments(std::initializer_list) noexcept {} template discard_comments(InputIterator, InputIterator) noexcept {} template void assign(InputIterator, InputIterator) noexcept {} void assign(std::initializer_list) noexcept {} void assign(size_type, const std::string&) noexcept {} iterator insert(const_iterator, const std::string&) {return iterator{};} iterator insert(const_iterator, std::string&&) {return iterator{};} iterator insert(const_iterator, size_type, const std::string&) {return iterator{};} template iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};} iterator insert(const_iterator, std::initializer_list) {return iterator{};} template iterator emplace(const_iterator, Ts&& ...) {return iterator{};} iterator erase(const_iterator) {return iterator{};} iterator erase(const_iterator, const_iterator) {return iterator{};} void swap(discard_comments&) {return;} void push_back(const std::string&) {return;} void push_back(std::string&& ) {return;} void pop_back() {return;} template void emplace_back(Ts&& ...) {return;} void clear() {return;} size_type size() const noexcept {return 0;} size_type max_size() const noexcept {return 0;} size_type capacity() const noexcept {return 0;} bool empty() const noexcept {return true;} void reserve(size_type) {return;} void resize(size_type) {return;} void resize(size_type, const std::string&) {return;} void shrink_to_fit() {return;} // DO NOT access to the element of this container. This container is always // empty, so accessing through operator[], front/back, data causes address // error. reference operator[](const size_type) noexcept {return *data();} const_reference operator[](const size_type) const noexcept {return *data();} reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} reference front() noexcept {return *data();} const_reference front() const noexcept {return *data();} reference back() noexcept {return *data();} const_reference back() const noexcept {return *data();} pointer data() noexcept {return nullptr;} const_pointer data() const noexcept {return nullptr;} iterator begin() noexcept {return iterator{};} iterator end() noexcept {return iterator{};} const_iterator begin() const noexcept {return const_iterator{};} const_iterator end() const noexcept {return const_iterator{};} const_iterator cbegin() const noexcept {return const_iterator{};} const_iterator cend() const noexcept {return const_iterator{};} reverse_iterator rbegin() noexcept {return iterator{};} reverse_iterator rend() noexcept {return iterator{};} const_reverse_iterator rbegin() const noexcept {return const_iterator{};} const_reverse_iterator rend() const noexcept {return const_iterator{};} const_reverse_iterator crbegin() const noexcept {return const_iterator{};} const_reverse_iterator crend() const noexcept {return const_iterator{};} }; inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;} inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;} inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;} inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;} inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;} inline void swap(const discard_comments&, const discard_comments&) noexcept {return;} template std::basic_ostream& operator<<(std::basic_ostream& os, const discard_comments&) { return os; } } // toml11 #endif// TOML11_COMMENTS_HPP