mirror of https://github.com/trapexit/mergerfs.git
				
				
			
				 25 changed files with 11350 additions and 0 deletions
			
			
		- 
					1DEPENDENCIES
- 
					38src/toml.hpp
- 
					109src/toml/color.hpp
- 
					306src/toml/combinator.hpp
- 
					484src/toml/comments.hpp
- 
					631src/toml/datetime.hpp
- 
					83src/toml/exception.hpp
- 
					19src/toml/from.hpp
- 
					1154src/toml/get.hpp
- 
					19src/toml/into.hpp
- 
					294src/toml/lexer.hpp
- 
					113src/toml/literal.hpp
- 
					121src/toml/macros.hpp
- 
					2611src/toml/parser.hpp
- 
					416src/toml/region.hpp
- 
					717src/toml/result.hpp
- 
					994src/toml/serializer.hpp
- 
					239src/toml/source_location.hpp
- 
					43src/toml/storage.hpp
- 
					228src/toml/string.hpp
- 
					328src/toml/traits.hpp
- 
					173src/toml/types.hpp
- 
					150src/toml/utility.hpp
- 
					2037src/toml/value.hpp
- 
					42src/toml/version.hpp
| @ -0,0 +1,38 @@ | |||
| /*
 | |||
|  * The MIT License (MIT) | |||
|  * | |||
|  * Copyright (c) 2017 Toru Niina | |||
|  * | |||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | |||
|  * of this software and associated documentation files (the "Software"), to deal | |||
|  * in the Software without restriction, including without limitation the rights | |||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
|  * copies of the Software, and to permit persons to whom the Software is | |||
|  * furnished to do so, subject to the following conditions: | |||
|  * | |||
|  * The above copyright notice and this permission notice shall be included in | |||
|  * all copies or substantial portions of the Software. | |||
|  * | |||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
|  * THE SOFTWARE. | |||
|  */ | |||
| 
 | |||
| #ifndef TOML_FOR_MODERN_CPP
 | |||
| #define TOML_FOR_MODERN_CPP
 | |||
| 
 | |||
| #define TOML11_VERSION_MAJOR 3
 | |||
| #define TOML11_VERSION_MINOR 8
 | |||
| #define TOML11_VERSION_PATCH 1
 | |||
| 
 | |||
| #include "toml/parser.hpp"
 | |||
| #include "toml/literal.hpp"
 | |||
| #include "toml/serializer.hpp"
 | |||
| #include "toml/get.hpp"
 | |||
| #include "toml/macros.hpp"
 | |||
| 
 | |||
| #endif// TOML_FOR_MODERN_CPP
 | |||
| @ -0,0 +1,109 @@ | |||
| #ifndef TOML11_COLOR_HPP
 | |||
| #define TOML11_COLOR_HPP
 | |||
| #include <cstdint>
 | |||
| #include <ostream>
 | |||
| 
 | |||
| #ifdef TOML11_COLORIZE_ERROR_MESSAGE
 | |||
| #define TOML11_ERROR_MESSAGE_COLORIZED true
 | |||
| #else
 | |||
| #define TOML11_ERROR_MESSAGE_COLORIZED false
 | |||
| #endif
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| 
 | |||
| // put ANSI escape sequence to ostream
 | |||
| namespace color_ansi | |||
| { | |||
| namespace detail | |||
| { | |||
| 
 | |||
| inline int colorize_index() | |||
| { | |||
|     static const int index = std::ios_base::xalloc(); | |||
|     return index; | |||
| } | |||
| 
 | |||
| // Control color mode globally
 | |||
| class color_mode | |||
| { | |||
|   public: | |||
|     inline void enable() | |||
|     { | |||
|         should_color_ = true; | |||
|     } | |||
|     inline void disable() | |||
|     { | |||
|         should_color_ = false; | |||
|     } | |||
| 
 | |||
|     inline bool should_color() const | |||
|     { | |||
|         return should_color_; | |||
|     } | |||
| 
 | |||
|     static color_mode& status() | |||
|     { | |||
|         static color_mode status_; | |||
|         return status_; | |||
|     } | |||
| 
 | |||
|   private: | |||
|     bool should_color_ = false; | |||
| }; | |||
| 
 | |||
| } // detail
 | |||
| 
 | |||
| inline std::ostream& colorize(std::ostream& os) | |||
| { | |||
|     // by default, it is zero.
 | |||
|     os.iword(detail::colorize_index()) = 1; | |||
|     return os; | |||
| } | |||
| inline std::ostream& nocolorize(std::ostream& os) | |||
| { | |||
|     os.iword(detail::colorize_index()) = 0; | |||
|     return os; | |||
| } | |||
| inline std::ostream& reset  (std::ostream& os) | |||
| {if(os.iword(detail::colorize_index()) == 1) {os << "\033[00m";} return os;} | |||
| inline std::ostream& bold   (std::ostream& os) | |||
| {if(os.iword(detail::colorize_index()) == 1) {os << "\033[01m";} return os;} | |||
| inline std::ostream& grey   (std::ostream& os) | |||
| {if(os.iword(detail::colorize_index()) == 1) {os << "\033[30m";} return os;} | |||
| inline std::ostream& red    (std::ostream& os) | |||
| {if(os.iword(detail::colorize_index()) == 1) {os << "\033[31m";} return os;} | |||
| inline std::ostream& green  (std::ostream& os) | |||
| {if(os.iword(detail::colorize_index()) == 1) {os << "\033[32m";} return os;} | |||
| inline std::ostream& yellow (std::ostream& os) | |||
| {if(os.iword(detail::colorize_index()) == 1) {os << "\033[33m";} return os;} | |||
| inline std::ostream& blue   (std::ostream& os) | |||
| {if(os.iword(detail::colorize_index()) == 1) {os << "\033[34m";} return os;} | |||
| inline std::ostream& magenta(std::ostream& os) | |||
| {if(os.iword(detail::colorize_index()) == 1) {os << "\033[35m";} return os;} | |||
| inline std::ostream& cyan   (std::ostream& os) | |||
| {if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;} | |||
| inline std::ostream& white  (std::ostream& os) | |||
| {if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;} | |||
| 
 | |||
| inline void enable() | |||
| { | |||
|     return detail::color_mode::status().enable(); | |||
| } | |||
| inline void disable() | |||
| { | |||
|     return detail::color_mode::status().disable(); | |||
| } | |||
| 
 | |||
| inline bool should_color() | |||
| { | |||
|     return detail::color_mode::status().should_color(); | |||
| } | |||
| 
 | |||
| } // color_ansi
 | |||
| 
 | |||
| // ANSI escape sequence is the only and default colorization method currently
 | |||
| namespace color = color_ansi; | |||
| 
 | |||
| } // toml
 | |||
| #endif// TOML11_COLOR_HPP
 | |||
| @ -0,0 +1,306 @@ | |||
| //     Copyright Toru Niina 2017.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_COMBINATOR_HPP
 | |||
| #define TOML11_COMBINATOR_HPP
 | |||
| #include <cassert>
 | |||
| #include <cctype>
 | |||
| #include <cstdio>
 | |||
| 
 | |||
| #include <array>
 | |||
| #include <iomanip>
 | |||
| #include <iterator>
 | |||
| #include <limits>
 | |||
| #include <type_traits>
 | |||
| 
 | |||
| #include "region.hpp"
 | |||
| #include "result.hpp"
 | |||
| #include "traits.hpp"
 | |||
| #include "utility.hpp"
 | |||
| 
 | |||
| // they scans characters and returns region if it matches to the condition.
 | |||
| // when they fail, it does not change the location.
 | |||
| // in lexer.hpp, these are used.
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| namespace detail | |||
| { | |||
| 
 | |||
| // to output character as an error message.
 | |||
| inline std::string show_char(const char c) | |||
| { | |||
|     // It suppresses an error that occurs only in Debug mode of MSVC++ on Windows.
 | |||
|     // I'm not completely sure but they check the value of char to be in the
 | |||
|     // range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes
 | |||
|     // has negative value (if char has sign). So here it re-interprets c as
 | |||
|     // unsigned char through pointer. In general, converting pointer to a
 | |||
|     // pointer that has different type cause UB, but `(signed|unsigned)?char`
 | |||
|     // are one of the exceptions. Converting pointer only to char and std::byte
 | |||
|     // (c++17) are valid.
 | |||
|     if(std::isgraph(*reinterpret_cast<unsigned char const*>(std::addressof(c)))) | |||
|     { | |||
|         return std::string(1, c); | |||
|     } | |||
|     else | |||
|     { | |||
|         std::array<char, 5> buf; | |||
|         buf.fill('\0'); | |||
|         const auto r = std::snprintf( | |||
|                 buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF); | |||
|         (void) r; // Unused variable warning
 | |||
|         assert(r == static_cast<int>(buf.size()) - 1); | |||
|         return std::string(buf.data()); | |||
|     } | |||
| } | |||
| 
 | |||
| template<char C> | |||
| struct character | |||
| { | |||
|     static constexpr char target = C; | |||
| 
 | |||
|     static result<region, none_t> | |||
|     invoke(location& loc) | |||
|     { | |||
|         if(loc.iter() == loc.end()) {return none();} | |||
|         const auto first = loc.iter(); | |||
| 
 | |||
|         const char c = *(loc.iter()); | |||
|         if(c != target) | |||
|         { | |||
|             return none(); | |||
|         } | |||
|         loc.advance(); // update location
 | |||
| 
 | |||
|         return ok(region(loc, first, loc.iter())); | |||
|     } | |||
| }; | |||
| template<char C> | |||
| constexpr char character<C>::target; | |||
| 
 | |||
| // closed interval [Low, Up]. both Low and Up are included.
 | |||
| template<char Low, char Up> | |||
| struct in_range | |||
| { | |||
|     // assuming ascii part of UTF-8...
 | |||
|     static_assert(Low <= Up, "lower bound should be less than upper bound."); | |||
| 
 | |||
|     static constexpr char upper = Up; | |||
|     static constexpr char lower = Low; | |||
| 
 | |||
|     static result<region, none_t> | |||
|     invoke(location& loc) | |||
|     { | |||
|         if(loc.iter() == loc.end()) {return none();} | |||
|         const auto first = loc.iter(); | |||
| 
 | |||
|         const char c = *(loc.iter()); | |||
|         if(c < lower || upper < c) | |||
|         { | |||
|             return none(); | |||
|         } | |||
| 
 | |||
|         loc.advance(); | |||
|         return ok(region(loc, first, loc.iter())); | |||
|     } | |||
| }; | |||
| template<char L, char U> constexpr char in_range<L, U>::upper; | |||
| template<char L, char U> constexpr char in_range<L, U>::lower; | |||
| 
 | |||
| // keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char.
 | |||
| // for detecting invalid characters, like control sequences in toml string.
 | |||
| template<typename Combinator> | |||
| struct exclude | |||
| { | |||
|     static result<region, none_t> | |||
|     invoke(location& loc) | |||
|     { | |||
|         if(loc.iter() == loc.end()) {return none();} | |||
|         auto first = loc.iter(); | |||
| 
 | |||
|         auto rslt = Combinator::invoke(loc); | |||
|         if(rslt.is_ok()) | |||
|         { | |||
|             loc.reset(first); | |||
|             return none(); | |||
|         } | |||
|         loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but...
 | |||
|         return ok(region(loc, first, loc.iter())); | |||
|     } | |||
| }; | |||
| 
 | |||
| // increment `iter`, if matches. otherwise, just return empty string.
 | |||
| template<typename Combinator> | |||
| struct maybe | |||
| { | |||
|     static result<region, none_t> | |||
|     invoke(location& loc) | |||
|     { | |||
|         const auto rslt = Combinator::invoke(loc); | |||
|         if(rslt.is_ok()) | |||
|         { | |||
|             return rslt; | |||
|         } | |||
|         return ok(region(loc)); | |||
|     } | |||
| }; | |||
| 
 | |||
| template<typename ... Ts> | |||
| struct sequence; | |||
| 
 | |||
| template<typename Head, typename ... Tail> | |||
| struct sequence<Head, Tail...> | |||
| { | |||
|     static result<region, none_t> | |||
|     invoke(location& loc) | |||
|     { | |||
|         const auto first = loc.iter(); | |||
|         auto rslt = Head::invoke(loc); | |||
|         if(rslt.is_err()) | |||
|         { | |||
|             loc.reset(first); | |||
|             return none(); | |||
|         } | |||
|         return sequence<Tail...>::invoke(loc, std::move(rslt.unwrap()), first); | |||
|     } | |||
| 
 | |||
|     // called from the above function only, recursively.
 | |||
|     template<typename Iterator> | |||
|     static result<region, none_t> | |||
|     invoke(location& loc, region reg, Iterator first) | |||
|     { | |||
|         const auto rslt = Head::invoke(loc); | |||
|         if(rslt.is_err()) | |||
|         { | |||
|             loc.reset(first); | |||
|             return none(); | |||
|         } | |||
|         reg += rslt.unwrap(); // concat regions
 | |||
|         return sequence<Tail...>::invoke(loc, std::move(reg), first); | |||
|     } | |||
| }; | |||
| 
 | |||
| template<typename Head> | |||
| struct sequence<Head> | |||
| { | |||
|     // would be called from sequence<T ...>::invoke only.
 | |||
|     template<typename Iterator> | |||
|     static result<region, none_t> | |||
|     invoke(location& loc, region reg, Iterator first) | |||
|     { | |||
|         const auto rslt = Head::invoke(loc); | |||
|         if(rslt.is_err()) | |||
|         { | |||
|             loc.reset(first); | |||
|             return none(); | |||
|         } | |||
|         reg += rslt.unwrap(); // concat regions
 | |||
|         return ok(reg); | |||
|     } | |||
| }; | |||
| 
 | |||
| template<typename ... Ts> | |||
| struct either; | |||
| 
 | |||
| template<typename Head, typename ... Tail> | |||
| struct either<Head, Tail...> | |||
| { | |||
|     static result<region, none_t> | |||
|     invoke(location& loc) | |||
|     { | |||
|         const auto rslt = Head::invoke(loc); | |||
|         if(rslt.is_ok()) {return rslt;} | |||
|         return either<Tail...>::invoke(loc); | |||
|     } | |||
| }; | |||
| template<typename Head> | |||
| struct either<Head> | |||
| { | |||
|     static result<region, none_t> | |||
|     invoke(location& loc) | |||
|     { | |||
|         return Head::invoke(loc); | |||
|     } | |||
| }; | |||
| 
 | |||
| template<typename T, typename N> | |||
| struct repeat; | |||
| 
 | |||
| template<std::size_t N> struct exactly{}; | |||
| template<std::size_t N> struct at_least{}; | |||
| struct unlimited{}; | |||
| 
 | |||
| template<typename T, std::size_t N> | |||
| struct repeat<T, exactly<N>> | |||
| { | |||
|     static result<region, none_t> | |||
|     invoke(location& loc) | |||
|     { | |||
|         region retval(loc); | |||
|         const auto first = loc.iter(); | |||
|         for(std::size_t i=0; i<N; ++i) | |||
|         { | |||
|             auto rslt = T::invoke(loc); | |||
|             if(rslt.is_err()) | |||
|             { | |||
|                 loc.reset(first); | |||
|                 return none(); | |||
|             } | |||
|             retval += rslt.unwrap(); | |||
|         } | |||
|         return ok(std::move(retval)); | |||
|     } | |||
| }; | |||
| 
 | |||
| template<typename T, std::size_t N> | |||
| struct repeat<T, at_least<N>> | |||
| { | |||
|     static result<region, none_t> | |||
|     invoke(location& loc) | |||
|     { | |||
|         region retval(loc); | |||
| 
 | |||
|         const auto first = loc.iter(); | |||
|         for(std::size_t i=0; i<N; ++i) | |||
|         { | |||
|             auto rslt = T::invoke(loc); | |||
|             if(rslt.is_err()) | |||
|             { | |||
|                 loc.reset(first); | |||
|                 return none(); | |||
|             } | |||
|             retval += rslt.unwrap(); | |||
|         } | |||
|         while(true) | |||
|         { | |||
|             auto rslt = T::invoke(loc); | |||
|             if(rslt.is_err()) | |||
|             { | |||
|                 return ok(std::move(retval)); | |||
|             } | |||
|             retval += rslt.unwrap(); | |||
|         } | |||
|     } | |||
| }; | |||
| 
 | |||
| template<typename T> | |||
| struct repeat<T, unlimited> | |||
| { | |||
|     static result<region, none_t> | |||
|     invoke(location& loc) | |||
|     { | |||
|         region retval(loc); | |||
|         while(true) | |||
|         { | |||
|             auto rslt = T::invoke(loc); | |||
|             if(rslt.is_err()) | |||
|             { | |||
|                 return ok(std::move(retval)); | |||
|             } | |||
|             retval += rslt.unwrap(); | |||
|         } | |||
|     } | |||
| }; | |||
| 
 | |||
| } // detail
 | |||
| } // toml
 | |||
| #endif// TOML11_COMBINATOR_HPP
 | |||
| @ -0,0 +1,484 @@ | |||
| //     Copyright Toru Niina 2019.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_COMMENTS_HPP
 | |||
| #define TOML11_COMMENTS_HPP
 | |||
| #include <initializer_list>
 | |||
| #include <iterator>
 | |||
| #include <stdexcept>
 | |||
| #include <string>
 | |||
| #include <type_traits>
 | |||
| #include <utility>
 | |||
| #include <vector>
 | |||
| 
 | |||
| #ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT
 | |||
| #  define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments
 | |||
| #else
 | |||
| #  define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments
 | |||
| #endif
 | |||
| 
 | |||
| // This file provides mainly two classes, `preserve_comments` and `discard_comments`.
 | |||
| // Those two are a container that have the same interface as `std::vector<std::string>`
 | |||
| // but bahaves in the opposite way. `preserve_comments` is just the same as
 | |||
| // `std::vector<std::string>` 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<toml::preserve_comments> data =
 | |||
| //     toml::parse<toml::preserve_comments>("example.toml");
 | |||
| //
 | |||
| // the interface is almost the same as std::vector<std::string>.
 | |||
| 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<std::string>; | |||
| 
 | |||
|     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<std::string>& c): comments(c){} | |||
|     explicit preserve_comments(std::vector<std::string>&& c) | |||
|         : comments(std::move(c)) | |||
|     {} | |||
|     preserve_comments& operator=(const std::vector<std::string>& c) | |||
|     { | |||
|         comments = c; | |||
|         return *this; | |||
|     } | |||
|     preserve_comments& operator=(std::vector<std::string>&& 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<std::string> x): comments(x) {} | |||
|     template<typename InputIterator> | |||
|     preserve_comments(InputIterator first, InputIterator last) | |||
|         : comments(first, last) | |||
|     {} | |||
| 
 | |||
|     template<typename InputIterator> | |||
|     void assign(InputIterator first, InputIterator last) {comments.assign(first, last);} | |||
|     void assign(std::initializer_list<std::string> 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<typename InputIterator> | |||
|     void insert(iterator p, InputIterator first, InputIterator last) | |||
|     { | |||
|         return comments.insert(p, first, last); | |||
|     } | |||
|     void insert(iterator p, std::initializer_list<std::string> ini) | |||
|     { | |||
|         return comments.insert(p, ini); | |||
|     } | |||
| 
 | |||
|     template<typename ... Ts> | |||
|     iterator emplace(iterator p, Ts&& ... args) | |||
|     { | |||
|         return comments.emplace(p, std::forward<Ts>(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<typename InputIterator> | |||
|     iterator insert(const_iterator p, InputIterator first, InputIterator last) | |||
|     { | |||
|         return comments.insert(p, first, last); | |||
|     } | |||
|     iterator insert(const_iterator p, std::initializer_list<std::string> ini) | |||
|     { | |||
|         return comments.insert(p, ini); | |||
|     } | |||
| 
 | |||
|     template<typename ... Ts> | |||
|     iterator emplace(const_iterator p, Ts&& ... args) | |||
|     { | |||
|         return comments.emplace(p, std::forward<Ts>(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<typename ... Ts> | |||
|     void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward<Ts>(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<std::string>&); | |||
|     friend void swap(std::vector<std::string>&, 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<std::string>& rhs) | |||
| { | |||
|     lhs.comments.swap(rhs); | |||
|     return; | |||
| } | |||
| inline void swap(std::vector<std::string>& lhs, preserve_comments& rhs) | |||
| { | |||
|     lhs.swap(rhs.comments); | |||
|     return; | |||
| } | |||
| 
 | |||
| template<typename charT, typename traits> | |||
| std::basic_ostream<charT, traits>& | |||
| operator<<(std::basic_ostream<charT, traits>& 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<typename T, bool is_const> | |||
| struct empty_iterator | |||
| { | |||
|     using value_type        = T; | |||
|     using reference_type    = typename std::conditional<is_const, T const&, T&>::type; | |||
|     using pointer_type      = typename std::conditional<is_const, T const*, T*>::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<typename T, bool C> | |||
| bool operator==(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;} | |||
| template<typename T, bool C> | |||
| bool operator!=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;} | |||
| template<typename T, bool C> | |||
| bool operator< (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;} | |||
| template<typename T, bool C> | |||
| bool operator<=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;} | |||
| template<typename T, bool C> | |||
| bool operator> (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;} | |||
| template<typename T, bool C> | |||
| bool operator>=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;} | |||
| 
 | |||
| template<typename T, bool C> | |||
| typename empty_iterator<T, C>::difference_type | |||
| operator-(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return 0;} | |||
| 
 | |||
| template<typename T, bool C> | |||
| empty_iterator<T, C> | |||
| operator+(typename empty_iterator<T, C>::difference_type, const empty_iterator<T, C>& rhs) noexcept {return rhs;} | |||
| template<typename T, bool C> | |||
| empty_iterator<T, C> | |||
| operator+(const empty_iterator<T, C>& lhs, typename empty_iterator<T, C>::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 chosen 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<std::string, false>; | |||
|     using const_iterator         = detail::empty_iterator<std::string, true>; | |||
|     using reverse_iterator       = detail::empty_iterator<std::string, false>; | |||
|     using const_reverse_iterator = detail::empty_iterator<std::string, true>; | |||
| 
 | |||
|     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<std::string>&) noexcept {} | |||
|     explicit discard_comments(std::vector<std::string>&&)      noexcept {} | |||
|     discard_comments& operator=(const std::vector<std::string>&) noexcept {return *this;} | |||
|     discard_comments& operator=(std::vector<std::string>&&)      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<std::string>) noexcept {} | |||
|     template<typename InputIterator> | |||
|     discard_comments(InputIterator, InputIterator) noexcept {} | |||
| 
 | |||
|     template<typename InputIterator> | |||
|     void assign(InputIterator, InputIterator)       noexcept {} | |||
|     void assign(std::initializer_list<std::string>) 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<typename InputIterator> | |||
|     iterator insert(const_iterator, InputIterator, InputIterator)       {return iterator{};} | |||
|     iterator insert(const_iterator, std::initializer_list<std::string>) {return iterator{};} | |||
| 
 | |||
|     template<typename ... Ts> | |||
|     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<typename ... Ts> | |||
|     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 {never_call("toml::discard_comment::operator[]");} | |||
|     const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");} | |||
|     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 {never_call("toml::discard_comment::front");} | |||
|     const_reference front() const noexcept {never_call("toml::discard_comment::front");} | |||
|     reference       back()        noexcept {never_call("toml::discard_comment::back");} | |||
|     const_reference back()  const noexcept {never_call("toml::discard_comment::back");} | |||
| 
 | |||
|     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{};} | |||
| 
 | |||
|   private: | |||
| 
 | |||
|     [[noreturn]] static void never_call(const char *const this_function) | |||
|     { | |||
| #ifdef __has_builtin
 | |||
| #  if __has_builtin(__builtin_unreachable)
 | |||
|         __builtin_unreachable(); | |||
| #  endif
 | |||
| #endif
 | |||
|         throw std::logic_error{this_function}; | |||
|     } | |||
| }; | |||
| 
 | |||
| 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<typename charT, typename traits> | |||
| std::basic_ostream<charT, traits>& | |||
| operator<<(std::basic_ostream<charT, traits>& os, const discard_comments&) | |||
| { | |||
|     return os; | |||
| } | |||
| 
 | |||
| } // toml11
 | |||
| #endif// TOML11_COMMENTS_HPP
 | |||
| @ -0,0 +1,631 @@ | |||
| //     Copyright Toru Niina 2017.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_DATETIME_HPP
 | |||
| #define TOML11_DATETIME_HPP
 | |||
| #include <cstdint>
 | |||
| #include <cstdlib>
 | |||
| #include <ctime>
 | |||
| 
 | |||
| #include <array>
 | |||
| #include <chrono>
 | |||
| #include <iomanip>
 | |||
| #include <ostream>
 | |||
| #include <tuple>
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| 
 | |||
| // To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is
 | |||
| // provided in the absolutely same purpose, but C++11 is actually not compatible
 | |||
| // with C11. We need to dispatch the function depending on the OS.
 | |||
| namespace detail | |||
| { | |||
| // TODO: find more sophisticated way to handle this
 | |||
| #if defined(_MSC_VER)
 | |||
| inline std::tm localtime_s(const std::time_t* src) | |||
| { | |||
|     std::tm dst; | |||
|     const auto result = ::localtime_s(&dst, src); | |||
|     if (result) { throw std::runtime_error("localtime_s failed."); } | |||
|     return dst; | |||
| } | |||
| inline std::tm gmtime_s(const std::time_t* src) | |||
| { | |||
|     std::tm dst; | |||
|     const auto result = ::gmtime_s(&dst, src); | |||
|     if (result) { throw std::runtime_error("gmtime_s failed."); } | |||
|     return dst; | |||
| } | |||
| #elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
 | |||
| inline std::tm localtime_s(const std::time_t* src) | |||
| { | |||
|     std::tm dst; | |||
|     const auto result = ::localtime_r(src, &dst); | |||
|     if (!result) { throw std::runtime_error("localtime_r failed."); } | |||
|     return dst; | |||
| } | |||
| inline std::tm gmtime_s(const std::time_t* src) | |||
| { | |||
|     std::tm dst; | |||
|     const auto result = ::gmtime_r(src, &dst); | |||
|     if (!result) { throw std::runtime_error("gmtime_r failed."); } | |||
|     return dst; | |||
| } | |||
| #else // fallback. not threadsafe
 | |||
| inline std::tm localtime_s(const std::time_t* src) | |||
| { | |||
|     const auto result = std::localtime(src); | |||
|     if (!result) { throw std::runtime_error("localtime failed."); } | |||
|     return *result; | |||
| } | |||
| inline std::tm gmtime_s(const std::time_t* src) | |||
| { | |||
|     const auto result = std::gmtime(src); | |||
|     if (!result) { throw std::runtime_error("gmtime failed."); } | |||
|     return *result; | |||
| } | |||
| #endif
 | |||
| } // detail
 | |||
| 
 | |||
| enum class month_t : std::uint8_t | |||
| { | |||
|     Jan =  0, | |||
|     Feb =  1, | |||
|     Mar =  2, | |||
|     Apr =  3, | |||
|     May =  4, | |||
|     Jun =  5, | |||
|     Jul =  6, | |||
|     Aug =  7, | |||
|     Sep =  8, | |||
|     Oct =  9, | |||
|     Nov = 10, | |||
|     Dec = 11 | |||
| }; | |||
| 
 | |||
| struct local_date | |||
| { | |||
|     std::int16_t year{};   // A.D. (like, 2018)
 | |||
|     std::uint8_t month{};  // [0, 11]
 | |||
|     std::uint8_t day{};    // [1, 31]
 | |||
| 
 | |||
|     local_date(int y, month_t m, int d) | |||
|         : year (static_cast<std::int16_t>(y)), | |||
|           month(static_cast<std::uint8_t>(m)), | |||
|           day  (static_cast<std::uint8_t>(d)) | |||
|     {} | |||
| 
 | |||
|     explicit local_date(const std::tm& t) | |||
|         : year (static_cast<std::int16_t>(t.tm_year + 1900)), | |||
|           month(static_cast<std::uint8_t>(t.tm_mon)), | |||
|           day  (static_cast<std::uint8_t>(t.tm_mday)) | |||
|     {} | |||
| 
 | |||
|     explicit local_date(const std::chrono::system_clock::time_point& tp) | |||
|     { | |||
|         const auto t    = std::chrono::system_clock::to_time_t(tp); | |||
|         const auto time = detail::localtime_s(&t); | |||
|         *this = local_date(time); | |||
|     } | |||
| 
 | |||
|     explicit local_date(const std::time_t t) | |||
|         : local_date(std::chrono::system_clock::from_time_t(t)) | |||
|     {} | |||
| 
 | |||
|     operator std::chrono::system_clock::time_point() const | |||
|     { | |||
|         // std::mktime returns date as local time zone. no conversion needed
 | |||
|         std::tm t; | |||
|         t.tm_sec   = 0; | |||
|         t.tm_min   = 0; | |||
|         t.tm_hour  = 0; | |||
|         t.tm_mday  = static_cast<int>(this->day); | |||
|         t.tm_mon   = static_cast<int>(this->month); | |||
|         t.tm_year  = static_cast<int>(this->year) - 1900; | |||
|         t.tm_wday  = 0; // the value will be ignored
 | |||
|         t.tm_yday  = 0; // the value will be ignored
 | |||
|         t.tm_isdst = -1; | |||
|         return std::chrono::system_clock::from_time_t(std::mktime(&t)); | |||
|     } | |||
| 
 | |||
|     operator std::time_t() const | |||
|     { | |||
|         return std::chrono::system_clock::to_time_t( | |||
|                 std::chrono::system_clock::time_point(*this)); | |||
|     } | |||
| 
 | |||
|     local_date() = default; | |||
|     ~local_date() = default; | |||
|     local_date(local_date const&) = default; | |||
|     local_date(local_date&&)      = default; | |||
|     local_date& operator=(local_date const&) = default; | |||
|     local_date& operator=(local_date&&)      = default; | |||
| }; | |||
| 
 | |||
| inline bool operator==(const local_date& lhs, const local_date& rhs) | |||
| { | |||
|     return std::make_tuple(lhs.year, lhs.month, lhs.day) == | |||
|            std::make_tuple(rhs.year, rhs.month, rhs.day); | |||
| } | |||
| inline bool operator!=(const local_date& lhs, const local_date& rhs) | |||
| { | |||
|     return !(lhs == rhs); | |||
| } | |||
| inline bool operator< (const local_date& lhs, const local_date& rhs) | |||
| { | |||
|     return std::make_tuple(lhs.year, lhs.month, lhs.day) < | |||
|            std::make_tuple(rhs.year, rhs.month, rhs.day); | |||
| } | |||
| inline bool operator<=(const local_date& lhs, const local_date& rhs) | |||
| { | |||
|     return (lhs < rhs) || (lhs == rhs); | |||
| } | |||
| inline bool operator> (const local_date& lhs, const local_date& rhs) | |||
| { | |||
|     return !(lhs <= rhs); | |||
| } | |||
| inline bool operator>=(const local_date& lhs, const local_date& rhs) | |||
| { | |||
|     return !(lhs < rhs); | |||
| } | |||
| 
 | |||
| template<typename charT, typename traits> | |||
| std::basic_ostream<charT, traits>& | |||
| operator<<(std::basic_ostream<charT, traits>& os, const local_date& date) | |||
| { | |||
|     os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year )     << '-'; | |||
|     os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month) + 1 << '-'; | |||
|     os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day  )    ; | |||
|     return os; | |||
| } | |||
| 
 | |||
| struct local_time | |||
| { | |||
|     std::uint8_t  hour{};        // [0, 23]
 | |||
|     std::uint8_t  minute{};      // [0, 59]
 | |||
|     std::uint8_t  second{};      // [0, 60]
 | |||
|     std::uint16_t millisecond{}; // [0, 999]
 | |||
|     std::uint16_t microsecond{}; // [0, 999]
 | |||
|     std::uint16_t nanosecond{};  // [0, 999]
 | |||
| 
 | |||
|     local_time(int h, int m, int s, | |||
|                int ms = 0, int us = 0, int ns = 0) | |||
|         : hour  (static_cast<std::uint8_t>(h)), | |||
|           minute(static_cast<std::uint8_t>(m)), | |||
|           second(static_cast<std::uint8_t>(s)), | |||
|           millisecond(static_cast<std::uint16_t>(ms)), | |||
|           microsecond(static_cast<std::uint16_t>(us)), | |||
|           nanosecond (static_cast<std::uint16_t>(ns)) | |||
|     {} | |||
| 
 | |||
|     explicit local_time(const std::tm& t) | |||
|         : hour  (static_cast<std::uint8_t>(t.tm_hour)), | |||
|           minute(static_cast<std::uint8_t>(t.tm_min)), | |||
|           second(static_cast<std::uint8_t>(t.tm_sec)), | |||
|           millisecond(0), microsecond(0), nanosecond(0) | |||
|     {} | |||
| 
 | |||
|     template<typename Rep, typename Period> | |||
|     explicit local_time(const std::chrono::duration<Rep, Period>& t) | |||
|     { | |||
|         const auto h = std::chrono::duration_cast<std::chrono::hours>(t); | |||
|         this->hour = static_cast<std::uint8_t>(h.count()); | |||
|         const auto t2 = t - h; | |||
|         const auto m = std::chrono::duration_cast<std::chrono::minutes>(t2); | |||
|         this->minute = static_cast<std::uint8_t>(m.count()); | |||
|         const auto t3 = t2 - m; | |||
|         const auto s = std::chrono::duration_cast<std::chrono::seconds>(t3); | |||
|         this->second = static_cast<std::uint8_t>(s.count()); | |||
|         const auto t4 = t3 - s; | |||
|         const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(t4); | |||
|         this->millisecond = static_cast<std::uint16_t>(ms.count()); | |||
|         const auto t5 = t4 - ms; | |||
|         const auto us = std::chrono::duration_cast<std::chrono::microseconds>(t5); | |||
|         this->microsecond = static_cast<std::uint16_t>(us.count()); | |||
|         const auto t6 = t5 - us; | |||
|         const auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(t6); | |||
|         this->nanosecond = static_cast<std::uint16_t>(ns.count()); | |||
|     } | |||
| 
 | |||
|     operator std::chrono::nanoseconds() const | |||
|     { | |||
|         return std::chrono::nanoseconds (this->nanosecond)  + | |||
|                std::chrono::microseconds(this->microsecond) + | |||
|                std::chrono::milliseconds(this->millisecond) + | |||
|                std::chrono::seconds(this->second) + | |||
|                std::chrono::minutes(this->minute) + | |||
|                std::chrono::hours(this->hour); | |||
|     } | |||
| 
 | |||
|     local_time() = default; | |||
|     ~local_time() = default; | |||
|     local_time(local_time const&) = default; | |||
|     local_time(local_time&&)      = default; | |||
|     local_time& operator=(local_time const&) = default; | |||
|     local_time& operator=(local_time&&)      = default; | |||
| }; | |||
| 
 | |||
| inline bool operator==(const local_time& lhs, const local_time& rhs) | |||
| { | |||
|     return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) == | |||
|            std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); | |||
| } | |||
| inline bool operator!=(const local_time& lhs, const local_time& rhs) | |||
| { | |||
|     return !(lhs == rhs); | |||
| } | |||
| inline bool operator< (const local_time& lhs, const local_time& rhs) | |||
| { | |||
|     return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) < | |||
|            std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); | |||
| } | |||
| inline bool operator<=(const local_time& lhs, const local_time& rhs) | |||
| { | |||
|     return (lhs < rhs) || (lhs == rhs); | |||
| } | |||
| inline bool operator> (const local_time& lhs, const local_time& rhs) | |||
| { | |||
|     return !(lhs <= rhs); | |||
| } | |||
| inline bool operator>=(const local_time& lhs, const local_time& rhs) | |||
| { | |||
|     return !(lhs < rhs); | |||
| } | |||
| 
 | |||
| template<typename charT, typename traits> | |||
| std::basic_ostream<charT, traits>& | |||
| operator<<(std::basic_ostream<charT, traits>& os, const local_time& time) | |||
| { | |||
|     os << std::setfill('0') << std::setw(2) << static_cast<int>(time.hour  ) << ':'; | |||
|     os << std::setfill('0') << std::setw(2) << static_cast<int>(time.minute) << ':'; | |||
|     os << std::setfill('0') << std::setw(2) << static_cast<int>(time.second); | |||
|     if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) | |||
|     { | |||
|         os << '.'; | |||
|         os << std::setfill('0') << std::setw(3) << static_cast<int>(time.millisecond); | |||
|         if(time.microsecond != 0 || time.nanosecond != 0) | |||
|         { | |||
|             os << std::setfill('0') << std::setw(3) << static_cast<int>(time.microsecond); | |||
|             if(time.nanosecond != 0) | |||
|             { | |||
|                 os << std::setfill('0') << std::setw(3) << static_cast<int>(time.nanosecond); | |||
|             } | |||
|         } | |||
|     } | |||
|     return os; | |||
| } | |||
| 
 | |||
| struct time_offset | |||
| { | |||
|     std::int8_t hour{};   // [-12, 12]
 | |||
|     std::int8_t minute{}; // [-59, 59]
 | |||
| 
 | |||
|     time_offset(int h, int m) | |||
|         : hour  (static_cast<std::int8_t>(h)), | |||
|           minute(static_cast<std::int8_t>(m)) | |||
|     {} | |||
| 
 | |||
|     operator std::chrono::minutes() const | |||
|     { | |||
|         return std::chrono::minutes(this->minute) + | |||
|                std::chrono::hours(this->hour); | |||
|     } | |||
| 
 | |||
|     time_offset() = default; | |||
|     ~time_offset() = default; | |||
|     time_offset(time_offset const&) = default; | |||
|     time_offset(time_offset&&)      = default; | |||
|     time_offset& operator=(time_offset const&) = default; | |||
|     time_offset& operator=(time_offset&&)      = default; | |||
| }; | |||
| 
 | |||
| inline bool operator==(const time_offset& lhs, const time_offset& rhs) | |||
| { | |||
|     return std::make_tuple(lhs.hour, lhs.minute) == | |||
|            std::make_tuple(rhs.hour, rhs.minute); | |||
| } | |||
| inline bool operator!=(const time_offset& lhs, const time_offset& rhs) | |||
| { | |||
|     return !(lhs == rhs); | |||
| } | |||
| inline bool operator< (const time_offset& lhs, const time_offset& rhs) | |||
| { | |||
|     return std::make_tuple(lhs.hour, lhs.minute) < | |||
|            std::make_tuple(rhs.hour, rhs.minute); | |||
| } | |||
| inline bool operator<=(const time_offset& lhs, const time_offset& rhs) | |||
| { | |||
|     return (lhs < rhs) || (lhs == rhs); | |||
| } | |||
| inline bool operator> (const time_offset& lhs, const time_offset& rhs) | |||
| { | |||
|     return !(lhs <= rhs); | |||
| } | |||
| inline bool operator>=(const time_offset& lhs, const time_offset& rhs) | |||
| { | |||
|     return !(lhs < rhs); | |||
| } | |||
| 
 | |||
| template<typename charT, typename traits> | |||
| std::basic_ostream<charT, traits>& | |||
| operator<<(std::basic_ostream<charT, traits>& os, const time_offset& offset) | |||
| { | |||
|     if(offset.hour == 0 && offset.minute == 0) | |||
|     { | |||
|         os << 'Z'; | |||
|         return os; | |||
|     } | |||
|     int minute = static_cast<int>(offset.hour) * 60 + offset.minute; | |||
|     if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';} | |||
|     os << std::setfill('0') << std::setw(2) << minute / 60 << ':'; | |||
|     os << std::setfill('0') << std::setw(2) << minute % 60; | |||
|     return os; | |||
| } | |||
| 
 | |||
| struct local_datetime | |||
| { | |||
|     local_date date{}; | |||
|     local_time time{}; | |||
| 
 | |||
|     local_datetime(local_date d, local_time t): date(d), time(t) {} | |||
| 
 | |||
|     explicit local_datetime(const std::tm& t): date(t), time(t){} | |||
| 
 | |||
|     explicit local_datetime(const std::chrono::system_clock::time_point& tp) | |||
|     { | |||
|         const auto t = std::chrono::system_clock::to_time_t(tp); | |||
|         std::tm ltime = detail::localtime_s(&t); | |||
| 
 | |||
|         this->date = local_date(ltime); | |||
|         this->time = local_time(ltime); | |||
| 
 | |||
|         // std::tm lacks subsecond information, so diff between tp and tm
 | |||
|         // can be used to get millisecond & microsecond information.
 | |||
|         const auto t_diff = tp - | |||
|             std::chrono::system_clock::from_time_t(std::mktime(<ime)); | |||
|         this->time.millisecond = static_cast<std::uint16_t>( | |||
|           std::chrono::duration_cast<std::chrono::milliseconds>(t_diff).count()); | |||
|         this->time.microsecond = static_cast<std::uint16_t>( | |||
|           std::chrono::duration_cast<std::chrono::microseconds>(t_diff).count()); | |||
|         this->time.nanosecond = static_cast<std::uint16_t>( | |||
|           std::chrono::duration_cast<std::chrono::nanoseconds >(t_diff).count()); | |||
|     } | |||
| 
 | |||
|     explicit local_datetime(const std::time_t t) | |||
|         : local_datetime(std::chrono::system_clock::from_time_t(t)) | |||
|     {} | |||
| 
 | |||
|     operator std::chrono::system_clock::time_point() const | |||
|     { | |||
|         using internal_duration = | |||
|             typename std::chrono::system_clock::time_point::duration; | |||
| 
 | |||
|         // Normally DST begins at A.M. 3 or 4. If we re-use conversion operator
 | |||
|         // of local_date and local_time independently, the conversion fails if
 | |||
|         // it is the day when DST begins or ends. Since local_date considers the
 | |||
|         // time is 00:00 A.M. and local_time does not consider DST because it
 | |||
|         // does not have any date information. We need to consider both date and
 | |||
|         // time information at the same time to convert it correctly.
 | |||
| 
 | |||
|         std::tm t; | |||
|         t.tm_sec   = static_cast<int>(this->time.second); | |||
|         t.tm_min   = static_cast<int>(this->time.minute); | |||
|         t.tm_hour  = static_cast<int>(this->time.hour); | |||
|         t.tm_mday  = static_cast<int>(this->date.day); | |||
|         t.tm_mon   = static_cast<int>(this->date.month); | |||
|         t.tm_year  = static_cast<int>(this->date.year) - 1900; | |||
|         t.tm_wday  = 0; // the value will be ignored
 | |||
|         t.tm_yday  = 0; // the value will be ignored
 | |||
|         t.tm_isdst = -1; | |||
| 
 | |||
|         // std::mktime returns date as local time zone. no conversion needed
 | |||
|         auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t)); | |||
|         dt += std::chrono::duration_cast<internal_duration>( | |||
|                 std::chrono::milliseconds(this->time.millisecond) + | |||
|                 std::chrono::microseconds(this->time.microsecond) + | |||
|                 std::chrono::nanoseconds (this->time.nanosecond)); | |||
|         return dt; | |||
|     } | |||
| 
 | |||
|     operator std::time_t() const | |||
|     { | |||
|         return std::chrono::system_clock::to_time_t( | |||
|                 std::chrono::system_clock::time_point(*this)); | |||
|     } | |||
| 
 | |||
|     local_datetime() = default; | |||
|     ~local_datetime() = default; | |||
|     local_datetime(local_datetime const&) = default; | |||
|     local_datetime(local_datetime&&)      = default; | |||
|     local_datetime& operator=(local_datetime const&) = default; | |||
|     local_datetime& operator=(local_datetime&&)      = default; | |||
| }; | |||
| 
 | |||
| inline bool operator==(const local_datetime& lhs, const local_datetime& rhs) | |||
| { | |||
|     return std::make_tuple(lhs.date, lhs.time) == | |||
|            std::make_tuple(rhs.date, rhs.time); | |||
| } | |||
| inline bool operator!=(const local_datetime& lhs, const local_datetime& rhs) | |||
| { | |||
|     return !(lhs == rhs); | |||
| } | |||
| inline bool operator< (const local_datetime& lhs, const local_datetime& rhs) | |||
| { | |||
|     return std::make_tuple(lhs.date, lhs.time) < | |||
|            std::make_tuple(rhs.date, rhs.time); | |||
| } | |||
| inline bool operator<=(const local_datetime& lhs, const local_datetime& rhs) | |||
| { | |||
|     return (lhs < rhs) || (lhs == rhs); | |||
| } | |||
| inline bool operator> (const local_datetime& lhs, const local_datetime& rhs) | |||
| { | |||
|     return !(lhs <= rhs); | |||
| } | |||
| inline bool operator>=(const local_datetime& lhs, const local_datetime& rhs) | |||
| { | |||
|     return !(lhs < rhs); | |||
| } | |||
| 
 | |||
| template<typename charT, typename traits> | |||
| std::basic_ostream<charT, traits>& | |||
| operator<<(std::basic_ostream<charT, traits>& os, const local_datetime& dt) | |||
| { | |||
|     os << dt.date << 'T' << dt.time; | |||
|     return os; | |||
| } | |||
| 
 | |||
| struct offset_datetime | |||
| { | |||
|     local_date  date{}; | |||
|     local_time  time{}; | |||
|     time_offset offset{}; | |||
| 
 | |||
|     offset_datetime(local_date d, local_time t, time_offset o) | |||
|         : date(d), time(t), offset(o) | |||
|     {} | |||
|     offset_datetime(const local_datetime& dt, time_offset o) | |||
|         : date(dt.date), time(dt.time), offset(o) | |||
|     {} | |||
|     explicit offset_datetime(const local_datetime& ld) | |||
|         : date(ld.date), time(ld.time), offset(get_local_offset(nullptr)) | |||
|           // use the current local timezone offset
 | |||
|     {} | |||
|     explicit offset_datetime(const std::chrono::system_clock::time_point& tp) | |||
|         : offset(0, 0) // use gmtime
 | |||
|     { | |||
|         const auto timet = std::chrono::system_clock::to_time_t(tp); | |||
|         const auto tm    = detail::gmtime_s(&timet); | |||
|         this->date = local_date(tm); | |||
|         this->time = local_time(tm); | |||
|     } | |||
|     explicit offset_datetime(const std::time_t& t) | |||
|         : offset(0, 0) // use gmtime
 | |||
|     { | |||
|         const auto tm    = detail::gmtime_s(&t); | |||
|         this->date = local_date(tm); | |||
|         this->time = local_time(tm); | |||
|     } | |||
|     explicit offset_datetime(const std::tm& t) | |||
|         : offset(0, 0) // assume gmtime
 | |||
|     { | |||
|         this->date = local_date(t); | |||
|         this->time = local_time(t); | |||
|     } | |||
| 
 | |||
|     operator std::chrono::system_clock::time_point() const | |||
|     { | |||
|         // get date-time
 | |||
|         using internal_duration = | |||
|             typename std::chrono::system_clock::time_point::duration; | |||
| 
 | |||
|         // first, convert it to local date-time information in the same way as
 | |||
|         // local_datetime does. later we will use time_t to adjust time offset.
 | |||
|         std::tm t; | |||
|         t.tm_sec   = static_cast<int>(this->time.second); | |||
|         t.tm_min   = static_cast<int>(this->time.minute); | |||
|         t.tm_hour  = static_cast<int>(this->time.hour); | |||
|         t.tm_mday  = static_cast<int>(this->date.day); | |||
|         t.tm_mon   = static_cast<int>(this->date.month); | |||
|         t.tm_year  = static_cast<int>(this->date.year) - 1900; | |||
|         t.tm_wday  = 0; // the value will be ignored
 | |||
|         t.tm_yday  = 0; // the value will be ignored
 | |||
|         t.tm_isdst = -1; | |||
|         const std::time_t tp_loc = std::mktime(std::addressof(t)); | |||
| 
 | |||
|         auto tp = std::chrono::system_clock::from_time_t(tp_loc); | |||
|         tp += std::chrono::duration_cast<internal_duration>( | |||
|                 std::chrono::milliseconds(this->time.millisecond) + | |||
|                 std::chrono::microseconds(this->time.microsecond) + | |||
|                 std::chrono::nanoseconds (this->time.nanosecond)); | |||
| 
 | |||
|         // Since mktime uses local time zone, it should be corrected.
 | |||
|         // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if
 | |||
|         // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need
 | |||
|         // to add `+09:00` to `03:00:00Z`.
 | |||
|         //    Here, it uses the time_t converted from date-time info to handle
 | |||
|         // daylight saving time.
 | |||
|         const auto ofs = get_local_offset(std::addressof(tp_loc)); | |||
|         tp += std::chrono::hours  (ofs.hour); | |||
|         tp += std::chrono::minutes(ofs.minute); | |||
| 
 | |||
|         // We got `12:00:00Z` by correcting local timezone applied by mktime.
 | |||
|         // Then we will apply the offset. Let's say `12:00:00-08:00` is given.
 | |||
|         // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`.
 | |||
|         // So we need to subtract the offset.
 | |||
|         tp -= std::chrono::minutes(this->offset); | |||
|         return tp; | |||
|     } | |||
| 
 | |||
|     operator std::time_t() const | |||
|     { | |||
|         return std::chrono::system_clock::to_time_t( | |||
|                 std::chrono::system_clock::time_point(*this)); | |||
|     } | |||
| 
 | |||
|     offset_datetime() = default; | |||
|     ~offset_datetime() = default; | |||
|     offset_datetime(offset_datetime const&) = default; | |||
|     offset_datetime(offset_datetime&&)      = default; | |||
|     offset_datetime& operator=(offset_datetime const&) = default; | |||
|     offset_datetime& operator=(offset_datetime&&)      = default; | |||
| 
 | |||
|   private: | |||
| 
 | |||
|     static time_offset get_local_offset(const std::time_t* tp) | |||
|     { | |||
|         // get local timezone with the same date-time information as mktime
 | |||
|         const auto t = detail::localtime_s(tp); | |||
| 
 | |||
|         std::array<char, 6> buf; | |||
|         const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0
 | |||
|         if(result != 5) | |||
|         { | |||
|             throw std::runtime_error("toml::offset_datetime: cannot obtain " | |||
|                                      "timezone information of current env"); | |||
|         } | |||
|         const int ofs = std::atoi(buf.data()); | |||
|         const int ofs_h = ofs / 100; | |||
|         const int ofs_m = ofs - (ofs_h * 100); | |||
|         return time_offset(ofs_h, ofs_m); | |||
|     } | |||
| }; | |||
| 
 | |||
| inline bool operator==(const offset_datetime& lhs, const offset_datetime& rhs) | |||
| { | |||
|     return std::make_tuple(lhs.date, lhs.time, lhs.offset) == | |||
|            std::make_tuple(rhs.date, rhs.time, rhs.offset); | |||
| } | |||
| inline bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) | |||
| { | |||
|     return !(lhs == rhs); | |||
| } | |||
| inline bool operator< (const offset_datetime& lhs, const offset_datetime& rhs) | |||
| { | |||
|     return std::make_tuple(lhs.date, lhs.time, lhs.offset) < | |||
|            std::make_tuple(rhs.date, rhs.time, rhs.offset); | |||
| } | |||
| inline bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) | |||
| { | |||
|     return (lhs < rhs) || (lhs == rhs); | |||
| } | |||
| inline bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) | |||
| { | |||
|     return !(lhs <= rhs); | |||
| } | |||
| inline bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) | |||
| { | |||
|     return !(lhs < rhs); | |||
| } | |||
| 
 | |||
| template<typename charT, typename traits> | |||
| std::basic_ostream<charT, traits>& | |||
| operator<<(std::basic_ostream<charT, traits>& os, const offset_datetime& dt) | |||
| { | |||
|     os << dt.date << 'T' << dt.time << dt.offset; | |||
|     return os; | |||
| } | |||
| 
 | |||
| }//toml
 | |||
| #endif// TOML11_DATETIME
 | |||
| @ -0,0 +1,83 @@ | |||
| //     Copyright Toru Niina 2017.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_EXCEPTION_HPP
 | |||
| #define TOML11_EXCEPTION_HPP
 | |||
| 
 | |||
| #include <array>
 | |||
| #include <string>
 | |||
| #include <stdexcept>
 | |||
| 
 | |||
| #include <cstring>
 | |||
| 
 | |||
| #include "source_location.hpp"
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| 
 | |||
| struct file_io_error : public std::runtime_error | |||
| { | |||
|   public: | |||
|     file_io_error(int errnum, const std::string& msg, const std::string& fname) | |||
|         : std::runtime_error(msg + " \"" + fname + "\": errno = " + std::to_string(errnum)), | |||
|           errno_(errnum) | |||
|     {} | |||
| 
 | |||
|     int get_errno() const noexcept {return errno_;} | |||
| 
 | |||
|   private: | |||
|     int errno_; | |||
| }; | |||
| 
 | |||
| struct exception : public std::exception | |||
| { | |||
|   public: | |||
|     explicit exception(const source_location& loc): loc_(loc) {} | |||
|     virtual ~exception() noexcept override = default; | |||
|     virtual const char* what() const noexcept override {return "";} | |||
|     virtual source_location const& location() const noexcept {return loc_;} | |||
| 
 | |||
|   protected: | |||
|     source_location loc_; | |||
| }; | |||
| 
 | |||
| struct syntax_error : public toml::exception | |||
| { | |||
|   public: | |||
|     explicit syntax_error(const std::string& what_arg, const source_location& loc) | |||
|         : exception(loc), what_(what_arg) | |||
|     {} | |||
|     virtual ~syntax_error() noexcept override = default; | |||
|     virtual const char* what() const noexcept override {return what_.c_str();} | |||
| 
 | |||
|   protected: | |||
|     std::string what_; | |||
| }; | |||
| 
 | |||
| struct type_error : public toml::exception | |||
| { | |||
|   public: | |||
|     explicit type_error(const std::string& what_arg, const source_location& loc) | |||
|         : exception(loc), what_(what_arg) | |||
|     {} | |||
|     virtual ~type_error() noexcept override = default; | |||
|     virtual const char* what() const noexcept override {return what_.c_str();} | |||
| 
 | |||
|   protected: | |||
|     std::string what_; | |||
| }; | |||
| 
 | |||
| struct internal_error : public toml::exception | |||
| { | |||
|   public: | |||
|     explicit internal_error(const std::string& what_arg, const source_location& loc) | |||
|         : exception(loc), what_(what_arg) | |||
|     {} | |||
|     virtual ~internal_error() noexcept override = default; | |||
|     virtual const char* what() const noexcept override {return what_.c_str();} | |||
| 
 | |||
|   protected: | |||
|     std::string what_; | |||
| }; | |||
| 
 | |||
| } // toml
 | |||
| #endif // TOML_EXCEPTION
 | |||
| @ -0,0 +1,19 @@ | |||
| //     Copyright Toru Niina 2019.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_FROM_HPP
 | |||
| #define TOML11_FROM_HPP
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| 
 | |||
| template<typename T> | |||
| struct from; | |||
| // {
 | |||
| //     static T from_toml(const toml::value& v)
 | |||
| //     {
 | |||
| //         // User-defined conversions ...
 | |||
| //     }
 | |||
| // };
 | |||
| 
 | |||
| } // toml
 | |||
| #endif // TOML11_FROM_HPP
 | |||
						
							
						
						
							1154
	
						
						src/toml/get.hpp
						
							File diff suppressed because it is too large
							
							
								
									View File
								
							
						
					
				File diff suppressed because it is too large
							
							
								
									View File
								
							
						| @ -0,0 +1,19 @@ | |||
| //     Copyright Toru Niina 2019.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_INTO_HPP
 | |||
| #define TOML11_INTO_HPP
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| 
 | |||
| template<typename T> | |||
| struct into; | |||
| // {
 | |||
| //     static toml::value into_toml(const T& user_defined_type)
 | |||
| //     {
 | |||
| //         // User-defined conversions ...
 | |||
| //     }
 | |||
| // };
 | |||
| 
 | |||
| } // toml
 | |||
| #endif // TOML11_INTO_HPP
 | |||
| @ -0,0 +1,294 @@ | |||
| //     Copyright Toru Niina 2017.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_LEXER_HPP
 | |||
| #define TOML11_LEXER_HPP
 | |||
| #include <istream>
 | |||
| #include <sstream>
 | |||
| #include <stdexcept>
 | |||
| 
 | |||
| #include "combinator.hpp"
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| namespace detail | |||
| { | |||
| 
 | |||
| // these scans contents from current location in a container of char
 | |||
| // and extract a region that matches their own pattern.
 | |||
| // to see the implementation of each component, see combinator.hpp.
 | |||
| 
 | |||
| using lex_wschar  = either<character<' '>, character<'\t'>>; | |||
| using lex_ws      = repeat<lex_wschar, at_least<1>>; | |||
| using lex_newline = either<character<'\n'>, | |||
|                            sequence<character<'\r'>, character<'\n'>>>; | |||
| using lex_lower   = in_range<'a', 'z'>; | |||
| using lex_upper   = in_range<'A', 'Z'>; | |||
| using lex_alpha   = either<lex_lower, lex_upper>; | |||
| using lex_digit   = in_range<'0', '9'>; | |||
| using lex_nonzero = in_range<'1', '9'>; | |||
| using lex_oct_dig = in_range<'0', '7'>; | |||
| using lex_bin_dig = in_range<'0', '1'>; | |||
| using lex_hex_dig = either<lex_digit, in_range<'A', 'F'>, in_range<'a', 'f'>>; | |||
| 
 | |||
| using lex_hex_prefix = sequence<character<'0'>, character<'x'>>; | |||
| using lex_oct_prefix = sequence<character<'0'>, character<'o'>>; | |||
| using lex_bin_prefix = sequence<character<'0'>, character<'b'>>; | |||
| using lex_underscore = character<'_'>; | |||
| using lex_plus       = character<'+'>; | |||
| using lex_minus      = character<'-'>; | |||
| using lex_sign       = either<lex_plus, lex_minus>; | |||
| 
 | |||
| // digit | nonzero 1*(digit | _ digit)
 | |||
| using lex_unsigned_dec_int = either<sequence<lex_nonzero, repeat< | |||
|     either<lex_digit, sequence<lex_underscore, lex_digit>>, at_least<1>>>, | |||
|     lex_digit>; | |||
| // (+|-)? unsigned_dec_int
 | |||
| using lex_dec_int = sequence<maybe<lex_sign>, lex_unsigned_dec_int>; | |||
| 
 | |||
| // hex_prefix hex_dig *(hex_dig | _ hex_dig)
 | |||
| using lex_hex_int = sequence<lex_hex_prefix, sequence<lex_hex_dig, repeat< | |||
|     either<lex_hex_dig, sequence<lex_underscore, lex_hex_dig>>, unlimited>>>; | |||
| // oct_prefix oct_dig *(oct_dig | _ oct_dig)
 | |||
| using lex_oct_int = sequence<lex_oct_prefix, sequence<lex_oct_dig, repeat< | |||
|     either<lex_oct_dig, sequence<lex_underscore, lex_oct_dig>>, unlimited>>>; | |||
| // bin_prefix bin_dig *(bin_dig | _ bin_dig)
 | |||
| using lex_bin_int = sequence<lex_bin_prefix, sequence<lex_bin_dig, repeat< | |||
|     either<lex_bin_dig, sequence<lex_underscore, lex_bin_dig>>, unlimited>>>; | |||
| 
 | |||
| // (dec_int | hex_int | oct_int | bin_int)
 | |||
| using lex_integer = either<lex_bin_int, lex_oct_int, lex_hex_int, lex_dec_int>; | |||
| 
 | |||
| // ===========================================================================
 | |||
| 
 | |||
| using lex_inf = sequence<character<'i'>, character<'n'>, character<'f'>>; | |||
| using lex_nan = sequence<character<'n'>, character<'a'>, character<'n'>>; | |||
| using lex_special_float = sequence<maybe<lex_sign>, either<lex_inf, lex_nan>>; | |||
| 
 | |||
| using lex_zero_prefixable_int = sequence<lex_digit, repeat<either<lex_digit, | |||
|     sequence<lex_underscore, lex_digit>>, unlimited>>; | |||
| 
 | |||
| using lex_fractional_part = sequence<character<'.'>, lex_zero_prefixable_int>; | |||
| 
 | |||
| using lex_exponent_part   = sequence<either<character<'e'>, character<'E'>>, | |||
|         maybe<lex_sign>, lex_zero_prefixable_int>; | |||
| 
 | |||
| using lex_float = either<lex_special_float, | |||
|       sequence<lex_dec_int, either<lex_exponent_part, | |||
|       sequence<lex_fractional_part, maybe<lex_exponent_part>>>>>; | |||
| 
 | |||
| // ===========================================================================
 | |||
| 
 | |||
| using lex_true = sequence<character<'t'>, character<'r'>, | |||
|                           character<'u'>, character<'e'>>; | |||
| using lex_false = sequence<character<'f'>, character<'a'>, character<'l'>, | |||
|                            character<'s'>, character<'e'>>; | |||
| using lex_boolean = either<lex_true, lex_false>; | |||
| 
 | |||
| // ===========================================================================
 | |||
| 
 | |||
| using lex_date_fullyear = repeat<lex_digit, exactly<4>>; | |||
| using lex_date_month    = repeat<lex_digit, exactly<2>>; | |||
| using lex_date_mday     = repeat<lex_digit, exactly<2>>; | |||
| using lex_time_delim    = either<character<'T'>, character<'t'>, character<' '>>; | |||
| using lex_time_hour     = repeat<lex_digit, exactly<2>>; | |||
| using lex_time_minute   = repeat<lex_digit, exactly<2>>; | |||
| using lex_time_second   = repeat<lex_digit, exactly<2>>; | |||
| using lex_time_secfrac  = sequence<character<'.'>, | |||
|                                    repeat<lex_digit, at_least<1>>>; | |||
| 
 | |||
| using lex_time_numoffset = sequence<either<character<'+'>, character<'-'>>, | |||
|                                     sequence<lex_time_hour, character<':'>, | |||
|                                              lex_time_minute>>; | |||
| using lex_time_offset = either<character<'Z'>, character<'z'>, | |||
|                                lex_time_numoffset>; | |||
| 
 | |||
| using lex_partial_time = sequence<lex_time_hour,   character<':'>, | |||
|                                   lex_time_minute, character<':'>, | |||
|                                   lex_time_second, maybe<lex_time_secfrac>>; | |||
| using lex_full_date    = sequence<lex_date_fullyear, character<'-'>, | |||
|                                   lex_date_month,    character<'-'>, | |||
|                                   lex_date_mday>; | |||
| using lex_full_time    = sequence<lex_partial_time, lex_time_offset>; | |||
| 
 | |||
| using lex_offset_date_time = sequence<lex_full_date, lex_time_delim, lex_full_time>; | |||
| using lex_local_date_time  = sequence<lex_full_date, lex_time_delim, lex_partial_time>; | |||
| using lex_local_date       = lex_full_date; | |||
| using lex_local_time       = lex_partial_time; | |||
| 
 | |||
| // ===========================================================================
 | |||
| 
 | |||
| using lex_quotation_mark  = character<'"'>; | |||
| using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab) is allowed
 | |||
|                                            in_range<0x0A, 0x1F>, | |||
|                                            character<0x22>, character<0x5C>, | |||
|                                            character<0x7F>>>; | |||
| 
 | |||
| using lex_escape          = character<'\\'>; | |||
| using lex_escape_unicode_short = sequence<character<'u'>, | |||
|                                           repeat<lex_hex_dig, exactly<4>>>; | |||
| using lex_escape_unicode_long  = sequence<character<'U'>, | |||
|                                           repeat<lex_hex_dig, exactly<8>>>; | |||
| using lex_escape_seq_char = either<character<'"'>, character<'\\'>, | |||
|                                    character<'b'>, character<'f'>, | |||
|                                    character<'n'>, character<'r'>, | |||
|                                    character<'t'>, | |||
| #ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
 | |||
|                                    character<'e'>, // ESC (0x1B)
 | |||
| #endif
 | |||
|                                    lex_escape_unicode_short, | |||
|                                    lex_escape_unicode_long | |||
|                                    >; | |||
| using lex_escaped      = sequence<lex_escape, lex_escape_seq_char>; | |||
| using lex_basic_char   = either<lex_basic_unescaped, lex_escaped>; | |||
| using lex_basic_string = sequence<lex_quotation_mark, | |||
|                                   repeat<lex_basic_char, unlimited>, | |||
|                                   lex_quotation_mark>; | |||
| 
 | |||
| // After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings
 | |||
| // are allowed to be used.
 | |||
| // After this, the following strings are *explicitly* allowed.
 | |||
| // - One or two `"`s in a multi-line basic string is allowed wherever it is.
 | |||
| // - Three consecutive `"`s in a multi-line basic string is considered as a delimiter.
 | |||
| // - One or two `"`s can appear just before or after the delimiter.
 | |||
| // ```toml
 | |||
| // str4 = """Here are two quotation marks: "". Simple enough."""
 | |||
| // str5 = """Here are three quotation marks: ""\"."""
 | |||
| // str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
 | |||
| // str7 = """"This," she said, "is just a pointless statement.""""
 | |||
| // ```
 | |||
| // In the current implementation (v3.3.0), it is difficult to parse `str7` in
 | |||
| // the above example. It is difficult to recognize `"` at the end of string body
 | |||
| // collectly. It will be misunderstood as a `"""` delimiter and an additional,
 | |||
| // invalid `"`. Like this:
 | |||
| // ```console
 | |||
| //   what():  [error] toml::parse_table: invalid line format
 | |||
| //  --> hoge.toml
 | |||
| //     |
 | |||
| //  13 | str7 = """"This," she said, "is just a pointless statement.""""
 | |||
| //     |                                                               ^- expected newline, but got '"'.
 | |||
| // ```
 | |||
| // As a quick workaround for this problem, `lex_ml_basic_string_delim` was
 | |||
| // split into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
 | |||
| // `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s.
 | |||
| // In parse_ml_basic_string() function, the trailing `"`s will be attached to
 | |||
| // the string body.
 | |||
| //
 | |||
| using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>; | |||
| using lex_ml_basic_string_open  = lex_ml_basic_string_delim; | |||
| using lex_ml_basic_string_close = sequence< | |||
|         repeat<lex_quotation_mark, exactly<3>>, | |||
|         maybe<lex_quotation_mark>, maybe<lex_quotation_mark> | |||
|     >; | |||
| 
 | |||
| using lex_ml_basic_unescaped    = exclude<either<in_range<0x00, 0x08>, // 0x09 is tab
 | |||
|                                                  in_range<0x0A, 0x1F>, | |||
|                                                  character<0x5C>, // backslash
 | |||
|                                                  character<0x7F>, // DEL
 | |||
|                                                  lex_ml_basic_string_delim>>; | |||
| 
 | |||
| using lex_ml_basic_escaped_newline = sequence< | |||
|         lex_escape, maybe<lex_ws>, lex_newline, | |||
|         repeat<either<lex_ws, lex_newline>, unlimited>>; | |||
| 
 | |||
| using lex_ml_basic_char = either<lex_ml_basic_unescaped, lex_escaped>; | |||
| using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline, | |||
|                                         lex_ml_basic_escaped_newline>, | |||
|                                  unlimited>; | |||
| using lex_ml_basic_string = sequence<lex_ml_basic_string_open, | |||
|                                      lex_ml_basic_body, | |||
|                                      lex_ml_basic_string_close>; | |||
| 
 | |||
| using lex_literal_char = exclude<either<in_range<0x00, 0x08>, in_range<0x0A, 0x1F>, | |||
|                                         character<0x7F>, character<0x27>>>; | |||
| using lex_apostrophe = character<'\''>; | |||
| using lex_literal_string = sequence<lex_apostrophe, | |||
|                                     repeat<lex_literal_char, unlimited>, | |||
|                                     lex_apostrophe>; | |||
| 
 | |||
| // the same reason as above.
 | |||
| using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>; | |||
| using lex_ml_literal_string_open  = lex_ml_literal_string_delim; | |||
| using lex_ml_literal_string_close = sequence< | |||
|         repeat<lex_apostrophe, exactly<3>>, | |||
|         maybe<lex_apostrophe>, maybe<lex_apostrophe> | |||
|     >; | |||
| 
 | |||
| using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>, | |||
|                                            in_range<0x0A, 0x1F>, | |||
|                                            character<0x7F>, | |||
|                                            lex_ml_literal_string_delim>>; | |||
| using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>, | |||
|                                    unlimited>; | |||
| using lex_ml_literal_string = sequence<lex_ml_literal_string_open, | |||
|                                        lex_ml_literal_body, | |||
|                                        lex_ml_literal_string_close>; | |||
| 
 | |||
| using lex_string = either<lex_ml_basic_string,   lex_basic_string, | |||
|                           lex_ml_literal_string, lex_literal_string>; | |||
| 
 | |||
| // ===========================================================================
 | |||
| using lex_dot_sep = sequence<maybe<lex_ws>, character<'.'>, maybe<lex_ws>>; | |||
| 
 | |||
| using lex_unquoted_key = repeat<either<lex_alpha, lex_digit, | |||
|                                        character<'-'>, character<'_'>>, | |||
|                                 at_least<1>>; | |||
| using lex_quoted_key = either<lex_basic_string, lex_literal_string>; | |||
| using lex_simple_key = either<lex_unquoted_key, lex_quoted_key>; | |||
| using lex_dotted_key = sequence<lex_simple_key, | |||
|                                 repeat<sequence<lex_dot_sep, lex_simple_key>, | |||
|                                        at_least<1> | |||
|                                        > | |||
|                                 >; | |||
| using lex_key = either<lex_dotted_key, lex_simple_key>; | |||
| 
 | |||
| using lex_keyval_sep = sequence<maybe<lex_ws>, | |||
|                                 character<'='>, | |||
|                                 maybe<lex_ws>>; | |||
| 
 | |||
| using lex_std_table_open  = character<'['>; | |||
| using lex_std_table_close = character<']'>; | |||
| using lex_std_table       = sequence<lex_std_table_open, | |||
|                                      maybe<lex_ws>, | |||
|                                      lex_key, | |||
|                                      maybe<lex_ws>, | |||
|                                      lex_std_table_close>; | |||
| 
 | |||
| using lex_array_table_open  = sequence<lex_std_table_open,  lex_std_table_open>; | |||
| using lex_array_table_close = sequence<lex_std_table_close, lex_std_table_close>; | |||
| using lex_array_table       = sequence<lex_array_table_open, | |||
|                                        maybe<lex_ws>, | |||
|                                        lex_key, | |||
|                                        maybe<lex_ws>, | |||
|                                        lex_array_table_close>; | |||
| 
 | |||
| using lex_utf8_1byte = in_range<0x00, 0x7F>; | |||
| using lex_utf8_2byte = sequence< | |||
|         in_range<'\xC2', '\xDF'>, | |||
|         in_range<'\x80', '\xBF'> | |||
|     >; | |||
| using lex_utf8_3byte = sequence<either< | |||
|         sequence<character<'\xE0'>, in_range<'\xA0', '\xBF'>>, | |||
|         sequence<in_range<'\xE1', '\xEC'>, in_range<'\x80', '\xBF'>>, | |||
|         sequence<character<'\xED'>, in_range<'\x80', '\x9F'>>, | |||
|         sequence<in_range<'\xEE', '\xEF'>, in_range<'\x80', '\xBF'>> | |||
|     >, in_range<'\x80', '\xBF'>>; | |||
| using lex_utf8_4byte = sequence<either< | |||
|         sequence<character<'\xF0'>, in_range<'\x90', '\xBF'>>, | |||
|         sequence<in_range<'\xF1', '\xF3'>, in_range<'\x80', '\xBF'>>, | |||
|         sequence<character<'\xF4'>, in_range<'\x80', '\x8F'>> | |||
|     >, in_range<'\x80', '\xBF'>, in_range<'\x80', '\xBF'>>; | |||
| using lex_utf8_code = either< | |||
|         lex_utf8_1byte, | |||
|         lex_utf8_2byte, | |||
|         lex_utf8_3byte, | |||
|         lex_utf8_4byte | |||
|     >; | |||
| 
 | |||
| using lex_comment_start_symbol = character<'#'>; | |||
| using lex_non_eol_ascii = either<character<0x09>, in_range<0x20, 0x7E>>; | |||
| using lex_comment = sequence<lex_comment_start_symbol, repeat<either< | |||
|     lex_non_eol_ascii, lex_utf8_2byte, lex_utf8_3byte, lex_utf8_4byte>, unlimited>>; | |||
| 
 | |||
| } // detail
 | |||
| } // toml
 | |||
| #endif // TOML_LEXER_HPP
 | |||
| @ -0,0 +1,113 @@ | |||
| //     Copyright Toru Niina 2019.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_LITERAL_HPP
 | |||
| #define TOML11_LITERAL_HPP
 | |||
| #include "parser.hpp"
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| inline namespace literals | |||
| { | |||
| inline namespace toml_literals | |||
| { | |||
| 
 | |||
| // implementation
 | |||
| inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector> | |||
| literal_internal_impl(::toml::detail::location loc) | |||
| { | |||
|     using value_type = ::toml::basic_value< | |||
|         TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>; | |||
|     // if there are some comments or empty lines, skip them.
 | |||
|     using skip_line = ::toml::detail::repeat<toml::detail::sequence< | |||
|             ::toml::detail::maybe<::toml::detail::lex_ws>, | |||
|             ::toml::detail::maybe<::toml::detail::lex_comment>, | |||
|             ::toml::detail::lex_newline | |||
|         >, ::toml::detail::at_least<1>>; | |||
|     skip_line::invoke(loc); | |||
| 
 | |||
|     // if there are some whitespaces before a value, skip them.
 | |||
|     using skip_ws = ::toml::detail::repeat< | |||
|         ::toml::detail::lex_ws, ::toml::detail::at_least<1>>; | |||
|     skip_ws::invoke(loc); | |||
| 
 | |||
|     // to distinguish arrays and tables, first check it is a table or not.
 | |||
|     //
 | |||
|     // "[1,2,3]"_toml;   // this is an array
 | |||
|     // "[table]"_toml;   // a table that has an empty table named "table" inside.
 | |||
|     // "[[1,2,3]]"_toml; // this is an array of arrays
 | |||
|     // "[[table]]"_toml; // this is a table that has an array of tables inside.
 | |||
|     //
 | |||
|     // "[[1]]"_toml;     // this can be both... (currently it becomes a table)
 | |||
|     // "1 = [{}]"_toml;  // this is a table that has an array of table named 1.
 | |||
|     // "[[1,]]"_toml;    // this is an array of arrays.
 | |||
|     // "[[1],]"_toml;    // this also.
 | |||
| 
 | |||
|     const auto the_front = loc.iter(); | |||
| 
 | |||
|     const bool is_table_key = ::toml::detail::lex_std_table::invoke(loc); | |||
|     loc.reset(the_front); | |||
| 
 | |||
|     const bool is_aots_key  = ::toml::detail::lex_array_table::invoke(loc); | |||
|     loc.reset(the_front); | |||
| 
 | |||
|     // If it is neither a table-key or a array-of-table-key, it may be a value.
 | |||
|     if(!is_table_key && !is_aots_key) | |||
|     { | |||
|         if(auto data = ::toml::detail::parse_value<value_type>(loc, 0)) | |||
|         { | |||
|             return data.unwrap(); | |||
|         } | |||
|     } | |||
| 
 | |||
|     // Note that still it can be a table, because the literal might be something
 | |||
|     // like the following.
 | |||
|     // ```cpp
 | |||
|     // R"( // c++11 raw string literals
 | |||
|     //   key = "value"
 | |||
|     //   int = 42
 | |||
|     // )"_toml;
 | |||
|     // ```
 | |||
|     // It is a valid toml file.
 | |||
|     // It should be parsed as if we parse a file with this content.
 | |||
| 
 | |||
|     if(auto data = ::toml::detail::parse_toml_file<value_type>(loc)) | |||
|     { | |||
|         return data.unwrap(); | |||
|     } | |||
|     else // none of them.
 | |||
|     { | |||
|         throw ::toml::syntax_error(data.unwrap_err(), source_location(loc)); | |||
|     } | |||
| 
 | |||
| } | |||
| 
 | |||
| inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector> | |||
| operator"" _toml(const char* str, std::size_t len) | |||
| { | |||
|     ::toml::detail::location loc( | |||
|             std::string("TOML literal encoded in a C++ code"), | |||
|             std::vector<char>(str, str + len)); | |||
|     // literal length does not include the null character at the end.
 | |||
|     return literal_internal_impl(std::move(loc)); | |||
| } | |||
| 
 | |||
| // value of __cplusplus in C++2a/20 mode is not fixed yet along compilers.
 | |||
| // So here we use the feature test macro for `char8_t` itself.
 | |||
| #if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
 | |||
| // value of u8"" literal has been changed from char to char8_t and char8_t is
 | |||
| // NOT compatible to char
 | |||
| inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector> | |||
| operator"" _toml(const char8_t* str, std::size_t len) | |||
| { | |||
|     ::toml::detail::location loc( | |||
|             std::string("TOML literal encoded in a C++ code"), | |||
|             std::vector<char>(reinterpret_cast<const char*>(str), | |||
|                               reinterpret_cast<const char*>(str) + len)); | |||
|     return literal_internal_impl(std::move(loc)); | |||
| } | |||
| #endif
 | |||
| 
 | |||
| } // toml_literals
 | |||
| } // literals
 | |||
| } // toml
 | |||
| #endif//TOML11_LITERAL_HPP
 | |||
| @ -0,0 +1,121 @@ | |||
| #ifndef TOML11_MACROS_HPP
 | |||
| #define TOML11_MACROS_HPP
 | |||
| 
 | |||
| #define TOML11_STRINGIZE_AUX(x) #x
 | |||
| #define TOML11_STRINGIZE(x)     TOML11_STRINGIZE_AUX(x)
 | |||
| 
 | |||
| #define TOML11_CONCATENATE_AUX(x, y) x##y
 | |||
| #define TOML11_CONCATENATE(x, y)     TOML11_CONCATENATE_AUX(x, y)
 | |||
| 
 | |||
| // ============================================================================
 | |||
| // TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
 | |||
| 
 | |||
| #ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
 | |||
| 
 | |||
| // ----------------------------------------------------------------------------
 | |||
| // TOML11_ARGS_SIZE
 | |||
| 
 | |||
| #define TOML11_INDEX_RSEQ() \
 | |||
|     32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ | |||
|     16, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1, 0 | |||
| #define TOML11_ARGS_SIZE_IMPL(\
 | |||
|     ARG1,  ARG2,  ARG3,  ARG4,  ARG5,  ARG6,  ARG7,  ARG8,  ARG9,  ARG10, \ | |||
|     ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \ | |||
|     ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \ | |||
|     ARG31, ARG32, N, ...) N | |||
| #define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__)
 | |||
| #define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ())
 | |||
| 
 | |||
| // ----------------------------------------------------------------------------
 | |||
| // TOML11_FOR_EACH_VA_ARGS
 | |||
| 
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1     ) FUNCTOR(ARG1)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__)
 | |||
| #define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__)
 | |||
| 
 | |||
| #define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\
 | |||
|     TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__) | |||
| 
 | |||
| // ----------------------------------------------------------------------------
 | |||
| // TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
 | |||
| 
 | |||
| // use it in the following way.
 | |||
| // ```cpp
 | |||
| // namespace foo
 | |||
| // {
 | |||
| // struct Foo
 | |||
| // {
 | |||
| //     std::string s;
 | |||
| //     double      d;
 | |||
| //     int         i;
 | |||
| // };
 | |||
| // } // foo
 | |||
| //
 | |||
| // TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i)
 | |||
| // ```
 | |||
| // And then you can use `toml::find<foo::Foo>(file, "foo");`
 | |||
| //
 | |||
| #define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\
 | |||
|     obj.VAR_NAME = toml::find<decltype(obj.VAR_NAME)>(v, TOML11_STRINGIZE(VAR_NAME)); | |||
| 
 | |||
| #define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\
 | |||
|     v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME; | |||
| 
 | |||
| #define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\
 | |||
|     namespace toml {                                                                     \ | |||
|     template<>                                                                           \ | |||
|     struct from<NAME>                                                                    \ | |||
|     {                                                                                    \ | |||
|         template<typename C, template<typename ...> class T,                             \ | |||
|                  template<typename ...> class A>                                         \ | |||
|         static NAME from_toml(const basic_value<C, T, A>& v)                             \ | |||
|         {                                                                                \ | |||
|             NAME obj;                                                                    \ | |||
|             TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ | |||
|             return obj;                                                                  \ | |||
|         }                                                                                \ | |||
|     };                                                                                   \ | |||
|     template<>                                                                           \ | |||
|     struct into<NAME>                                                                    \ | |||
|     {                                                                                    \ | |||
|         static value into_toml(const NAME& obj)                                          \ | |||
|         {                                                                                \ | |||
|             ::toml::value v = ::toml::table{};                                           \ | |||
|             TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ | |||
|             return v;                                                                    \ | |||
|         }                                                                                \ | |||
|     };                                                                                   \ | |||
|     } /* toml */ | |||
| 
 | |||
| #endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
 | |||
| 
 | |||
| #endif// TOML11_MACROS_HPP
 | |||
						
							
						
						
							2611
	
						
						src/toml/parser.hpp
						
							File diff suppressed because it is too large
							
							
								
									View File
								
							
						
					
				File diff suppressed because it is too large
							
							
								
									View File
								
							
						| @ -0,0 +1,416 @@ | |||
| //     Copyright Toru Niina 2017.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_REGION_HPP
 | |||
| #define TOML11_REGION_HPP
 | |||
| #include <memory>
 | |||
| #include <vector>
 | |||
| #include <algorithm>
 | |||
| #include <initializer_list>
 | |||
| #include <iterator>
 | |||
| #include <iomanip>
 | |||
| #include <cassert>
 | |||
| #include "color.hpp"
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| namespace detail | |||
| { | |||
| 
 | |||
| // helper function to avoid std::string(0, 'c') or std::string(iter, iter)
 | |||
| template<typename Iterator> | |||
| std::string make_string(Iterator first, Iterator last) | |||
| { | |||
|     if(first == last) {return "";} | |||
|     return std::string(first, last); | |||
| } | |||
| inline std::string make_string(std::size_t len, char c) | |||
| { | |||
|     if(len == 0) {return "";} | |||
|     return std::string(len, c); | |||
| } | |||
| 
 | |||
| // region_base is a base class of location and region that are defined below.
 | |||
| // it will be used to generate better error messages.
 | |||
| struct region_base | |||
| { | |||
|     region_base() = default; | |||
|     virtual ~region_base() = default; | |||
|     region_base(const region_base&) = default; | |||
|     region_base(region_base&&     ) = default; | |||
|     region_base& operator=(const region_base&) = default; | |||
|     region_base& operator=(region_base&&     ) = default; | |||
| 
 | |||
|     virtual bool is_ok()           const noexcept {return false;} | |||
|     virtual char front()           const noexcept {return '\0';} | |||
| 
 | |||
|     virtual std::string str()      const {return std::string("unknown region");} | |||
|     virtual std::string name()     const {return std::string("unknown file");} | |||
|     virtual std::string line()     const {return std::string("unknown line");} | |||
|     virtual std::string line_num() const {return std::string("?");} | |||
| 
 | |||
|     // length of the region
 | |||
|     virtual std::size_t size()     const noexcept {return 0;} | |||
|     // number of characters in the line before the region
 | |||
|     virtual std::size_t before()   const noexcept {return 0;} | |||
|     // number of characters in the line after the region
 | |||
|     virtual std::size_t after()    const noexcept {return 0;} | |||
| 
 | |||
|     virtual std::vector<std::string> comments() const {return {};} | |||
|     // ```toml
 | |||
|     // # comment_before
 | |||
|     // key = "value" # comment_inline
 | |||
|     // ```
 | |||
| }; | |||
| 
 | |||
| // location represents a position in a container, which contains a file content.
 | |||
| // it can be considered as a region that contains only one character.
 | |||
| //
 | |||
| // it contains pointer to the file content and iterator that points the current
 | |||
| // location.
 | |||
| struct location final : public region_base | |||
| { | |||
|     using const_iterator  = typename std::vector<char>::const_iterator; | |||
|     using difference_type = typename std::iterator_traits<const_iterator>::difference_type; | |||
|     using source_ptr      = std::shared_ptr<const std::vector<char>>; | |||
| 
 | |||
|     location(std::string source_name, std::vector<char> cont) | |||
|       : source_(std::make_shared<std::vector<char>>(std::move(cont))), | |||
|         line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) | |||
|     {} | |||
|     location(std::string source_name, const std::string& cont) | |||
|       : source_(std::make_shared<std::vector<char>>(cont.begin(), cont.end())), | |||
|         line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) | |||
|     {} | |||
| 
 | |||
|     location(const location&) = default; | |||
|     location(location&&)      = default; | |||
|     location& operator=(const location&) = default; | |||
|     location& operator=(location&&)      = default; | |||
|     ~location() = default; | |||
| 
 | |||
|     bool is_ok() const noexcept override {return static_cast<bool>(source_);} | |||
|     char front() const noexcept override {return *iter_;} | |||
| 
 | |||
|     // this const prohibits codes like `++(loc.iter())`.
 | |||
|     std::add_const<const_iterator>::type iter()  const noexcept {return iter_;} | |||
| 
 | |||
|     const_iterator begin() const noexcept {return source_->cbegin();} | |||
|     const_iterator end()   const noexcept {return source_->cend();} | |||
| 
 | |||
|     // XXX `location::line_num()` used to be implemented using `std::count` to
 | |||
|     // count a number of '\n'. But with a long toml file (typically, 10k lines),
 | |||
|     // it becomes intolerably slow because each time it generates error messages,
 | |||
|     // it counts '\n' from thousands of characters. To workaround it, I decided
 | |||
|     // to introduce `location::line_number_` member variable and synchronize it
 | |||
|     // to the location changes the point to look. So an overload of `iter()`
 | |||
|     // which returns mutable reference is removed and `advance()`, `retrace()`
 | |||
|     // and `reset()` is added.
 | |||
|     void advance(difference_type n = 1) noexcept | |||
|     { | |||
|         this->line_number_ += static_cast<std::size_t>( | |||
|                 std::count(this->iter_, std::next(this->iter_, n), '\n')); | |||
|         this->iter_ += n; | |||
|         return; | |||
|     } | |||
|     void retrace(difference_type n = 1) noexcept | |||
|     { | |||
|         this->line_number_ -= static_cast<std::size_t>( | |||
|                 std::count(std::prev(this->iter_, n), this->iter_, '\n')); | |||
|         this->iter_ -= n; | |||
|         return; | |||
|     } | |||
|     void reset(const_iterator rollback) noexcept | |||
|     { | |||
|         // since c++11, std::distance works in both ways for random-access
 | |||
|         // iterators and returns a negative value if `first > last`.
 | |||
|         if(0 <= std::distance(rollback, this->iter_)) // rollback < iter
 | |||
|         { | |||
|             this->line_number_ -= static_cast<std::size_t>( | |||
|                     std::count(rollback, this->iter_, '\n')); | |||
|         } | |||
|         else // iter < rollback [[unlikely]]
 | |||
|         { | |||
|             this->line_number_ += static_cast<std::size_t>( | |||
|                     std::count(this->iter_, rollback, '\n')); | |||
|         } | |||
|         this->iter_ = rollback; | |||
|         return; | |||
|     } | |||
| 
 | |||
|     std::string str()  const override {return make_string(1, *this->iter());} | |||
|     std::string name() const override {return source_name_;} | |||
| 
 | |||
|     std::string line_num() const override | |||
|     { | |||
|         return std::to_string(this->line_number_); | |||
|     } | |||
| 
 | |||
|     std::string line() const override | |||
|     { | |||
|         return make_string(this->line_begin(), this->line_end()); | |||
|     } | |||
| 
 | |||
|     const_iterator line_begin() const noexcept | |||
|     { | |||
|         using reverse_iterator = std::reverse_iterator<const_iterator>; | |||
|         return std::find(reverse_iterator(this->iter()), | |||
|                          reverse_iterator(this->begin()), '\n').base(); | |||
|     } | |||
|     const_iterator line_end() const noexcept | |||
|     { | |||
|         return std::find(this->iter(), this->end(), '\n'); | |||
|     } | |||
| 
 | |||
|     // location is always points a character. so the size is 1.
 | |||
|     std::size_t size() const noexcept override | |||
|     { | |||
|         return 1u; | |||
|     } | |||
|     std::size_t before() const noexcept override | |||
|     { | |||
|         const auto sz = std::distance(this->line_begin(), this->iter()); | |||
|         assert(sz >= 0); | |||
|         return static_cast<std::size_t>(sz); | |||
|     } | |||
|     std::size_t after() const noexcept override | |||
|     { | |||
|         const auto sz = std::distance(this->iter(), this->line_end()); | |||
|         assert(sz >= 0); | |||
|         return static_cast<std::size_t>(sz); | |||
|     } | |||
| 
 | |||
|     source_ptr const& source() const& noexcept {return source_;} | |||
|     source_ptr&&      source() &&     noexcept {return std::move(source_);} | |||
| 
 | |||
|   private: | |||
| 
 | |||
|     source_ptr     source_; | |||
|     std::size_t    line_number_; | |||
|     std::string    source_name_; | |||
|     const_iterator iter_; | |||
| }; | |||
| 
 | |||
| // region represents a range in a container, which contains a file content.
 | |||
| //
 | |||
| // it contains pointer to the file content and iterator that points the first
 | |||
| // and last location.
 | |||
| struct region final : public region_base | |||
| { | |||
|     using const_iterator = typename std::vector<char>::const_iterator; | |||
|     using source_ptr     = std::shared_ptr<const std::vector<char>>; | |||
| 
 | |||
|     // delete default constructor. source_ never be null.
 | |||
|     region() = delete; | |||
| 
 | |||
|     explicit region(const location& loc) | |||
|       : source_(loc.source()), source_name_(loc.name()), | |||
|         first_(loc.iter()), last_(loc.iter()) | |||
|     {} | |||
|     explicit region(location&& loc) | |||
|       : source_(loc.source()), source_name_(loc.name()), | |||
|         first_(loc.iter()), last_(loc.iter()) | |||
|     {} | |||
| 
 | |||
|     region(const location& loc, const_iterator f, const_iterator l) | |||
|       : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) | |||
|     {} | |||
|     region(location&& loc, const_iterator f, const_iterator l) | |||
|       : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) | |||
|     {} | |||
| 
 | |||
|     region(const region&) = default; | |||
|     region(region&&)      = default; | |||
|     region& operator=(const region&) = default; | |||
|     region& operator=(region&&)      = default; | |||
|     ~region() = default; | |||
| 
 | |||
|     region& operator+=(const region& other) | |||
|     { | |||
|         // different regions cannot be concatenated
 | |||
|         assert(this->source_ == other.source_ && this->last_ == other.first_); | |||
| 
 | |||
|         this->last_ = other.last_; | |||
|         return *this; | |||
|     } | |||
| 
 | |||
|     bool is_ok() const noexcept override {return static_cast<bool>(source_);} | |||
|     char front() const noexcept override {return *first_;} | |||
| 
 | |||
|     std::string str()  const override {return make_string(first_, last_);} | |||
|     std::string line() const override | |||
|     { | |||
|         if(this->contain_newline()) | |||
|         { | |||
|             return make_string(this->line_begin(), | |||
|                     std::find(this->line_begin(), this->last(), '\n')); | |||
|         } | |||
|         return make_string(this->line_begin(), this->line_end()); | |||
|     } | |||
|     std::string line_num() const override | |||
|     { | |||
|         return std::to_string(1 + std::count(this->begin(), this->first(), '\n')); | |||
|     } | |||
| 
 | |||
|     std::size_t size() const noexcept override | |||
|     { | |||
|         const auto sz = std::distance(first_, last_); | |||
|         assert(sz >= 0); | |||
|         return static_cast<std::size_t>(sz); | |||
|     } | |||
|     std::size_t before() const noexcept override | |||
|     { | |||
|         const auto sz = std::distance(this->line_begin(), this->first()); | |||
|         assert(sz >= 0); | |||
|         return static_cast<std::size_t>(sz); | |||
|     } | |||
|     std::size_t after() const noexcept override | |||
|     { | |||
|         const auto sz = std::distance(this->last(), this->line_end()); | |||
|         assert(sz >= 0); | |||
|         return static_cast<std::size_t>(sz); | |||
|     } | |||
| 
 | |||
|     bool contain_newline() const noexcept | |||
|     { | |||
|         return std::find(this->first(), this->last(), '\n') != this->last(); | |||
|     } | |||
| 
 | |||
|     const_iterator line_begin() const noexcept | |||
|     { | |||
|         using reverse_iterator = std::reverse_iterator<const_iterator>; | |||
|         return std::find(reverse_iterator(this->first()), | |||
|                          reverse_iterator(this->begin()), '\n').base(); | |||
|     } | |||
|     const_iterator line_end() const noexcept | |||
|     { | |||
|         return std::find(this->last(), this->end(), '\n'); | |||
|     } | |||
| 
 | |||
|     const_iterator begin() const noexcept {return source_->cbegin();} | |||
|     const_iterator end()   const noexcept {return source_->cend();} | |||
|     const_iterator first() const noexcept {return first_;} | |||
|     const_iterator last()  const noexcept {return last_;} | |||
| 
 | |||
|     source_ptr const& source() const& noexcept {return source_;} | |||
|     source_ptr&&      source() &&     noexcept {return std::move(source_);} | |||
| 
 | |||
|     std::string name() const override {return source_name_;} | |||
| 
 | |||
|     std::vector<std::string> comments() const override | |||
|     { | |||
|         // assuming the current region (`*this`) points a value.
 | |||
|         // ```toml
 | |||
|         // a = "value"
 | |||
|         //     ^^^^^^^- this region
 | |||
|         // ```
 | |||
|         using rev_iter = std::reverse_iterator<const_iterator>; | |||
| 
 | |||
|         std::vector<std::string> com{}; | |||
|         { | |||
|             // find comments just before the current region.
 | |||
|             // ```toml
 | |||
|             // # this should be collected.
 | |||
|             // # this also.
 | |||
|             // a = value # not this.
 | |||
|             // ```
 | |||
| 
 | |||
|             // # this is a comment for `a`, not array elements.
 | |||
|             // a = [1, 2, 3, 4, 5]
 | |||
|             if(this->first() == std::find_if(this->line_begin(), this->first(), | |||
|                 [](const char c) noexcept -> bool {return c == '[' || c == '{';})) | |||
|             { | |||
|                 auto iter = this->line_begin(); // points the first character
 | |||
|                 while(iter != this->begin()) | |||
|                 { | |||
|                     iter = std::prev(iter); | |||
| 
 | |||
|                     // range [line_start, iter) represents the previous line
 | |||
|                     const auto line_start   = std::find( | |||
|                             rev_iter(iter), rev_iter(this->begin()), '\n').base(); | |||
|                     const auto comment_found = std::find(line_start, iter, '#'); | |||
|                     if(comment_found == iter) | |||
|                     { | |||
|                         break; // comment not found.
 | |||
|                     } | |||
| 
 | |||
|                     // exclude the following case.
 | |||
|                     // > a = "foo" # comment // <-- this is not a comment for b but a.
 | |||
|                     // > b = "current value"
 | |||
|                     if(std::all_of(line_start, comment_found, | |||
|                             [](const char c) noexcept -> bool { | |||
|                                 return c == ' ' || c == '\t'; | |||
|                             })) | |||
|                     { | |||
|                         // unwrap the first '#' by std::next.
 | |||
|                         auto s = make_string(std::next(comment_found), iter); | |||
|                         if(!s.empty() && s.back() == '\r') {s.pop_back();} | |||
|                         com.push_back(std::move(s)); | |||
|                     } | |||
|                     else | |||
|                     { | |||
|                         break; | |||
|                     } | |||
|                     iter = line_start; | |||
|                 } | |||
|             } | |||
|         } | |||
| 
 | |||
|         if(com.size() > 1) | |||
|         { | |||
|             std::reverse(com.begin(), com.end()); | |||
|         } | |||
| 
 | |||
|         { | |||
|             // find comments just after the current region.
 | |||
|             // ```toml
 | |||
|             // # not this.
 | |||
|             // a = value # this one.
 | |||
|             // a = [ # not this (technically difficult)
 | |||
|             //
 | |||
|             // ] # and this.
 | |||
|             // ```
 | |||
|             // The reason why it's difficult is that it requires parsing in the
 | |||
|             // following case.
 | |||
|             // ```toml
 | |||
|             // a = [ 10 # this comment is for `10`. not for `a` but `a[0]`.
 | |||
|             // # ...
 | |||
|             // ] # this is apparently a comment for a.
 | |||
|             //
 | |||
|             // b = [
 | |||
|             // 3.14 ] # there is no way to add a comment to `3.14` currently.
 | |||
|             //
 | |||
|             // c = [
 | |||
|             //   3.14 # do this if you need a comment here.
 | |||
|             // ]
 | |||
|             // ```
 | |||
|             const auto comment_found = | |||
|                 std::find(this->last(), this->line_end(), '#'); | |||
|             if(comment_found != this->line_end()) // '#' found
 | |||
|             { | |||
|                 // table = {key = "value"} # what is this for?
 | |||
|                 // the above comment is not for "value", but {key="value"}.
 | |||
|                 if(comment_found == std::find_if(this->last(), comment_found, | |||
|                     [](const char c) noexcept -> bool { | |||
|                         return !(c == ' ' || c == '\t' || c == ','); | |||
|                     })) | |||
|                 { | |||
|                     // unwrap the first '#' by std::next.
 | |||
|                     auto s = make_string(std::next(comment_found), this->line_end()); | |||
|                     if(!s.empty() && s.back() == '\r') {s.pop_back();} | |||
|                     com.push_back(std::move(s)); | |||
|                 } | |||
|             } | |||
|         } | |||
|         return com; | |||
|     } | |||
| 
 | |||
|   private: | |||
| 
 | |||
|     source_ptr     source_; | |||
|     std::string    source_name_; | |||
|     const_iterator first_, last_; | |||
| }; | |||
| 
 | |||
| } // detail
 | |||
| } // toml
 | |||
| #endif// TOML11_REGION_H
 | |||
| @ -0,0 +1,717 @@ | |||
| //     Copyright Toru Niina 2017.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_RESULT_HPP
 | |||
| #define TOML11_RESULT_HPP
 | |||
| #include "traits.hpp"
 | |||
| #include <type_traits>
 | |||
| #include <stdexcept>
 | |||
| #include <utility>
 | |||
| #include <new>
 | |||
| #include <string>
 | |||
| #include <sstream>
 | |||
| #include <cassert>
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| 
 | |||
| template<typename T> | |||
| struct success | |||
| { | |||
|     using value_type = T; | |||
|     value_type value; | |||
| 
 | |||
|     explicit success(const value_type& v) | |||
|         noexcept(std::is_nothrow_copy_constructible<value_type>::value) | |||
|         : value(v) | |||
|     {} | |||
|     explicit success(value_type&& v) | |||
|         noexcept(std::is_nothrow_move_constructible<value_type>::value) | |||
|         : value(std::move(v)) | |||
|     {} | |||
| 
 | |||
|     template<typename U> | |||
|     explicit success(U&& v): value(std::forward<U>(v)) {} | |||
| 
 | |||
|     template<typename U> | |||
|     explicit success(const success<U>& v): value(v.value) {} | |||
|     template<typename U> | |||
|     explicit success(success<U>&& v): value(std::move(v.value)) {} | |||
| 
 | |||
|     ~success() = default; | |||
|     success(const success&) = default; | |||
|     success(success&&)      = default; | |||
|     success& operator=(const success&) = default; | |||
|     success& operator=(success&&)      = default; | |||
| }; | |||
| 
 | |||
| template<typename T> | |||
| struct failure | |||
| { | |||
|     using value_type = T; | |||
|     value_type value; | |||
| 
 | |||
|     explicit failure(const value_type& v) | |||
|         noexcept(std::is_nothrow_copy_constructible<value_type>::value) | |||
|         : value(v) | |||
|     {} | |||
|     explicit failure(value_type&& v) | |||
|         noexcept(std::is_nothrow_move_constructible<value_type>::value) | |||
|         : value(std::move(v)) | |||
|     {} | |||
| 
 | |||
|     template<typename U> | |||
|     explicit failure(U&& v): value(std::forward<U>(v)) {} | |||
| 
 | |||
|     template<typename U> | |||
|     explicit failure(const failure<U>& v): value(v.value) {} | |||
|     template<typename U> | |||
|     explicit failure(failure<U>&& v): value(std::move(v.value)) {} | |||
| 
 | |||
|     ~failure() = default; | |||
|     failure(const failure&) = default; | |||
|     failure(failure&&)      = default; | |||
|     failure& operator=(const failure&) = default; | |||
|     failure& operator=(failure&&)      = default; | |||
| }; | |||
| 
 | |||
| template<typename T> | |||
| success<typename std::remove_cv<typename std::remove_reference<T>::type>::type> | |||
| ok(T&& v) | |||
| { | |||
|     return success< | |||
|         typename std::remove_cv<typename std::remove_reference<T>::type>::type | |||
|         >(std::forward<T>(v)); | |||
| } | |||
| template<typename T> | |||
| failure<typename std::remove_cv<typename std::remove_reference<T>::type>::type> | |||
| err(T&& v) | |||
| { | |||
|     return failure< | |||
|         typename std::remove_cv<typename std::remove_reference<T>::type>::type | |||
|         >(std::forward<T>(v)); | |||
| } | |||
| 
 | |||
| inline success<std::string> ok(const char* literal) | |||
| { | |||
|     return success<std::string>(std::string(literal)); | |||
| } | |||
| inline failure<std::string> err(const char* literal) | |||
| { | |||
|     return failure<std::string>(std::string(literal)); | |||
| } | |||
| 
 | |||
| 
 | |||
| template<typename T, typename E> | |||
| struct result | |||
| { | |||
|     using value_type = T; | |||
|     using error_type = E; | |||
|     using success_type = success<value_type>; | |||
|     using failure_type = failure<error_type>; | |||
| 
 | |||
|     result(const success_type& s): is_ok_(true) | |||
|     { | |||
|         auto tmp = ::new(std::addressof(this->succ)) success_type(s); | |||
|         assert(tmp == std::addressof(this->succ)); | |||
|         (void)tmp; | |||
|     } | |||
|     result(const failure_type& f): is_ok_(false) | |||
|     { | |||
|         auto tmp = ::new(std::addressof(this->fail)) failure_type(f); | |||
|         assert(tmp == std::addressof(this->fail)); | |||
|         (void)tmp; | |||
|     } | |||
|     result(success_type&& s): is_ok_(true) | |||
|     { | |||
|         auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); | |||
|         assert(tmp == std::addressof(this->succ)); | |||
|         (void)tmp; | |||
|     } | |||
|     result(failure_type&& f): is_ok_(false) | |||
|     { | |||
|         auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); | |||
|         assert(tmp == std::addressof(this->fail)); | |||
|         (void)tmp; | |||
|     } | |||
| 
 | |||
|     template<typename U> | |||
|     result(const success<U>& s): is_ok_(true) | |||
|     { | |||
|         auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); | |||
|         assert(tmp == std::addressof(this->succ)); | |||
|         (void)tmp; | |||
|     } | |||
|     template<typename U> | |||
|     result(const failure<U>& f): is_ok_(false) | |||
|     { | |||
|         auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); | |||
|         assert(tmp == std::addressof(this->fail)); | |||
|         (void)tmp; | |||
|     } | |||
|     template<typename U> | |||
|     result(success<U>&& s): is_ok_(true) | |||
|     { | |||
|         auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); | |||
|         assert(tmp == std::addressof(this->succ)); | |||
|         (void)tmp; | |||
|     } | |||
|     template<typename U> | |||
|     result(failure<U>&& f): is_ok_(false) | |||
|     { | |||
|         auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); | |||
|         assert(tmp == std::addressof(this->fail)); | |||
|         (void)tmp; | |||
|     } | |||
| 
 | |||
|     result& operator=(const success_type& s) | |||
|     { | |||
|         this->cleanup(); | |||
|         this->is_ok_ = true; | |||
|         auto tmp = ::new(std::addressof(this->succ)) success_type(s); | |||
|         assert(tmp == std::addressof(this->succ)); | |||
|         (void)tmp; | |||
|         return *this; | |||
|     } | |||
|     result& operator=(const failure_type& f) | |||
|     { | |||
|         this->cleanup(); | |||
|         this->is_ok_ = false; | |||
|         auto tmp = ::new(std::addressof(this->fail)) failure_type(f); | |||
|         assert(tmp == std::addressof(this->fail)); | |||
|         (void)tmp; | |||
|         return *this; | |||
|     } | |||
|     result& operator=(success_type&& s) | |||
|     { | |||
|         this->cleanup(); | |||
|         this->is_ok_ = true; | |||
|         auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); | |||
|         assert(tmp == std::addressof(this->succ)); | |||
|         (void)tmp; | |||
|         return *this; | |||
|     } | |||
|     result& operator=(failure_type&& f) | |||
|     { | |||
|         this->cleanup(); | |||
|         this->is_ok_ = false; | |||
|         auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); | |||
|         assert(tmp == std::addressof(this->fail)); | |||
|         (void)tmp; | |||
|         return *this; | |||
|     } | |||
| 
 | |||
|     template<typename U> | |||
|     result& operator=(const success<U>& s) | |||
|     { | |||
|         this->cleanup(); | |||
|         this->is_ok_ = true; | |||
|         auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); | |||
|         assert(tmp == std::addressof(this->succ)); | |||
|         (void)tmp; | |||
|         return *this; | |||
|     } | |||
|     template<typename U> | |||
|     result& operator=(const failure<U>& f) | |||
|     { | |||
|         this->cleanup(); | |||
|         this->is_ok_ = false; | |||
|         auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); | |||
|         assert(tmp == std::addressof(this->fail)); | |||
|         (void)tmp; | |||
|         return *this; | |||
|     } | |||
|     template<typename U> | |||
|     result& operator=(success<U>&& s) | |||
|     { | |||
|         this->cleanup(); | |||
|         this->is_ok_ = true; | |||
|         auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); | |||
|         assert(tmp == std::addressof(this->succ)); | |||
|         (void)tmp; | |||
|         return *this; | |||
|     } | |||
|     template<typename U> | |||
|     result& operator=(failure<U>&& f) | |||
|     { | |||
|         this->cleanup(); | |||
|         this->is_ok_ = false; | |||
|         auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); | |||
|         assert(tmp == std::addressof(this->fail)); | |||
|         (void)tmp; | |||
|         return *this; | |||
|     } | |||
| 
 | |||
|     ~result() noexcept {this->cleanup();} | |||
| 
 | |||
|     result(const result& other): is_ok_(other.is_ok()) | |||
|     { | |||
|         if(other.is_ok()) | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); | |||
|             assert(tmp == std::addressof(this->succ)); | |||
|             (void)tmp; | |||
|         } | |||
|         else | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); | |||
|             assert(tmp == std::addressof(this->fail)); | |||
|             (void)tmp; | |||
|         } | |||
|     } | |||
|     result(result&& other): is_ok_(other.is_ok()) | |||
|     { | |||
|         if(other.is_ok()) | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); | |||
|             assert(tmp == std::addressof(this->succ)); | |||
|             (void)tmp; | |||
|         } | |||
|         else | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); | |||
|             assert(tmp == std::addressof(this->fail)); | |||
|             (void)tmp; | |||
|         } | |||
|     } | |||
| 
 | |||
|     template<typename U, typename F> | |||
|     result(const result<U, F>& other): is_ok_(other.is_ok()) | |||
|     { | |||
|         if(other.is_ok()) | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); | |||
|             assert(tmp == std::addressof(this->succ)); | |||
|             (void)tmp; | |||
|         } | |||
|         else | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); | |||
|             assert(tmp == std::addressof(this->fail)); | |||
|             (void)tmp; | |||
|         } | |||
|     } | |||
|     template<typename U, typename F> | |||
|     result(result<U, F>&& other): is_ok_(other.is_ok()) | |||
|     { | |||
|         if(other.is_ok()) | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); | |||
|             assert(tmp == std::addressof(this->succ)); | |||
|             (void)tmp; | |||
|         } | |||
|         else | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); | |||
|             assert(tmp == std::addressof(this->fail)); | |||
|             (void)tmp; | |||
|         } | |||
|     } | |||
| 
 | |||
|     result& operator=(const result& other) | |||
|     { | |||
|         this->cleanup(); | |||
|         if(other.is_ok()) | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); | |||
|             assert(tmp == std::addressof(this->succ)); | |||
|             (void)tmp; | |||
|         } | |||
|         else | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); | |||
|             assert(tmp == std::addressof(this->fail)); | |||
|             (void)tmp; | |||
|         } | |||
|         is_ok_ = other.is_ok(); | |||
|         return *this; | |||
|     } | |||
|     result& operator=(result&& other) | |||
|     { | |||
|         this->cleanup(); | |||
|         if(other.is_ok()) | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); | |||
|             assert(tmp == std::addressof(this->succ)); | |||
|             (void)tmp; | |||
|         } | |||
|         else | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); | |||
|             assert(tmp == std::addressof(this->fail)); | |||
|             (void)tmp; | |||
|         } | |||
|         is_ok_ = other.is_ok(); | |||
|         return *this; | |||
|     } | |||
| 
 | |||
|     template<typename U, typename F> | |||
|     result& operator=(const result<U, F>& other) | |||
|     { | |||
|         this->cleanup(); | |||
|         if(other.is_ok()) | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); | |||
|             assert(tmp == std::addressof(this->succ)); | |||
|             (void)tmp; | |||
|         } | |||
|         else | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); | |||
|             assert(tmp == std::addressof(this->fail)); | |||
|             (void)tmp; | |||
|         } | |||
|         is_ok_ = other.is_ok(); | |||
|         return *this; | |||
|     } | |||
|     template<typename U, typename F> | |||
|     result& operator=(result<U, F>&& other) | |||
|     { | |||
|         this->cleanup(); | |||
|         if(other.is_ok()) | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); | |||
|             assert(tmp == std::addressof(this->succ)); | |||
|             (void)tmp; | |||
|         } | |||
|         else | |||
|         { | |||
|             auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); | |||
|             assert(tmp == std::addressof(this->fail)); | |||
|             (void)tmp; | |||
|         } | |||
|         is_ok_ = other.is_ok(); | |||
|         return *this; | |||
|     } | |||
| 
 | |||
|     bool is_ok()  const noexcept {return is_ok_;} | |||
|     bool is_err() const noexcept {return !is_ok_;} | |||
| 
 | |||
|     operator bool() const noexcept {return is_ok_;} | |||
| 
 | |||
|     value_type&       unwrap() & | |||
|     { | |||
|         if(is_err()) | |||
|         { | |||
|             throw std::runtime_error("toml::result: bad unwrap: " + | |||
|                                      format_error(this->as_err())); | |||
|         } | |||
|         return this->succ.value; | |||
|     } | |||
|     value_type const& unwrap() const& | |||
|     { | |||
|         if(is_err()) | |||
|         { | |||
|             throw std::runtime_error("toml::result: bad unwrap: " + | |||
|                                      format_error(this->as_err())); | |||
|         } | |||
|         return this->succ.value; | |||
|     } | |||
|     value_type&&      unwrap() && | |||
|     { | |||
|         if(is_err()) | |||
|         { | |||
|             throw std::runtime_error("toml::result: bad unwrap: " + | |||
|                                      format_error(this->as_err())); | |||
|         } | |||
|         return std::move(this->succ.value); | |||
|     } | |||
| 
 | |||
|     value_type&       unwrap_or(value_type& opt) & | |||
|     { | |||
|         if(is_err()) {return opt;} | |||
|         return this->succ.value; | |||
|     } | |||
|     value_type const& unwrap_or(value_type const& opt) const& | |||
|     { | |||
|         if(is_err()) {return opt;} | |||
|         return this->succ.value; | |||
|     } | |||
|     value_type        unwrap_or(value_type opt) && | |||
|     { | |||
|         if(is_err()) {return opt;} | |||
|         return this->succ.value; | |||
|     } | |||
| 
 | |||
|     error_type&       unwrap_err() & | |||
|     { | |||
|         if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} | |||
|         return this->fail.value; | |||
|     } | |||
|     error_type const& unwrap_err() const& | |||
|     { | |||
|         if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} | |||
|         return this->fail.value; | |||
|     } | |||
|     error_type&&      unwrap_err() && | |||
|     { | |||
|         if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} | |||
|         return std::move(this->fail.value); | |||
|     } | |||
| 
 | |||
|     value_type&       as_ok() &      noexcept {return this->succ.value;} | |||
|     value_type const& as_ok() const& noexcept {return this->succ.value;} | |||
|     value_type&&      as_ok() &&     noexcept {return std::move(this->succ.value);} | |||
| 
 | |||
|     error_type&       as_err() &      noexcept {return this->fail.value;} | |||
|     error_type const& as_err() const& noexcept {return this->fail.value;} | |||
|     error_type&&      as_err() &&     noexcept {return std::move(this->fail.value);} | |||
| 
 | |||
| 
 | |||
|     // prerequisities
 | |||
|     // F: T -> U
 | |||
|     // retval: result<U, E>
 | |||
|     template<typename F> | |||
|     result<detail::return_type_of_t<F, value_type&>, error_type> | |||
|     map(F&& f) & | |||
|     { | |||
|         if(this->is_ok()){return ok(f(this->as_ok()));} | |||
|         return err(this->as_err()); | |||
|     } | |||
|     template<typename F> | |||
|     result<detail::return_type_of_t<F, value_type const&>, error_type> | |||
|     map(F&& f) const& | |||
|     { | |||
|         if(this->is_ok()){return ok(f(this->as_ok()));} | |||
|         return err(this->as_err()); | |||
|     } | |||
|     template<typename F> | |||
|     result<detail::return_type_of_t<F, value_type &&>, error_type> | |||
|     map(F&& f) && | |||
|     { | |||
|         if(this->is_ok()){return ok(f(std::move(this->as_ok())));} | |||
|         return err(std::move(this->as_err())); | |||
|     } | |||
| 
 | |||
|     // prerequisities
 | |||
|     // F: E -> F
 | |||
|     // retval: result<T, F>
 | |||
|     template<typename F> | |||
|     result<value_type, detail::return_type_of_t<F, error_type&>> | |||
|     map_err(F&& f) & | |||
|     { | |||
|         if(this->is_err()){return err(f(this->as_err()));} | |||
|         return ok(this->as_ok()); | |||
|     } | |||
|     template<typename F> | |||
|     result<value_type, detail::return_type_of_t<F, error_type const&>> | |||
|     map_err(F&& f) const& | |||
|     { | |||
|         if(this->is_err()){return err(f(this->as_err()));} | |||
|         return ok(this->as_ok()); | |||
|     } | |||
|     template<typename F> | |||
|     result<value_type, detail::return_type_of_t<F, error_type&&>> | |||
|     map_err(F&& f) && | |||
|     { | |||
|         if(this->is_err()){return err(f(std::move(this->as_err())));} | |||
|         return ok(std::move(this->as_ok())); | |||
|     } | |||
| 
 | |||
|     // prerequisities
 | |||
|     // F: T -> U
 | |||
|     // retval: U
 | |||
|     template<typename F, typename U> | |||
|     detail::return_type_of_t<F, value_type&> | |||
|     map_or_else(F&& f, U&& opt) & | |||
|     { | |||
|         if(this->is_err()){return std::forward<U>(opt);} | |||
|         return f(this->as_ok()); | |||
|     } | |||
|     template<typename F, typename U> | |||
|     detail::return_type_of_t<F, value_type const&> | |||
|     map_or_else(F&& f, U&& opt) const& | |||
|     { | |||
|         if(this->is_err()){return std::forward<U>(opt);} | |||
|         return f(this->as_ok()); | |||
|     } | |||
|     template<typename F, typename U> | |||
|     detail::return_type_of_t<F, value_type&&> | |||
|     map_or_else(F&& f, U&& opt) && | |||
|     { | |||
|         if(this->is_err()){return std::forward<U>(opt);} | |||
|         return f(std::move(this->as_ok())); | |||
|     } | |||
| 
 | |||
|     // prerequisities
 | |||
|     // F: E -> U
 | |||
|     // retval: U
 | |||
|     template<typename F, typename U> | |||
|     detail::return_type_of_t<F, error_type&> | |||
|     map_err_or_else(F&& f, U&& opt) & | |||
|     { | |||
|         if(this->is_ok()){return std::forward<U>(opt);} | |||
|         return f(this->as_err()); | |||
|     } | |||
|     template<typename F, typename U> | |||
|     detail::return_type_of_t<F, error_type const&> | |||
|     map_err_or_else(F&& f, U&& opt) const& | |||
|     { | |||
|         if(this->is_ok()){return std::forward<U>(opt);} | |||
|         return f(this->as_err()); | |||
|     } | |||
|     template<typename F, typename U> | |||
|     detail::return_type_of_t<F, error_type&&> | |||
|     map_err_or_else(F&& f, U&& opt) && | |||
|     { | |||
|         if(this->is_ok()){return std::forward<U>(opt);} | |||
|         return f(std::move(this->as_err())); | |||
|     } | |||
| 
 | |||
|     // prerequisities:
 | |||
|     // F: func T -> U
 | |||
|     // toml::err(error_type) should be convertible to U.
 | |||
|     // normally, type U is another result<S, F> and E is convertible to F
 | |||
|     template<typename F> | |||
|     detail::return_type_of_t<F, value_type&> | |||
|     and_then(F&& f) & | |||
|     { | |||
|         if(this->is_ok()){return f(this->as_ok());} | |||
|         return err(this->as_err()); | |||
|     } | |||
|     template<typename F> | |||
|     detail::return_type_of_t<F, value_type const&> | |||
|     and_then(F&& f) const& | |||
|     { | |||
|         if(this->is_ok()){return f(this->as_ok());} | |||
|         return err(this->as_err()); | |||
|     } | |||
|     template<typename F> | |||
|     detail::return_type_of_t<F, value_type&&> | |||
|     and_then(F&& f) && | |||
|     { | |||
|         if(this->is_ok()){return f(std::move(this->as_ok()));} | |||
|         return err(std::move(this->as_err())); | |||
|     } | |||
| 
 | |||
|     // prerequisities:
 | |||
|     // F: func E -> U
 | |||
|     // toml::ok(value_type) should be convertible to U.
 | |||
|     // normally, type U is another result<S, F> and T is convertible to S
 | |||
|     template<typename F> | |||
|     detail::return_type_of_t<F, error_type&> | |||
|     or_else(F&& f) & | |||
|     { | |||
|         if(this->is_err()){return f(this->as_err());} | |||
|         return ok(this->as_ok()); | |||
|     } | |||
|     template<typename F> | |||
|     detail::return_type_of_t<F, error_type const&> | |||
|     or_else(F&& f) const& | |||
|     { | |||
|         if(this->is_err()){return f(this->as_err());} | |||
|         return ok(this->as_ok()); | |||
|     } | |||
|     template<typename F> | |||
|     detail::return_type_of_t<F, error_type&&> | |||
|     or_else(F&& f) && | |||
|     { | |||
|         if(this->is_err()){return f(std::move(this->as_err()));} | |||
|         return ok(std::move(this->as_ok())); | |||
|     } | |||
| 
 | |||
|     // if *this is error, returns *this. otherwise, returns other.
 | |||
|     result and_other(const result& other) const& | |||
|     { | |||
|         return this->is_err() ? *this : other; | |||
|     } | |||
|     result and_other(result&& other) && | |||
|     { | |||
|         return this->is_err() ? std::move(*this) : std::move(other); | |||
|     } | |||
| 
 | |||
|     // if *this is okay, returns *this. otherwise, returns other.
 | |||
|     result or_other(const result& other) const& | |||
|     { | |||
|         return this->is_ok() ? *this : other; | |||
|     } | |||
|     result or_other(result&& other) && | |||
|     { | |||
|         return this->is_ok() ? std::move(*this) : std::move(other); | |||
|     } | |||
| 
 | |||
|     void swap(result<T, E>& other) | |||
|     { | |||
|         result<T, E> tmp(std::move(*this)); | |||
|         *this = std::move(other); | |||
|         other = std::move(tmp); | |||
|         return ; | |||
|     } | |||
| 
 | |||
|   private: | |||
| 
 | |||
|     static std::string format_error(std::exception const& excpt) | |||
|     { | |||
|         return std::string(excpt.what()); | |||
|     } | |||
|     template<typename U, typename std::enable_if<!std::is_base_of< | |||
|         std::exception, U>::value, std::nullptr_t>::type = nullptr> | |||
|     static std::string format_error(U const& others) | |||
|     { | |||
|         std::ostringstream oss; oss << others; | |||
|         return oss.str(); | |||
|     } | |||
| 
 | |||
|     void cleanup() noexcept | |||
|     { | |||
|         if(this->is_ok_) {this->succ.~success_type();} | |||
|         else             {this->fail.~failure_type();} | |||
|         return; | |||
|     } | |||
| 
 | |||
|   private: | |||
| 
 | |||
|     bool      is_ok_; | |||
|     union | |||
|     { | |||
|         success_type succ; | |||
|         failure_type fail; | |||
|     }; | |||
| }; | |||
| 
 | |||
| template<typename T, typename E> | |||
| void swap(result<T, E>& lhs, result<T, E>& rhs) | |||
| { | |||
|     lhs.swap(rhs); | |||
|     return; | |||
| } | |||
| 
 | |||
| // this might be confusing because it eagerly evaluated, while in the other
 | |||
| // cases operator && and || are short-circuited.
 | |||
| //
 | |||
| // template<typename T, typename E>
 | |||
| // inline result<T, E>
 | |||
| // operator&&(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
 | |||
| // {
 | |||
| //     return lhs.is_ok() ? rhs : lhs;
 | |||
| // }
 | |||
| //
 | |||
| // template<typename T, typename E>
 | |||
| // inline result<T, E>
 | |||
| // operator||(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
 | |||
| // {
 | |||
| //     return lhs.is_ok() ? lhs : rhs;
 | |||
| // }
 | |||
| 
 | |||
| // ----------------------------------------------------------------------------
 | |||
| // re-use result<T, E> as a optional<T> with none_t
 | |||
| 
 | |||
| namespace detail | |||
| { | |||
| struct none_t {}; | |||
| inline bool operator==(const none_t&, const none_t&) noexcept {return true;} | |||
| inline bool operator!=(const none_t&, const none_t&) noexcept {return false;} | |||
| inline bool operator< (const none_t&, const none_t&) noexcept {return false;} | |||
| inline bool operator<=(const none_t&, const none_t&) noexcept {return true;} | |||
| inline bool operator> (const none_t&, const none_t&) noexcept {return false;} | |||
| inline bool operator>=(const none_t&, const none_t&) noexcept {return true;} | |||
| template<typename charT, typename traitsT> | |||
| std::basic_ostream<charT, traitsT>& | |||
| operator<<(std::basic_ostream<charT, traitsT>& os, const none_t&) | |||
| { | |||
|     os << "none"; | |||
|     return os; | |||
| } | |||
| inline failure<none_t> none() noexcept {return failure<none_t>{none_t{}};} | |||
| } // detail
 | |||
| } // toml11
 | |||
| #endif// TOML11_RESULT_H
 | |||
| @ -0,0 +1,994 @@ | |||
| //     Copyright Toru Niina 2019.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_SERIALIZER_HPP
 | |||
| #define TOML11_SERIALIZER_HPP
 | |||
| #include <cmath>
 | |||
| #include <cstdio>
 | |||
| 
 | |||
| #include <limits>
 | |||
| 
 | |||
| #if defined(_WIN32)
 | |||
| #include <locale.h>
 | |||
| #elif defined(__APPLE__) || defined(__FreeBSD__)
 | |||
| #include <xlocale.h>
 | |||
| #elif defined(__linux__)
 | |||
| #include <locale.h>
 | |||
| #endif
 | |||
| 
 | |||
| #include "lexer.hpp"
 | |||
| #include "value.hpp"
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| 
 | |||
| // This function serialize a key. It checks a string is a bare key and
 | |||
| // escapes special characters if the string is not compatible to a bare key.
 | |||
| // ```cpp
 | |||
| // std::string k("non.bare.key"); // the key itself includes `.`s.
 | |||
| // std::string formatted = toml::format_key(k);
 | |||
| // assert(formatted == "\"non.bare.key\"");
 | |||
| // ```
 | |||
| //
 | |||
| // This function is exposed to make it easy to write a user-defined serializer.
 | |||
| // Since toml restricts characters available in a bare key, generally a string
 | |||
| // should be escaped. But checking whether a string needs to be surrounded by
 | |||
| // a `"` and escaping some special character is boring.
 | |||
| template<typename charT, typename traits, typename Alloc> | |||
| std::basic_string<charT, traits, Alloc> | |||
| format_key(const std::basic_string<charT, traits, Alloc>& k) | |||
| { | |||
|     if(k.empty()) | |||
|     { | |||
|         return std::string("\"\""); | |||
|     } | |||
| 
 | |||
|     // check the key can be a bare (unquoted) key
 | |||
|     detail::location loc(k, std::vector<char>(k.begin(), k.end())); | |||
|     detail::lex_unquoted_key::invoke(loc); | |||
|     if(loc.iter() == loc.end()) | |||
|     { | |||
|         return k; // all the tokens are consumed. the key is unquoted-key.
 | |||
|     } | |||
| 
 | |||
|     //if it includes special characters, then format it in a "quoted" key.
 | |||
|     std::basic_string<charT, traits, Alloc> serialized("\""); | |||
|     for(const char c : k) | |||
|     { | |||
|         switch(c) | |||
|         { | |||
|             case '\\': {serialized += "\\\\"; break;} | |||
|             case '\"': {serialized += "\\\""; break;} | |||
|             case '\b': {serialized += "\\b";  break;} | |||
|             case '\t': {serialized += "\\t";  break;} | |||
|             case '\f': {serialized += "\\f";  break;} | |||
|             case '\n': {serialized += "\\n";  break;} | |||
|             case '\r': {serialized += "\\r";  break;} | |||
|             default: { | |||
|                 if (c >= 0x00 && c < 0x20) | |||
|                 { | |||
|                     std::array<char, 7> buf; | |||
|                     std::snprintf(buf.data(), buf.size(), "\\u00%02x", static_cast<int>(c)); | |||
|                     serialized += buf.data(); | |||
|                 } | |||
|                 else | |||
|                 { | |||
|                     serialized += c; | |||
|                 } | |||
|                 break; | |||
|             } | |||
|         } | |||
|     } | |||
|     serialized += "\""; | |||
|     return serialized; | |||
| } | |||
| 
 | |||
| template<typename charT, typename traits, typename Alloc> | |||
| std::basic_string<charT, traits, Alloc> | |||
| format_keys(const std::vector<std::basic_string<charT, traits, Alloc>>& keys) | |||
| { | |||
|     if(keys.empty()) | |||
|     { | |||
|         return std::string("\"\""); | |||
|     } | |||
| 
 | |||
|     std::basic_string<charT, traits, Alloc> serialized; | |||
|     for(const auto& ky : keys) | |||
|     { | |||
|         serialized += format_key(ky); | |||
|         serialized += charT('.'); | |||
|     } | |||
|     serialized.pop_back(); // remove the last dot '.'
 | |||
|     return serialized; | |||
| } | |||
| 
 | |||
| template<typename Value> | |||
| struct serializer | |||
| { | |||
|     static_assert(detail::is_basic_value<Value>::value, | |||
|                   "toml::serializer is for toml::value and its variants, " | |||
|                   "toml::basic_value<...>."); | |||
| 
 | |||
|     using value_type           = Value; | |||
|     using key_type             = typename value_type::key_type            ; | |||
|     using comment_type         = typename value_type::comment_type        ; | |||
|     using boolean_type         = typename value_type::boolean_type        ; | |||
|     using integer_type         = typename value_type::integer_type        ; | |||
|     using floating_type        = typename value_type::floating_type       ; | |||
|     using string_type          = typename value_type::string_type         ; | |||
|     using local_time_type      = typename value_type::local_time_type     ; | |||
|     using local_date_type      = typename value_type::local_date_type     ; | |||
|     using local_datetime_type  = typename value_type::local_datetime_type ; | |||
|     using offset_datetime_type = typename value_type::offset_datetime_type; | |||
|     using array_type           = typename value_type::array_type          ; | |||
|     using table_type           = typename value_type::table_type          ; | |||
| 
 | |||
|     serializer(const std::size_t w              = 80u, | |||
|                const int         float_prec     = std::numeric_limits<toml::floating>::max_digits10, | |||
|                const bool        can_be_inlined = false, | |||
|                const bool        no_comment     = false, | |||
|                std::vector<toml::key> ks        = {}, | |||
|                const bool     value_has_comment = false) | |||
|         : can_be_inlined_(can_be_inlined), no_comment_(no_comment), | |||
|           value_has_comment_(value_has_comment && !no_comment), | |||
|           float_prec_(float_prec), width_(w), keys_(std::move(ks)) | |||
|     {} | |||
|     ~serializer() = default; | |||
| 
 | |||
|     std::string operator()(const boolean_type& b) const | |||
|     { | |||
|         return b ? "true" : "false"; | |||
|     } | |||
|     std::string operator()(const integer_type i) const | |||
|     { | |||
| #if defined(_WIN32)
 | |||
|         _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); | |||
|         const std::string original_locale(setlocale(LC_NUMERIC, nullptr)); | |||
|         setlocale(LC_NUMERIC, "C"); | |||
| #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
 | |||
|         const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0)); | |||
|         locale_t original_locale(0); | |||
|         if(c_locale != locale_t(0)) | |||
|         { | |||
|             original_locale = uselocale(c_locale); | |||
|         } | |||
| #endif
 | |||
| 
 | |||
|         const auto str = std::to_string(i); | |||
| 
 | |||
| #if defined(_WIN32)
 | |||
|         setlocale(LC_NUMERIC, original_locale.c_str()); | |||
|         _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); | |||
| #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
 | |||
|         if(original_locale != locale_t(0)) | |||
|         { | |||
|             uselocale(original_locale); | |||
|         } | |||
| #endif
 | |||
|         return str; | |||
|     } | |||
|     std::string operator()(const floating_type f) const | |||
|     { | |||
|         if(std::isnan(f)) | |||
|         { | |||
|             if(std::signbit(f)) | |||
|             { | |||
|                 return std::string("-nan"); | |||
|             } | |||
|             else | |||
|             { | |||
|                 return std::string("nan"); | |||
|             } | |||
|         } | |||
|         else if(!std::isfinite(f)) | |||
|         { | |||
|             if(std::signbit(f)) | |||
|             { | |||
|                 return std::string("-inf"); | |||
|             } | |||
|             else | |||
|             { | |||
|                 return std::string("inf"); | |||
|             } | |||
|         } | |||
| 
 | |||
|         // set locale to "C".
 | |||
|         // To make it thread-local, we use OS-specific features.
 | |||
|         // If we set process-global locale, it can break other thread that also
 | |||
|         // outputs something simultaneously.
 | |||
| #if defined(_WIN32)
 | |||
|         _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); | |||
|         const std::string original_locale(setlocale(LC_NUMERIC, nullptr)); | |||
|         setlocale(LC_NUMERIC, "C"); | |||
| #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
 | |||
|         const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0)); | |||
|         locale_t original_locale(0); | |||
|         if(c_locale != locale_t(0)) | |||
|         { | |||
|             original_locale = uselocale(c_locale); | |||
|         } | |||
| #endif
 | |||
| 
 | |||
|         const auto fmt = "%.*g"; | |||
|         const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f); | |||
|         // +1 for null character(\0)
 | |||
|         std::vector<char> buf(static_cast<std::size_t>(bsz + 1), '\0'); | |||
|         std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); | |||
| 
 | |||
|         // restore the original locale
 | |||
| #if defined(_WIN32)
 | |||
|         setlocale(LC_NUMERIC, original_locale.c_str()); | |||
|         _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); | |||
| #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
 | |||
|         if(original_locale != locale_t(0)) | |||
|         { | |||
|             uselocale(original_locale); | |||
|         } | |||
| #endif
 | |||
| 
 | |||
|         std::string token(buf.begin(), std::prev(buf.end())); | |||
|         if(!token.empty() && token.back() == '.') // 1. => 1.0
 | |||
|         { | |||
|             token += '0'; | |||
|         } | |||
| 
 | |||
|         const auto e = std::find_if( | |||
|             token.cbegin(), token.cend(), [](const char c) noexcept -> bool { | |||
|                 return c == 'e' || c == 'E'; | |||
|             }); | |||
|         const auto has_exponent = (token.cend() != e); | |||
|         const auto has_fraction = (token.cend() != std::find( | |||
|             token.cbegin(), token.cend(), '.')); | |||
| 
 | |||
|         if(!has_exponent && !has_fraction) | |||
|         { | |||
|             // the resulting value does not have any float specific part!
 | |||
|             token += ".0"; | |||
|         } | |||
|         return token; | |||
|     } | |||
|     std::string operator()(const string_type& s) const | |||
|     { | |||
|         if(s.kind == string_t::basic) | |||
|         { | |||
|             if((std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || | |||
|                 std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) && | |||
|                this->width_ != (std::numeric_limits<std::size_t>::max)()) | |||
|             { | |||
|                 // if linefeed or double-quote is contained,
 | |||
|                 // make it multiline basic string.
 | |||
|                 const auto escaped = this->escape_ml_basic_string(s.str); | |||
|                 std::string open("\"\"\""); | |||
|                 std::string close("\"\"\""); | |||
|                 if(escaped.find('\n') != std::string::npos || | |||
|                    this->width_ < escaped.size() + 6) | |||
|                 { | |||
|                     // if the string body contains newline or is enough long,
 | |||
|                     // add newlines after and before delimiters.
 | |||
|                     open += "\n"; | |||
|                     close = std::string("\\\n") + close; | |||
|                 } | |||
|                 return open + escaped + close; | |||
|             } | |||
| 
 | |||
|             // no linefeed. try to make it oneline-string.
 | |||
|             std::string oneline = this->escape_basic_string(s.str); | |||
|             if(oneline.size() + 2 < width_ || width_ < 2) | |||
|             { | |||
|                 const std::string quote("\""); | |||
|                 return quote + oneline + quote; | |||
|             } | |||
| 
 | |||
|             // the line is too long compared to the specified width.
 | |||
|             // split it into multiple lines.
 | |||
|             std::string token("\"\"\"\n"); | |||
|             while(!oneline.empty()) | |||
|             { | |||
|                 if(oneline.size() < width_) | |||
|                 { | |||
|                     token += oneline; | |||
|                     oneline.clear(); | |||
|                 } | |||
|                 else if(oneline.at(width_-2) == '\\') | |||
|                 { | |||
|                     token += oneline.substr(0, width_-2); | |||
|                     token += "\\\n"; | |||
|                     oneline.erase(0, width_-2); | |||
|                 } | |||
|                 else | |||
|                 { | |||
|                     token += oneline.substr(0, width_-1); | |||
|                     token += "\\\n"; | |||
|                     oneline.erase(0, width_-1); | |||
|                 } | |||
|             } | |||
|             return token + std::string("\\\n\"\"\""); | |||
|         } | |||
|         else // the string `s` is literal-string.
 | |||
|         { | |||
|             if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || | |||
|                std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) | |||
|             { | |||
|                 std::string open("'''"); | |||
|                 if(this->width_ + 6 < s.str.size()) | |||
|                 { | |||
|                     open += '\n'; // the first newline is ignored by TOML spec
 | |||
|                 } | |||
|                 const std::string close("'''"); | |||
|                 return open + s.str + close; | |||
|             } | |||
|             else | |||
|             { | |||
|                 const std::string quote("'"); | |||
|                 return quote + s.str + quote; | |||
|             } | |||
|         } | |||
|     } | |||
| 
 | |||
|     std::string operator()(const local_date_type& d) const | |||
|     { | |||
|         std::ostringstream oss; | |||
|         oss << d; | |||
|         return oss.str(); | |||
|     } | |||
|     std::string operator()(const local_time_type& t) const | |||
|     { | |||
|         std::ostringstream oss; | |||
|         oss << t; | |||
|         return oss.str(); | |||
|     } | |||
|     std::string operator()(const local_datetime_type& dt) const | |||
|     { | |||
|         std::ostringstream oss; | |||
|         oss << dt; | |||
|         return oss.str(); | |||
|     } | |||
|     std::string operator()(const offset_datetime_type& odt) const | |||
|     { | |||
|         std::ostringstream oss; | |||
|         oss << odt; | |||
|         return oss.str(); | |||
|     } | |||
| 
 | |||
|     std::string operator()(const array_type& v) const | |||
|     { | |||
|         if(v.empty()) | |||
|         { | |||
|             return std::string("[]"); | |||
|         } | |||
|         if(this->is_array_of_tables(v)) | |||
|         { | |||
|             return make_array_of_tables(v); | |||
|         } | |||
| 
 | |||
|         // not an array of tables. normal array.
 | |||
|         // first, try to make it inline if none of the elements have a comment.
 | |||
|         if( ! this->has_comment_inside(v)) | |||
|         { | |||
|             const auto inl = this->make_inline_array(v); | |||
|             if(inl.size() < this->width_ && | |||
|                std::find(inl.cbegin(), inl.cend(), '\n') == inl.cend()) | |||
|             { | |||
|                 return inl; | |||
|             } | |||
|         } | |||
| 
 | |||
|         // if the length exceeds this->width_, print multiline array.
 | |||
|         // key = [
 | |||
|         //   # ...
 | |||
|         //   42,
 | |||
|         //   ...
 | |||
|         // ]
 | |||
|         std::string token; | |||
|         std::string current_line; | |||
|         token += "[\n"; | |||
|         for(const auto& item : v) | |||
|         { | |||
|             if( ! item.comments().empty() && !no_comment_) | |||
|             { | |||
|                 // if comment exists, the element must be the only element in the line.
 | |||
|                 // e.g. the following is not allowed.
 | |||
|                 // ```toml
 | |||
|                 // array = [
 | |||
|                 // # comment for what?
 | |||
|                 // 1, 2, 3, 4, 5
 | |||
|                 // ]
 | |||
|                 // ```
 | |||
|                 if(!current_line.empty()) | |||
|                 { | |||
|                     if(current_line.back() != '\n') | |||
|                     { | |||
|                         current_line += '\n'; | |||
|                     } | |||
|                     token += current_line; | |||
|                     current_line.clear(); | |||
|                 } | |||
|                 for(const auto& c : item.comments()) | |||
|                 { | |||
|                     token += '#'; | |||
|                     token += c; | |||
|                     token += '\n'; | |||
|                 } | |||
|                 token += toml::visit(*this, item); | |||
|                 if(!token.empty() && token.back() == '\n') {token.pop_back();} | |||
|                 token += ",\n"; | |||
|                 continue; | |||
|             } | |||
|             std::string next_elem; | |||
|             if(item.is_table()) | |||
|             { | |||
|                 serializer ser(*this); | |||
|                 ser.can_be_inlined_ = true; | |||
|                 ser.width_ = (std::numeric_limits<std::size_t>::max)(); | |||
|                 next_elem += toml::visit(ser, item); | |||
|             } | |||
|             else | |||
|             { | |||
|                 next_elem += toml::visit(*this, item); | |||
|             } | |||
| 
 | |||
|             // comma before newline.
 | |||
|             if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();} | |||
| 
 | |||
|             // if current line does not exceeds the width limit, continue.
 | |||
|             if(current_line.size() + next_elem.size() + 1 < this->width_) | |||
|             { | |||
|                 current_line += next_elem; | |||
|                 current_line += ','; | |||
|             } | |||
|             else if(current_line.empty()) | |||
|             { | |||
|                 // if current line was empty, force put the next_elem because
 | |||
|                 // next_elem is not splittable
 | |||
|                 token += next_elem; | |||
|                 token += ",\n"; | |||
|                 // current_line is kept empty
 | |||
|             } | |||
|             else // reset current_line
 | |||
|             { | |||
|                 assert(current_line.back() == ','); | |||
|                 token += current_line; | |||
|                 token += '\n'; | |||
|                 current_line = next_elem; | |||
|                 current_line += ','; | |||
|             } | |||
|         } | |||
|         if(!current_line.empty()) | |||
|         { | |||
|             if(!current_line.empty() && current_line.back() != '\n') | |||
|             { | |||
|                 current_line += '\n'; | |||
|             } | |||
|             token += current_line; | |||
|         } | |||
|         token += "]\n"; | |||
|         return token; | |||
|     } | |||
| 
 | |||
|     // templatize for any table-like container
 | |||
|     std::string operator()(const table_type& v) const | |||
|     { | |||
|         // if an element has a comment, then it can't be inlined.
 | |||
|         // table = {# how can we write a comment for this? key = "value"}
 | |||
|         if(this->can_be_inlined_ && !(this->has_comment_inside(v))) | |||
|         { | |||
|             std::string token; | |||
|             if(!this->keys_.empty()) | |||
|             { | |||
|                 token += format_key(this->keys_.back()); | |||
|                 token += " = "; | |||
|             } | |||
|             token += this->make_inline_table(v); | |||
|             if(token.size() < this->width_ && | |||
|                token.end() == std::find(token.begin(), token.end(), '\n')) | |||
|             { | |||
|                 return token; | |||
|             } | |||
|         } | |||
| 
 | |||
|         std::string token; | |||
|         if(!keys_.empty()) | |||
|         { | |||
|             token += '['; | |||
|             token += format_keys(keys_); | |||
|             token += "]\n"; | |||
|         } | |||
|         token += this->make_multiline_table(v); | |||
|         return token; | |||
|     } | |||
| 
 | |||
|   private: | |||
| 
 | |||
|     std::string escape_basic_string(const std::string& s) const | |||
|     { | |||
|         //XXX assuming `s` is a valid utf-8 sequence.
 | |||
|         std::string retval; | |||
|         for(const char c : s) | |||
|         { | |||
|             switch(c) | |||
|             { | |||
|                 case '\\': {retval += "\\\\"; break;} | |||
|                 case '\"': {retval += "\\\""; break;} | |||
|                 case '\b': {retval += "\\b";  break;} | |||
|                 case '\t': {retval += "\\t";  break;} | |||
|                 case '\f': {retval += "\\f";  break;} | |||
|                 case '\n': {retval += "\\n";  break;} | |||
|                 case '\r': {retval += "\\r";  break;} | |||
|                 default  : | |||
|                 { | |||
|                     if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F) | |||
|                     { | |||
|                         retval += "\\u00"; | |||
|                         retval += char(48 + (c / 16)); | |||
|                         retval += char((c % 16 < 10 ? 48 : 55) + (c % 16)); | |||
|                     } | |||
|                     else | |||
|                     { | |||
|                         retval += c; | |||
|                     } | |||
|                 } | |||
|             } | |||
|         } | |||
|         return retval; | |||
|     } | |||
| 
 | |||
|     std::string escape_ml_basic_string(const std::string& s) const | |||
|     { | |||
|         std::string retval; | |||
|         for(auto i=s.cbegin(), e=s.cend(); i!=e; ++i) | |||
|         { | |||
|             switch(*i) | |||
|             { | |||
|                 case '\\': {retval += "\\\\"; break;} | |||
|                 // One or two consecutive "s are allowed.
 | |||
|                 // Later we will check there are no three consecutive "s.
 | |||
|                 //   case '\"': {retval += "\\\""; break;}
 | |||
|                 case '\b': {retval += "\\b";  break;} | |||
|                 case '\t': {retval += "\\t";  break;} | |||
|                 case '\f': {retval += "\\f";  break;} | |||
|                 case '\n': {retval += "\n";   break;} | |||
|                 case '\r': | |||
|                 { | |||
|                     if(std::next(i) != e && *std::next(i) == '\n') | |||
|                     { | |||
|                         retval += "\r\n"; | |||
|                         ++i; | |||
|                     } | |||
|                     else | |||
|                     { | |||
|                         retval += "\\r"; | |||
|                     } | |||
|                     break; | |||
|                 } | |||
|                 default  : | |||
|                 { | |||
|                     const auto c = *i; | |||
|                     if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F) | |||
|                     { | |||
|                         retval += "\\u00"; | |||
|                         retval += char(48 + (c / 16)); | |||
|                         retval += char((c % 16 < 10 ? 48 : 55) + (c % 16)); | |||
|                     } | |||
|                     else | |||
|                     { | |||
|                         retval += c; | |||
|                     } | |||
|                 } | |||
| 
 | |||
|             } | |||
|         } | |||
|         // Only 1 or 2 consecutive `"`s are allowed in multiline basic string.
 | |||
|         // 3 consecutive `"`s are considered as a closing delimiter.
 | |||
|         // We need to check if there are 3 or more consecutive `"`s and insert
 | |||
|         // backslash to break them down into several short `"`s like the `str6`
 | |||
|         // in the following example.
 | |||
|         // ```toml
 | |||
|         // str4 = """Here are two quotation marks: "". Simple enough."""
 | |||
|         // # str5 = """Here are three quotation marks: """."""  # INVALID
 | |||
|         // str5 = """Here are three quotation marks: ""\"."""
 | |||
|         // str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
 | |||
|         // ```
 | |||
|         auto found_3_quotes = retval.find("\"\"\""); | |||
|         while(found_3_quotes != std::string::npos) | |||
|         { | |||
|             retval.replace(found_3_quotes, 3, "\"\"\\\""); | |||
|             found_3_quotes = retval.find("\"\"\""); | |||
|         } | |||
|         return retval; | |||
|     } | |||
| 
 | |||
|     // if an element of a table or an array has a comment, it cannot be inlined.
 | |||
|     bool has_comment_inside(const array_type& a) const noexcept | |||
|     { | |||
|         // if no_comment is set, comments would not be written.
 | |||
|         if(this->no_comment_) {return false;} | |||
| 
 | |||
|         for(const auto& v : a) | |||
|         { | |||
|             if(!v.comments().empty()) {return true;} | |||
|         } | |||
|         return false; | |||
|     } | |||
|     bool has_comment_inside(const table_type& t) const noexcept | |||
|     { | |||
|         // if no_comment is set, comments would not be written.
 | |||
|         if(this->no_comment_) {return false;} | |||
| 
 | |||
|         for(const auto& kv : t) | |||
|         { | |||
|             if(!kv.second.comments().empty()) {return true;} | |||
|         } | |||
|         return false; | |||
|     } | |||
| 
 | |||
|     std::string make_inline_array(const array_type& v) const | |||
|     { | |||
|         assert(!has_comment_inside(v)); | |||
|         std::string token; | |||
|         token += '['; | |||
|         bool is_first = true; | |||
|         for(const auto& item : v) | |||
|         { | |||
|             if(is_first) {is_first = false;} else {token += ',';} | |||
|             token += visit(serializer( | |||
|                 (std::numeric_limits<std::size_t>::max)(), this->float_prec_, | |||
|                 /* inlined */ true, /*no comment*/ false, /*keys*/ {}, | |||
|                 /*has_comment*/ !item.comments().empty()), item); | |||
|         } | |||
|         token += ']'; | |||
|         return token; | |||
|     } | |||
| 
 | |||
|     std::string make_inline_table(const table_type& v) const | |||
|     { | |||
|         assert(!has_comment_inside(v)); | |||
|         assert(this->can_be_inlined_); | |||
|         std::string token; | |||
|         token += '{'; | |||
|         bool is_first = true; | |||
|         for(const auto& kv : v) | |||
|         { | |||
|             // in inline tables, trailing comma is not allowed (toml-lang #569).
 | |||
|             if(is_first) {is_first = false;} else {token += ',';} | |||
|             token += format_key(kv.first); | |||
|             token += '='; | |||
|             token += visit(serializer( | |||
|                 (std::numeric_limits<std::size_t>::max)(), this->float_prec_, | |||
|                 /* inlined */ true, /*no comment*/ false, /*keys*/ {}, | |||
|                 /*has_comment*/ !kv.second.comments().empty()), kv.second); | |||
|         } | |||
|         token += '}'; | |||
|         return token; | |||
|     } | |||
| 
 | |||
|     std::string make_multiline_table(const table_type& v) const | |||
|     { | |||
|         std::string token; | |||
| 
 | |||
|         // print non-table elements first.
 | |||
|         // ```toml
 | |||
|         // [foo]         # a table we're writing now here
 | |||
|         // key = "value" # <- non-table element, "key"
 | |||
|         // # ...
 | |||
|         // [foo.bar] # <- table element, "bar"
 | |||
|         // ```
 | |||
|         // because after printing [foo.bar], the remaining non-table values will
 | |||
|         // be assigned into [foo.bar], not [foo]. Those values should be printed
 | |||
|         // earlier.
 | |||
|         for(const auto& kv : v) | |||
|         { | |||
|             if(kv.second.is_table() || is_array_of_tables(kv.second)) | |||
|             { | |||
|                 continue; | |||
|             } | |||
| 
 | |||
|             token += write_comments(kv.second); | |||
| 
 | |||
|             const auto key_and_sep    = format_key(kv.first) + " = "; | |||
|             const auto residual_width = (this->width_ > key_and_sep.size()) ? | |||
|                                         this->width_ - key_and_sep.size() : 0; | |||
|             token += key_and_sep; | |||
|             token += visit(serializer(residual_width, this->float_prec_, | |||
|                 /*can be inlined*/ true, /*no comment*/ false, /*keys*/ {}, | |||
|                 /*has_comment*/ !kv.second.comments().empty()), kv.second); | |||
| 
 | |||
|             if(token.back() != '\n') | |||
|             { | |||
|                 token += '\n'; | |||
|             } | |||
|         } | |||
| 
 | |||
|         // normal tables / array of tables
 | |||
| 
 | |||
|         // after multiline table appeared, the other tables cannot be inline
 | |||
|         // because the table would be assigned into the table.
 | |||
|         // [foo]
 | |||
|         // ...
 | |||
|         // bar = {...} # <- bar will be a member of [foo].
 | |||
|         bool multiline_table_printed = false; | |||
|         for(const auto& kv : v) | |||
|         { | |||
|             if(!kv.second.is_table() && !is_array_of_tables(kv.second)) | |||
|             { | |||
|                 continue; // other stuff are already serialized. skip them.
 | |||
|             } | |||
| 
 | |||
|             std::vector<toml::key> ks(this->keys_); | |||
|             ks.push_back(kv.first); | |||
| 
 | |||
|             auto tmp = visit(serializer(this->width_, this->float_prec_, | |||
|                 !multiline_table_printed, this->no_comment_, ks, | |||
|                 /*has_comment*/ !kv.second.comments().empty()), kv.second); | |||
| 
 | |||
|             // If it is the first time to print a multi-line table, it would be
 | |||
|             // helpful to separate normal key-value pair and subtables by a
 | |||
|             // newline.
 | |||
|             // (this checks if the current key-value pair contains newlines.
 | |||
|             //  but it is not perfect because multi-line string can also contain
 | |||
|             //  a newline. in such a case, an empty line will be written) TODO
 | |||
|             if((!multiline_table_printed) && | |||
|                std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend()) | |||
|             { | |||
|                 multiline_table_printed = true; | |||
|                 token += '\n'; // separate key-value pairs and subtables
 | |||
| 
 | |||
|                 token += write_comments(kv.second); | |||
|                 token += tmp; | |||
| 
 | |||
|                 // care about recursive tables (all tables in each level prints
 | |||
|                 // newline and there will be a full of newlines)
 | |||
|                 if(tmp.substr(tmp.size() - 2, 2) != "\n\n" && | |||
|                    tmp.substr(tmp.size() - 4, 4) != "\r\n\r\n" ) | |||
|                 { | |||
|                     token += '\n'; | |||
|                 } | |||
|             } | |||
|             else | |||
|             { | |||
|                 token += write_comments(kv.second); | |||
|                 token += tmp; | |||
|                 token += '\n'; | |||
|             } | |||
|         } | |||
|         return token; | |||
|     } | |||
| 
 | |||
|     std::string make_array_of_tables(const array_type& v) const | |||
|     { | |||
|         // if it's not inlined, we need to add `[[table.key]]`.
 | |||
|         // but if it can be inlined, we can format it as the following.
 | |||
|         // ```
 | |||
|         // table.key = [
 | |||
|         //   {...},
 | |||
|         //   # comment
 | |||
|         //   {...},
 | |||
|         // ]
 | |||
|         // ```
 | |||
|         // This function checks if inlinization is possible or not, and then
 | |||
|         // format the array-of-tables in a proper way.
 | |||
|         //
 | |||
|         // Note about comments:
 | |||
|         //
 | |||
|         // If the array itself has a comment (value_has_comment_ == true), we
 | |||
|         // should try to make it inline.
 | |||
|         // ```toml
 | |||
|         // # comment about array
 | |||
|         // array = [
 | |||
|         //   # comment about table element
 | |||
|         //   {of = "table"}
 | |||
|         // ]
 | |||
|         // ```
 | |||
|         // If it is formatted as a multiline table, the two comments becomes
 | |||
|         // indistinguishable.
 | |||
|         // ```toml
 | |||
|         // # comment about array
 | |||
|         // # comment about table element
 | |||
|         // [[array]]
 | |||
|         // of = "table"
 | |||
|         // ```
 | |||
|         // So we need to try to make it inline, and it force-inlines regardless
 | |||
|         // of the line width limit.
 | |||
|         //     It may fail if the element of a table has comment. In that case,
 | |||
|         // the array-of-tables will be formatted as a multiline table.
 | |||
|         if(this->can_be_inlined_ || this->value_has_comment_) | |||
|         { | |||
|             std::string token; | |||
|             if(!keys_.empty()) | |||
|             { | |||
|                 token += format_key(keys_.back()); | |||
|                 token += " = "; | |||
|             } | |||
| 
 | |||
|             bool failed = false; | |||
|             token += "[\n"; | |||
|             for(const auto& item : v) | |||
|             { | |||
|                 // if an element of the table has a comment, the table
 | |||
|                 // cannot be inlined.
 | |||
|                 if(this->has_comment_inside(item.as_table())) | |||
|                 { | |||
|                     failed = true; | |||
|                     break; | |||
|                 } | |||
|                 // write comments for the table itself
 | |||
|                 token += write_comments(item); | |||
| 
 | |||
|                 const auto t = this->make_inline_table(item.as_table()); | |||
| 
 | |||
|                 if(t.size() + 1 > width_ || // +1 for the last comma {...},
 | |||
|                    std::find(t.cbegin(), t.cend(), '\n') != t.cend()) | |||
|                 { | |||
|                     // if the value itself has a comment, ignore the line width limit
 | |||
|                     if( ! this->value_has_comment_) | |||
|                     { | |||
|                         failed = true; | |||
|                         break; | |||
|                     } | |||
|                 } | |||
|                 token += t; | |||
|                 token += ",\n"; | |||
|             } | |||
| 
 | |||
|             if( ! failed) | |||
|             { | |||
|                 token += "]\n"; | |||
|                 return token; | |||
|             } | |||
|             // if failed, serialize them as [[array.of.tables]].
 | |||
|         } | |||
| 
 | |||
|         std::string token; | |||
|         for(const auto& item : v) | |||
|         { | |||
|             token += write_comments(item); | |||
|             token += "[["; | |||
|             token += format_keys(keys_); | |||
|             token += "]]\n"; | |||
|             token += this->make_multiline_table(item.as_table()); | |||
|         } | |||
|         return token; | |||
|     } | |||
| 
 | |||
|     std::string write_comments(const value_type& v) const | |||
|     { | |||
|         std::string retval; | |||
|         if(this->no_comment_) {return retval;} | |||
| 
 | |||
|         for(const auto& c : v.comments()) | |||
|         { | |||
|             retval += '#'; | |||
|             retval += c; | |||
|             retval += '\n'; | |||
|         } | |||
|         return retval; | |||
|     } | |||
| 
 | |||
|     bool is_array_of_tables(const value_type& v) const | |||
|     { | |||
|         if(!v.is_array() || v.as_array().empty()) {return false;} | |||
|         return is_array_of_tables(v.as_array()); | |||
|     } | |||
|     bool is_array_of_tables(const array_type& v) const | |||
|     { | |||
|         // Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to
 | |||
|         // check all the element in an array to check if the array is an array
 | |||
|         // of tables.
 | |||
|         return std::all_of(v.begin(), v.end(), [](const value_type& elem) { | |||
|                 return elem.is_table(); | |||
|             }); | |||
|     } | |||
| 
 | |||
|   private: | |||
| 
 | |||
|     bool        can_be_inlined_; | |||
|     bool        no_comment_; | |||
|     bool        value_has_comment_; | |||
|     int         float_prec_; | |||
|     std::size_t width_; | |||
|     std::vector<toml::key> keys_; | |||
| }; | |||
| 
 | |||
| template<typename C, | |||
|          template<typename ...> class M, template<typename ...> class V> | |||
| std::string | |||
| format(const basic_value<C, M, V>& v, std::size_t w = 80u, | |||
|        int fprec = std::numeric_limits<toml::floating>::max_digits10, | |||
|        bool no_comment = false, bool force_inline = false) | |||
| { | |||
|     using value_type = basic_value<C, M, V>; | |||
|     // if value is a table, it is considered to be a root object.
 | |||
|     // the root object can't be an inline table.
 | |||
|     if(v.is_table()) | |||
|     { | |||
|         std::ostringstream oss; | |||
|         if(!v.comments().empty()) | |||
|         { | |||
|             oss << v.comments(); | |||
|             oss << '\n'; // to split the file comment from the first element
 | |||
|         } | |||
|         const auto serialized = visit(serializer<value_type>(w, fprec, false, no_comment), v); | |||
|         oss << serialized; | |||
|         return oss.str(); | |||
|     } | |||
|     return visit(serializer<value_type>(w, fprec, force_inline), v); | |||
| } | |||
| 
 | |||
| namespace detail | |||
| { | |||
| template<typename charT, typename traits> | |||
| int comment_index(std::basic_ostream<charT, traits>&) | |||
| { | |||
|     static const int index = std::ios_base::xalloc(); | |||
|     return index; | |||
| } | |||
| } // detail
 | |||
| 
 | |||
| template<typename charT, typename traits> | |||
| std::basic_ostream<charT, traits>& | |||
| nocomment(std::basic_ostream<charT, traits>& os) | |||
| { | |||
|     // by default, it is zero. and by default, it shows comments.
 | |||
|     os.iword(detail::comment_index(os)) = 1; | |||
|     return os; | |||
| } | |||
| 
 | |||
| template<typename charT, typename traits> | |||
| std::basic_ostream<charT, traits>& | |||
| showcomment(std::basic_ostream<charT, traits>& os) | |||
| { | |||
|     // by default, it is zero. and by default, it shows comments.
 | |||
|     os.iword(detail::comment_index(os)) = 0; | |||
|     return os; | |||
| } | |||
| 
 | |||
| template<typename charT, typename traits, typename C, | |||
|          template<typename ...> class M, template<typename ...> class V> | |||
| std::basic_ostream<charT, traits>& | |||
| operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v) | |||
| { | |||
|     using value_type = basic_value<C, M, V>; | |||
| 
 | |||
|     // get status of std::setw().
 | |||
|     const auto w     = static_cast<std::size_t>(os.width()); | |||
|     const int  fprec = static_cast<int>(os.precision()); | |||
|     os.width(0); | |||
| 
 | |||
|     // by default, iword is initialized by 0. And by default, toml11 outputs
 | |||
|     // comments. So `0` means showcomment. 1 means nocommnet.
 | |||
|     const bool no_comment = (1 == os.iword(detail::comment_index(os))); | |||
| 
 | |||
|     if(!no_comment && v.is_table() && !v.comments().empty()) | |||
|     { | |||
|         os << v.comments(); | |||
|         os << '\n'; // to split the file comment from the first element
 | |||
|     } | |||
|     // the root object can't be an inline table. so pass `false`.
 | |||
|     const auto serialized = visit(serializer<value_type>(w, fprec, no_comment, false), v); | |||
|     os << serialized; | |||
| 
 | |||
|     // if v is a non-table value, and has only one comment, then
 | |||
|     // put a comment just after a value. in the following way.
 | |||
|     //
 | |||
|     // ```toml
 | |||
|     // key = "value" # comment.
 | |||
|     // ```
 | |||
|     //
 | |||
|     // Since the top-level toml object is a table, one who want to put a
 | |||
|     // non-table toml value must use this in a following way.
 | |||
|     //
 | |||
|     // ```cpp
 | |||
|     // toml::value v;
 | |||
|     // std::cout << "user-defined-key = " << v << std::endl;
 | |||
|     // ```
 | |||
|     //
 | |||
|     // In this case, it is impossible to put comments before key-value pair.
 | |||
|     // The only way to preserve comments is to put all of them after a value.
 | |||
|     if(!no_comment && !v.is_table() && !v.comments().empty()) | |||
|     { | |||
|         os << " #"; | |||
|         for(const auto& c : v.comments()) {os << c;} | |||
|     } | |||
|     return os; | |||
| } | |||
| 
 | |||
| } // toml
 | |||
| #endif// TOML11_SERIALIZER_HPP
 | |||
| @ -0,0 +1,239 @@ | |||
| //     Copyright Toru Niina 2019.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_SOURCE_LOCATION_HPP
 | |||
| #define TOML11_SOURCE_LOCATION_HPP
 | |||
| #include <cstdint>
 | |||
| #include <sstream>
 | |||
| 
 | |||
| #include "region.hpp"
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| 
 | |||
| // A struct to contain location in a toml file.
 | |||
| // The interface imitates std::experimental::source_location,
 | |||
| // but not completely the same.
 | |||
| //
 | |||
| // It would be constructed by toml::value. It can be used to generate
 | |||
| // user-defined error messages.
 | |||
| //
 | |||
| // - std::uint_least32_t line() const noexcept
 | |||
| //   - returns the line number where the region is on.
 | |||
| // - std::uint_least32_t column() const noexcept
 | |||
| //   - returns the column number where the region starts.
 | |||
| // - std::uint_least32_t region() const noexcept
 | |||
| //   - returns the size of the region.
 | |||
| //
 | |||
| // +-- line()       +-- region of interest (region() == 9)
 | |||
| // v            .---+---.
 | |||
| // 12 | value = "foo bar"
 | |||
| //              ^
 | |||
| //              +-- column()
 | |||
| //
 | |||
| // - std::string const& file_name() const noexcept;
 | |||
| //   - name of the file.
 | |||
| // - std::string const& line_str() const noexcept;
 | |||
| //   - the whole line that contains the region of interest.
 | |||
| //
 | |||
| struct source_location | |||
| { | |||
|   public: | |||
| 
 | |||
|     source_location() | |||
|         : line_num_(1), column_num_(1), region_size_(1), | |||
|           file_name_("unknown file"), line_str_("") | |||
|     {} | |||
| 
 | |||
|     explicit source_location(const detail::region_base* reg) | |||
|         : line_num_(1), column_num_(1), region_size_(1), | |||
|           file_name_("unknown file"), line_str_("") | |||
|     { | |||
|         if(reg) | |||
|         { | |||
|             if(reg->line_num() != detail::region_base().line_num()) | |||
|             { | |||
|                 line_num_ = static_cast<std::uint_least32_t>( | |||
|                         std::stoul(reg->line_num())); | |||
|             } | |||
|             column_num_  = static_cast<std::uint_least32_t>(reg->before() + 1); | |||
|             region_size_ = static_cast<std::uint_least32_t>(reg->size()); | |||
|             file_name_   = reg->name(); | |||
|             line_str_    = reg->line(); | |||
|         } | |||
|     } | |||
| 
 | |||
|     explicit source_location(const detail::region& reg) | |||
|         : line_num_(static_cast<std::uint_least32_t>(std::stoul(reg.line_num()))), | |||
|           column_num_(static_cast<std::uint_least32_t>(reg.before() + 1)), | |||
|           region_size_(static_cast<std::uint_least32_t>(reg.size())), | |||
|           file_name_(reg.name()), | |||
|           line_str_ (reg.line()) | |||
|     {} | |||
|     explicit source_location(const detail::location& loc) | |||
|         : line_num_(static_cast<std::uint_least32_t>(std::stoul(loc.line_num()))), | |||
|           column_num_(static_cast<std::uint_least32_t>(loc.before() + 1)), | |||
|           region_size_(static_cast<std::uint_least32_t>(loc.size())), | |||
|           file_name_(loc.name()), | |||
|           line_str_ (loc.line()) | |||
|     {} | |||
| 
 | |||
|     ~source_location() = default; | |||
|     source_location(source_location const&) = default; | |||
|     source_location(source_location &&)     = default; | |||
|     source_location& operator=(source_location const&) = default; | |||
|     source_location& operator=(source_location &&)     = default; | |||
| 
 | |||
|     std::uint_least32_t line()      const noexcept {return line_num_;} | |||
|     std::uint_least32_t column()    const noexcept {return column_num_;} | |||
|     std::uint_least32_t region()    const noexcept {return region_size_;} | |||
| 
 | |||
|     std::string const&  file_name() const noexcept {return file_name_;} | |||
|     std::string const&  line_str()  const noexcept {return line_str_;} | |||
| 
 | |||
|   private: | |||
| 
 | |||
|     std::uint_least32_t line_num_; | |||
|     std::uint_least32_t column_num_; | |||
|     std::uint_least32_t region_size_; | |||
|     std::string         file_name_; | |||
|     std::string         line_str_; | |||
| }; | |||
| 
 | |||
| namespace detail | |||
| { | |||
| 
 | |||
| // internal error message generation.
 | |||
| inline std::string format_underline(const std::string& message, | |||
|         const std::vector<std::pair<source_location, std::string>>& loc_com, | |||
|         const std::vector<std::string>& helps = {}, | |||
|         const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) | |||
| { | |||
|     std::size_t line_num_width = 0; | |||
|     for(const auto& lc : loc_com) | |||
|     { | |||
|         std::uint_least32_t line = lc.first.line(); | |||
|         std::size_t        digit = 0; | |||
|         while(line != 0) | |||
|         { | |||
|             line  /= 10; | |||
|             digit +=  1; | |||
|         } | |||
|         line_num_width = (std::max)(line_num_width, digit); | |||
|     } | |||
|     // 1 is the minimum width
 | |||
|     line_num_width = std::max<std::size_t>(line_num_width, 1); | |||
| 
 | |||
|     std::ostringstream retval; | |||
| 
 | |||
|     if(color::should_color() || colorize) | |||
|     { | |||
|         retval << color::colorize; // turn on ANSI color
 | |||
|     } | |||
| 
 | |||
|     // XXX
 | |||
|     // Here, before `colorize` support, it does not output `[error]` prefix
 | |||
|     // automatically. So some user may output it manually and this change may
 | |||
|     // duplicate the prefix. To avoid it, check the first 7 characters and
 | |||
|     // if it is "[error]", it removes that part from the message shown.
 | |||
|     if(message.size() > 7 && message.substr(0, 7) == "[error]") | |||
|     { | |||
|         retval | |||
| #ifndef TOML11_NO_ERROR_PREFIX
 | |||
|                << color::bold << color::red << "[error]" << color::reset | |||
| #endif
 | |||
|                << color::bold << message.substr(7) << color::reset << '\n'; | |||
|     } | |||
|     else | |||
|     { | |||
|         retval | |||
| #ifndef TOML11_NO_ERROR_PREFIX
 | |||
|                << color::bold << color::red << "[error] " << color::reset | |||
| #endif
 | |||
|                << color::bold << message << color::reset << '\n'; | |||
|     } | |||
| 
 | |||
|     const auto format_one_location = [line_num_width] | |||
|         (std::ostringstream& oss, | |||
|          const source_location& loc, const std::string& comment) -> void | |||
|         { | |||
|             oss << ' ' << color::bold << color::blue | |||
|                 << std::setw(static_cast<int>(line_num_width)) | |||
|                 << std::right << loc.line() << " | "  << color::reset | |||
|                 << loc.line_str() << '\n'; | |||
| 
 | |||
|             oss << make_string(line_num_width + 1, ' ') | |||
|                 << color::bold << color::blue << " | " << color::reset | |||
|                 << make_string(loc.column()-1 /*1-origin*/, ' '); | |||
| 
 | |||
|             if(loc.region() == 1) | |||
|             { | |||
|                 // invalid
 | |||
|                 // ^------
 | |||
|                 oss << color::bold << color::red << "^---" << color::reset; | |||
|             } | |||
|             else | |||
|             { | |||
|                 // invalid
 | |||
|                 // ~~~~~~~
 | |||
|                 const auto underline_len = (std::min)( | |||
|                     static_cast<std::size_t>(loc.region()), loc.line_str().size()); | |||
|                 oss << color::bold << color::red | |||
|                     << make_string(underline_len, '~') << color::reset; | |||
|             } | |||
|             oss << ' '; | |||
|             oss << comment; | |||
|             return; | |||
|         }; | |||
| 
 | |||
|     assert(!loc_com.empty()); | |||
| 
 | |||
|     // --> example.toml
 | |||
|     //   |
 | |||
|     retval << color::bold << color::blue << " --> " << color::reset | |||
|            << loc_com.front().first.file_name() << '\n'; | |||
|     retval << make_string(line_num_width + 1, ' ') | |||
|            << color::bold << color::blue << " |\n"  << color::reset; | |||
|     // 1 | key value
 | |||
|     //   |    ^--- missing =
 | |||
|     format_one_location(retval, loc_com.front().first, loc_com.front().second); | |||
| 
 | |||
|     // process the rest of the locations
 | |||
|     for(std::size_t i=1; i<loc_com.size(); ++i) | |||
|     { | |||
|         const auto& prev = loc_com.at(i-1); | |||
|         const auto& curr = loc_com.at(i); | |||
| 
 | |||
|         retval << '\n'; | |||
|         // if the filenames are the same, print "..."
 | |||
|         if(prev.first.file_name() == curr.first.file_name()) | |||
|         { | |||
|             retval << color::bold << color::blue << " ...\n" << color::reset; | |||
|         } | |||
|         else // if filename differs, print " --> filename.toml" again
 | |||
|         { | |||
|             retval << color::bold << color::blue << " --> " << color::reset | |||
|                    << curr.first.file_name() << '\n'; | |||
|             retval << make_string(line_num_width + 1, ' ') | |||
|                    << color::bold << color::blue << " |\n"  << color::reset; | |||
|         } | |||
| 
 | |||
|         format_one_location(retval, curr.first, curr.second); | |||
|     } | |||
| 
 | |||
|     if(!helps.empty()) | |||
|     { | |||
|         retval << '\n'; | |||
|         retval << make_string(line_num_width + 1, ' '); | |||
|         retval << color::bold << color::blue << " |" << color::reset; | |||
|         for(const auto& help : helps) | |||
|         { | |||
|             retval << color::bold << "\nHint: " << color::reset; | |||
|             retval << help; | |||
|         } | |||
|     } | |||
|     return retval.str(); | |||
| } | |||
| 
 | |||
| } // detail
 | |||
| } // toml
 | |||
| #endif// TOML11_SOURCE_LOCATION_HPP
 | |||
| @ -0,0 +1,43 @@ | |||
| //     Copyright Toru Niina 2017.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_STORAGE_HPP
 | |||
| #define TOML11_STORAGE_HPP
 | |||
| #include "utility.hpp"
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| namespace detail | |||
| { | |||
| 
 | |||
| // this contains pointer and deep-copy the content if copied.
 | |||
| // to avoid recursive pointer.
 | |||
| template<typename T> | |||
| struct storage | |||
| { | |||
|     using value_type = T; | |||
| 
 | |||
|     explicit storage(value_type const& v): ptr(toml::make_unique<T>(v)) {} | |||
|     explicit storage(value_type&&      v): ptr(toml::make_unique<T>(std::move(v))) {} | |||
|     ~storage() = default; | |||
|     storage(const storage& rhs): ptr(toml::make_unique<T>(*rhs.ptr)) {} | |||
|     storage& operator=(const storage& rhs) | |||
|     { | |||
|         this->ptr = toml::make_unique<T>(*rhs.ptr); | |||
|         return *this; | |||
|     } | |||
|     storage(storage&&) = default; | |||
|     storage& operator=(storage&&) = default; | |||
| 
 | |||
|     bool is_ok() const noexcept {return static_cast<bool>(ptr);} | |||
| 
 | |||
|     value_type&       value() &      noexcept {return *ptr;} | |||
|     value_type const& value() const& noexcept {return *ptr;} | |||
|     value_type&&      value() &&     noexcept {return std::move(*ptr);} | |||
| 
 | |||
|   private: | |||
|     std::unique_ptr<value_type> ptr; | |||
| }; | |||
| 
 | |||
| } // detail
 | |||
| } // toml
 | |||
| #endif// TOML11_STORAGE_HPP
 | |||
| @ -0,0 +1,228 @@ | |||
| //     Copyright Toru Niina 2017.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_STRING_HPP
 | |||
| #define TOML11_STRING_HPP
 | |||
| 
 | |||
| #include "version.hpp"
 | |||
| 
 | |||
| #include <cstdint>
 | |||
| 
 | |||
| #include <algorithm>
 | |||
| #include <string>
 | |||
| 
 | |||
| #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
 | |||
| #if __has_include(<string_view>)
 | |||
| #define TOML11_USING_STRING_VIEW 1
 | |||
| #include <string_view>
 | |||
| #endif
 | |||
| #endif
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| 
 | |||
| enum class string_t : std::uint8_t | |||
| { | |||
|     basic   = 0, | |||
|     literal = 1, | |||
| }; | |||
| 
 | |||
| struct string | |||
| { | |||
|     string()  = default; | |||
|     ~string() = default; | |||
|     string(const string& s) = default; | |||
|     string(string&& s)      = default; | |||
|     string& operator=(const string& s) = default; | |||
|     string& operator=(string&& s)      = default; | |||
| 
 | |||
|     string(const std::string& s): kind(string_t::basic), str(s){} | |||
|     string(const std::string& s, string_t k):   kind(k), str(s){} | |||
|     string(const char* s):        kind(string_t::basic), str(s){} | |||
|     string(const char* s,        string_t k):   kind(k), str(s){} | |||
| 
 | |||
|     string(std::string&& s): kind(string_t::basic), str(std::move(s)){} | |||
|     string(std::string&& s, string_t k):   kind(k), str(std::move(s)){} | |||
| 
 | |||
|     string& operator=(const std::string& s) | |||
|     {kind = string_t::basic; str = s; return *this;} | |||
|     string& operator=(std::string&& s) | |||
|     {kind = string_t::basic; str = std::move(s); return *this;} | |||
| 
 | |||
|     operator std::string&       () &      noexcept {return str;} | |||
|     operator std::string const& () const& noexcept {return str;} | |||
|     operator std::string&&      () &&     noexcept {return std::move(str);} | |||
| 
 | |||
|     string& operator+=(const char*        rhs) {str += rhs; return *this;} | |||
|     string& operator+=(const char         rhs) {str += rhs; return *this;} | |||
|     string& operator+=(const std::string& rhs) {str += rhs; return *this;} | |||
|     string& operator+=(const string&      rhs) {str += rhs.str; return *this;} | |||
| 
 | |||
| #if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0
 | |||
|     explicit string(std::string_view s): kind(string_t::basic), str(s){} | |||
|     string(std::string_view s, string_t k): kind(k), str(s){} | |||
| 
 | |||
|     string& operator=(std::string_view s) | |||
|     {kind = string_t::basic; str = s; return *this;} | |||
| 
 | |||
|     explicit operator std::string_view() const noexcept | |||
|     {return std::string_view(str);} | |||
| 
 | |||
|     string& operator+=(const std::string_view& rhs) {str += rhs; return *this;} | |||
| #endif
 | |||
| 
 | |||
|     string_t    kind; | |||
|     std::string str; | |||
| }; | |||
| 
 | |||
| inline bool operator==(const string& lhs, const string& rhs) | |||
| { | |||
|     return lhs.kind == rhs.kind && lhs.str == rhs.str; | |||
| } | |||
| inline bool operator!=(const string& lhs, const string& rhs) | |||
| { | |||
|     return !(lhs == rhs); | |||
| } | |||
| inline bool operator<(const string& lhs, const string& rhs) | |||
| { | |||
|     return (lhs.kind == rhs.kind) ? (lhs.str < rhs.str) : (lhs.kind < rhs.kind); | |||
| } | |||
| inline bool operator>(const string& lhs, const string& rhs) | |||
| { | |||
|     return rhs < lhs; | |||
| } | |||
| inline bool operator<=(const string& lhs, const string& rhs) | |||
| { | |||
|     return !(rhs < lhs); | |||
| } | |||
| inline bool operator>=(const string& lhs, const string& rhs) | |||
| { | |||
|     return !(lhs < rhs); | |||
| } | |||
| 
 | |||
| inline bool | |||
| operator==(const string& lhs, const std::string& rhs) {return lhs.str == rhs;} | |||
| inline bool | |||
| operator!=(const string& lhs, const std::string& rhs) {return lhs.str != rhs;} | |||
| inline bool | |||
| operator< (const string& lhs, const std::string& rhs) {return lhs.str <  rhs;} | |||
| inline bool | |||
| operator> (const string& lhs, const std::string& rhs) {return lhs.str >  rhs;} | |||
| inline bool | |||
| operator<=(const string& lhs, const std::string& rhs) {return lhs.str <= rhs;} | |||
| inline bool | |||
| operator>=(const string& lhs, const std::string& rhs) {return lhs.str >= rhs;} | |||
| 
 | |||
| inline bool | |||
| operator==(const std::string& lhs, const string& rhs) {return lhs == rhs.str;} | |||
| inline bool | |||
| operator!=(const std::string& lhs, const string& rhs) {return lhs != rhs.str;} | |||
| inline bool | |||
| operator< (const std::string& lhs, const string& rhs) {return lhs <  rhs.str;} | |||
| inline bool | |||
| operator> (const std::string& lhs, const string& rhs) {return lhs >  rhs.str;} | |||
| inline bool | |||
| operator<=(const std::string& lhs, const string& rhs) {return lhs <= rhs.str;} | |||
| inline bool | |||
| operator>=(const std::string& lhs, const string& rhs) {return lhs >= rhs.str;} | |||
| 
 | |||
| inline bool | |||
| operator==(const string& lhs, const char* rhs) {return lhs.str == std::string(rhs);} | |||
| inline bool | |||
| operator!=(const string& lhs, const char* rhs) {return lhs.str != std::string(rhs);} | |||
| inline bool | |||
| operator< (const string& lhs, const char* rhs) {return lhs.str <  std::string(rhs);} | |||
| inline bool | |||
| operator> (const string& lhs, const char* rhs) {return lhs.str >  std::string(rhs);} | |||
| inline bool | |||
| operator<=(const string& lhs, const char* rhs) {return lhs.str <= std::string(rhs);} | |||
| inline bool | |||
| operator>=(const string& lhs, const char* rhs) {return lhs.str >= std::string(rhs);} | |||
| 
 | |||
| inline bool | |||
| operator==(const char* lhs, const string& rhs) {return std::string(lhs) == rhs.str;} | |||
| inline bool | |||
| operator!=(const char* lhs, const string& rhs) {return std::string(lhs) != rhs.str;} | |||
| inline bool | |||
| operator< (const char* lhs, const string& rhs) {return std::string(lhs) <  rhs.str;} | |||
| inline bool | |||
| operator> (const char* lhs, const string& rhs) {return std::string(lhs) >  rhs.str;} | |||
| inline bool | |||
| operator<=(const char* lhs, const string& rhs) {return std::string(lhs) <= rhs.str;} | |||
| inline bool | |||
| operator>=(const char* lhs, const string& rhs) {return std::string(lhs) >= rhs.str;} | |||
| 
 | |||
| template<typename charT, typename traits> | |||
| std::basic_ostream<charT, traits>& | |||
| operator<<(std::basic_ostream<charT, traits>& os, const string& s) | |||
| { | |||
|     if(s.kind == string_t::basic) | |||
|     { | |||
|         if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend()) | |||
|         { | |||
|             // it contains newline. make it multiline string.
 | |||
|             os << "\"\"\"\n"; | |||
|             for(auto i=s.str.cbegin(), e=s.str.cend(); i!=e; ++i) | |||
|             { | |||
|                 switch(*i) | |||
|                 { | |||
|                     case '\\': {os << "\\\\"; break;} | |||
|                     case '\"': {os << "\\\""; break;} | |||
|                     case '\b': {os << "\\b";  break;} | |||
|                     case '\t': {os << "\\t";  break;} | |||
|                     case '\f': {os << "\\f";  break;} | |||
|                     case '\n': {os << '\n';   break;} | |||
|                     case '\r': | |||
|                     { | |||
|                         // since it is a multiline string,
 | |||
|                         // CRLF is not needed to be escaped.
 | |||
|                         if(std::next(i) != e && *std::next(i) == '\n') | |||
|                         { | |||
|                             os << "\r\n"; | |||
|                             ++i; | |||
|                         } | |||
|                         else | |||
|                         { | |||
|                             os << "\\r"; | |||
|                         } | |||
|                         break; | |||
|                     } | |||
|                     default: {os << *i; break;} | |||
|                 } | |||
|             } | |||
|             os << "\\\n\"\"\""; | |||
|             return os; | |||
|         } | |||
|         // no newline. make it inline.
 | |||
|         os << "\""; | |||
|         for(const auto c : s.str) | |||
|         { | |||
|             switch(c) | |||
|             { | |||
|                 case '\\': {os << "\\\\"; break;} | |||
|                 case '\"': {os << "\\\""; break;} | |||
|                 case '\b': {os << "\\b";  break;} | |||
|                 case '\t': {os << "\\t";  break;} | |||
|                 case '\f': {os << "\\f";  break;} | |||
|                 case '\n': {os << "\\n";  break;} | |||
|                 case '\r': {os << "\\r";  break;} | |||
|                 default  : {os << c;      break;} | |||
|             } | |||
|         } | |||
|         os << "\""; | |||
|         return os; | |||
|     } | |||
|     // the string `s` is literal-string.
 | |||
|     if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || | |||
|        std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) | |||
|     { | |||
|         // contains newline or single quote. make it multiline.
 | |||
|         os << "'''\n" << s.str << "'''"; | |||
|         return os; | |||
|     } | |||
|     // normal literal string
 | |||
|     os << '\'' << s.str << '\''; | |||
|     return os; | |||
| } | |||
| 
 | |||
| } // toml
 | |||
| #endif// TOML11_STRING_H
 | |||
| @ -0,0 +1,328 @@ | |||
| //     Copyright Toru Niina 2017.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_TRAITS_HPP
 | |||
| #define TOML11_TRAITS_HPP
 | |||
| 
 | |||
| #include "from.hpp"
 | |||
| #include "into.hpp"
 | |||
| #include "version.hpp"
 | |||
| 
 | |||
| #include <chrono>
 | |||
| #include <forward_list>
 | |||
| #include <string>
 | |||
| #include <tuple>
 | |||
| #include <type_traits>
 | |||
| #include <utility>
 | |||
| 
 | |||
| #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
 | |||
| #if __has_include(<string_view>)
 | |||
| #include <string_view>
 | |||
| #endif // has_include(<string_view>)
 | |||
| #endif // cplusplus   >= C++17
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| template<typename C, template<typename ...> class T, template<typename ...> class A> | |||
| class basic_value; | |||
| 
 | |||
| namespace detail | |||
| { | |||
| // ---------------------------------------------------------------------------
 | |||
| // check whether type T is a kind of container/map class
 | |||
| 
 | |||
| struct has_iterator_impl | |||
| { | |||
|     template<typename T> static std::true_type  check(typename T::iterator*); | |||
|     template<typename T> static std::false_type check(...); | |||
| }; | |||
| struct has_value_type_impl | |||
| { | |||
|     template<typename T> static std::true_type  check(typename T::value_type*); | |||
|     template<typename T> static std::false_type check(...); | |||
| }; | |||
| struct has_key_type_impl | |||
| { | |||
|     template<typename T> static std::true_type  check(typename T::key_type*); | |||
|     template<typename T> static std::false_type check(...); | |||
| }; | |||
| struct has_mapped_type_impl | |||
| { | |||
|     template<typename T> static std::true_type  check(typename T::mapped_type*); | |||
|     template<typename T> static std::false_type check(...); | |||
| }; | |||
| struct has_reserve_method_impl | |||
| { | |||
|     template<typename T> static std::false_type check(...); | |||
|     template<typename T> static std::true_type  check( | |||
|         decltype(std::declval<T>().reserve(std::declval<std::size_t>()))*); | |||
| }; | |||
| struct has_push_back_method_impl | |||
| { | |||
|     template<typename T> static std::false_type check(...); | |||
|     template<typename T> static std::true_type  check( | |||
|         decltype(std::declval<T>().push_back(std::declval<typename T::value_type>()))*); | |||
| }; | |||
| struct is_comparable_impl | |||
| { | |||
|     template<typename T> static std::false_type check(...); | |||
|     template<typename T> static std::true_type  check( | |||
|         decltype(std::declval<T>() < std::declval<T>())*); | |||
| }; | |||
| 
 | |||
| struct has_from_toml_method_impl | |||
| { | |||
|     template<typename T, typename C, | |||
|              template<typename ...> class Tb, template<typename ...> class A> | |||
|     static std::true_type  check( | |||
|         decltype(std::declval<T>().from_toml( | |||
|                 std::declval<::toml::basic_value<C, Tb, A>>()))*); | |||
| 
 | |||
|     template<typename T, typename C, | |||
|              template<typename ...> class Tb, template<typename ...> class A> | |||
|     static std::false_type check(...); | |||
| }; | |||
| struct has_into_toml_method_impl | |||
| { | |||
|     template<typename T> | |||
|     static std::true_type  check(decltype(std::declval<T>().into_toml())*); | |||
|     template<typename T> | |||
|     static std::false_type check(...); | |||
| }; | |||
| 
 | |||
| struct has_specialized_from_impl | |||
| { | |||
|     template<typename T> | |||
|     static std::false_type check(...); | |||
|     template<typename T, std::size_t S = sizeof(::toml::from<T>)> | |||
|     static std::true_type check(::toml::from<T>*); | |||
| }; | |||
| struct has_specialized_into_impl | |||
| { | |||
|     template<typename T> | |||
|     static std::false_type check(...); | |||
|     template<typename T, std::size_t S = sizeof(::toml::into<T>)> | |||
|     static std::true_type check(::toml::from<T>*); | |||
| }; | |||
| 
 | |||
| 
 | |||
| /// Intel C++ compiler can not use decltype in parent class declaration, here
 | |||
| /// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076
 | |||
| #ifdef __INTEL_COMPILER
 | |||
| #define decltype(...) std::enable_if<true, decltype(__VA_ARGS__)>::type
 | |||
| #endif
 | |||
| 
 | |||
| template<typename T> | |||
| struct has_iterator    : decltype(has_iterator_impl::check<T>(nullptr)){}; | |||
| template<typename T> | |||
| struct has_value_type  : decltype(has_value_type_impl::check<T>(nullptr)){}; | |||
| template<typename T> | |||
| struct has_key_type    : decltype(has_key_type_impl::check<T>(nullptr)){}; | |||
| template<typename T> | |||
| struct has_mapped_type : decltype(has_mapped_type_impl::check<T>(nullptr)){}; | |||
| template<typename T> | |||
| struct has_reserve_method : decltype(has_reserve_method_impl::check<T>(nullptr)){}; | |||
| template<typename T> | |||
| struct has_push_back_method : decltype(has_push_back_method_impl::check<T>(nullptr)){}; | |||
| template<typename T> | |||
| struct is_comparable : decltype(is_comparable_impl::check<T>(nullptr)){}; | |||
| 
 | |||
| template<typename T, typename C, | |||
|          template<typename ...> class Tb, template<typename ...> class A> | |||
| struct has_from_toml_method | |||
| : decltype(has_from_toml_method_impl::check<T, C, Tb, A>(nullptr)){}; | |||
| 
 | |||
| template<typename T> | |||
| struct has_into_toml_method | |||
| : decltype(has_into_toml_method_impl::check<T>(nullptr)){}; | |||
| 
 | |||
| template<typename T> | |||
| struct has_specialized_from : decltype(has_specialized_from_impl::check<T>(nullptr)){}; | |||
| template<typename T> | |||
| struct has_specialized_into : decltype(has_specialized_into_impl::check<T>(nullptr)){}; | |||
| 
 | |||
| #ifdef __INTEL_COMPILER
 | |||
| #undef decltype
 | |||
| #endif
 | |||
| 
 | |||
| // ---------------------------------------------------------------------------
 | |||
| // C++17 and/or/not
 | |||
| 
 | |||
| #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
 | |||
| 
 | |||
| using std::conjunction; | |||
| using std::disjunction; | |||
| using std::negation; | |||
| 
 | |||
| #else
 | |||
| 
 | |||
| template<typename ...> struct conjunction : std::true_type{}; | |||
| template<typename T>   struct conjunction<T> : T{}; | |||
| template<typename T, typename ... Ts> | |||
| struct conjunction<T, Ts...> : | |||
|     std::conditional<static_cast<bool>(T::value), conjunction<Ts...>, T>::type | |||
| {}; | |||
| 
 | |||
| template<typename ...> struct disjunction : std::false_type{}; | |||
| template<typename T>   struct disjunction<T> : T {}; | |||
| template<typename T, typename ... Ts> | |||
| struct disjunction<T, Ts...> : | |||
|     std::conditional<static_cast<bool>(T::value), T, disjunction<Ts...>>::type | |||
| {}; | |||
| 
 | |||
| template<typename T> | |||
| struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{}; | |||
| 
 | |||
| #endif
 | |||
| 
 | |||
| // ---------------------------------------------------------------------------
 | |||
| // type checkers
 | |||
| 
 | |||
| template<typename T> struct is_std_pair : std::false_type{}; | |||
| template<typename T1, typename T2> | |||
| struct is_std_pair<std::pair<T1, T2>> : std::true_type{}; | |||
| 
 | |||
| template<typename T> struct is_std_tuple : std::false_type{}; | |||
| template<typename ... Ts> | |||
| struct is_std_tuple<std::tuple<Ts...>> : std::true_type{}; | |||
| 
 | |||
| template<typename T> struct is_std_forward_list : std::false_type{}; | |||
| template<typename T> | |||
| struct is_std_forward_list<std::forward_list<T>> : std::true_type{}; | |||
| 
 | |||
| template<typename T> struct is_chrono_duration: std::false_type{}; | |||
| template<typename Rep, typename Period> | |||
| struct is_chrono_duration<std::chrono::duration<Rep, Period>>: std::true_type{}; | |||
| 
 | |||
| template<typename T> | |||
| struct is_map : conjunction< // map satisfies all the following conditions
 | |||
|     has_iterator<T>,         // has T::iterator
 | |||
|     has_value_type<T>,       // has T::value_type
 | |||
|     has_key_type<T>,         // has T::key_type
 | |||
|     has_mapped_type<T>       // has T::mapped_type
 | |||
|     >{}; | |||
| template<typename T> struct is_map<T&>                : is_map<T>{}; | |||
| template<typename T> struct is_map<T const&>          : is_map<T>{}; | |||
| template<typename T> struct is_map<T volatile&>       : is_map<T>{}; | |||
| template<typename T> struct is_map<T const volatile&> : is_map<T>{}; | |||
| 
 | |||
| template<typename T> | |||
| struct is_container : conjunction< | |||
|     negation<is_map<T>>,                         // not a map
 | |||
|     negation<std::is_same<T, std::string>>,      // not a std::string
 | |||
| #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
 | |||
| #if __has_include(<string_view>)
 | |||
|     negation<std::is_same<T, std::string_view>>, // not a std::string_view
 | |||
| #endif // has_include(<string_view>)
 | |||
| #endif
 | |||
|     has_iterator<T>,                             // has T::iterator
 | |||
|     has_value_type<T>                            // has T::value_type
 | |||
|     >{}; | |||
| template<typename T> struct is_container<T&>                : is_container<T>{}; | |||
| template<typename T> struct is_container<T const&>          : is_container<T>{}; | |||
| template<typename T> struct is_container<T volatile&>       : is_container<T>{}; | |||
| template<typename T> struct is_container<T const volatile&> : is_container<T>{}; | |||
| 
 | |||
| template<typename T> | |||
| struct is_basic_value: std::false_type{}; | |||
| template<typename T> struct is_basic_value<T&>                : is_basic_value<T>{}; | |||
| template<typename T> struct is_basic_value<T const&>          : is_basic_value<T>{}; | |||
| template<typename T> struct is_basic_value<T volatile&>       : is_basic_value<T>{}; | |||
| template<typename T> struct is_basic_value<T const volatile&> : is_basic_value<T>{}; | |||
| template<typename C, template<typename ...> class M, template<typename ...> class V> | |||
| struct is_basic_value<::toml::basic_value<C, M, V>>: std::true_type{}; | |||
| 
 | |||
| // ---------------------------------------------------------------------------
 | |||
| // C++14 index_sequence
 | |||
| 
 | |||
| #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
 | |||
| 
 | |||
| using std::index_sequence; | |||
| using std::make_index_sequence; | |||
| 
 | |||
| #else
 | |||
| 
 | |||
| template<std::size_t ... Ns> struct index_sequence{}; | |||
| 
 | |||
| template<typename IS, std::size_t N> struct push_back_index_sequence{}; | |||
| template<std::size_t N, std::size_t ... Ns> | |||
| struct push_back_index_sequence<index_sequence<Ns...>, N> | |||
| { | |||
|     typedef index_sequence<Ns..., N> type; | |||
| }; | |||
| 
 | |||
| template<std::size_t N> | |||
| struct index_sequence_maker | |||
| { | |||
|     typedef typename push_back_index_sequence< | |||
|         typename index_sequence_maker<N-1>::type, N>::type type; | |||
| }; | |||
| template<> | |||
| struct index_sequence_maker<0> | |||
| { | |||
|     typedef index_sequence<0> type; | |||
| }; | |||
| template<std::size_t N> | |||
| using make_index_sequence = typename index_sequence_maker<N-1>::type; | |||
| 
 | |||
| #endif // cplusplus >= 2014
 | |||
| 
 | |||
| // ---------------------------------------------------------------------------
 | |||
| // C++14 enable_if_t
 | |||
| 
 | |||
| #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
 | |||
| 
 | |||
| using std::enable_if_t; | |||
| 
 | |||
| #else
 | |||
| 
 | |||
| template<bool B, typename T> | |||
| using enable_if_t = typename std::enable_if<B, T>::type; | |||
| 
 | |||
| #endif // cplusplus >= 2014
 | |||
| 
 | |||
| // ---------------------------------------------------------------------------
 | |||
| // return_type_of_t
 | |||
| 
 | |||
| #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L && defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable>=201703
 | |||
| 
 | |||
| template<typename F, typename ... Args> | |||
| using return_type_of_t = std::invoke_result_t<F, Args...>; | |||
| 
 | |||
| #else
 | |||
| // result_of is deprecated after C++17
 | |||
| template<typename F, typename ... Args> | |||
| using return_type_of_t = typename std::result_of<F(Args...)>::type; | |||
| 
 | |||
| #endif
 | |||
| 
 | |||
| // ---------------------------------------------------------------------------
 | |||
| // is_string_literal
 | |||
| //
 | |||
| // to use this, pass `typename remove_reference<T>::type` to T.
 | |||
| 
 | |||
| template<typename T> | |||
| struct is_string_literal: | |||
| disjunction< | |||
|     std::is_same<const char*, T>, | |||
|     conjunction< | |||
|         std::is_array<T>, | |||
|         std::is_same<const char, typename std::remove_extent<T>::type> | |||
|         > | |||
|     >{}; | |||
| 
 | |||
| // ---------------------------------------------------------------------------
 | |||
| // C++20 remove_cvref_t
 | |||
| 
 | |||
| template<typename T> | |||
| struct remove_cvref | |||
| { | |||
|     using type = typename std::remove_cv< | |||
|         typename std::remove_reference<T>::type>::type; | |||
| }; | |||
| 
 | |||
| template<typename T> | |||
| using remove_cvref_t = typename remove_cvref<T>::type; | |||
| 
 | |||
| }// detail
 | |||
| }//toml
 | |||
| #endif // TOML_TRAITS
 | |||
| @ -0,0 +1,173 @@ | |||
| //     Copyright Toru Niina 2017.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_TYPES_HPP
 | |||
| #define TOML11_TYPES_HPP
 | |||
| #include <unordered_map>
 | |||
| #include <vector>
 | |||
| 
 | |||
| #include "comments.hpp"
 | |||
| #include "datetime.hpp"
 | |||
| #include "string.hpp"
 | |||
| #include "traits.hpp"
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| 
 | |||
| template<typename Comment, // discard/preserve_comment
 | |||
|          template<typename ...> class Table, // map-like class
 | |||
|          template<typename ...> class Array> // vector-like class
 | |||
| class basic_value; | |||
| 
 | |||
| using character = char; | |||
| using key = std::string; | |||
| 
 | |||
| #if !defined(__clang__) && defined(__GNUC__) && __GNUC__ <= 4
 | |||
| #  pragma GCC diagnostic push
 | |||
| #  pragma GCC diagnostic ignored "-Wshadow"
 | |||
| #endif
 | |||
| 
 | |||
| using boolean        = bool; | |||
| using integer        = std::int64_t; | |||
| using floating       = double; // "float" is a keyword, cannot use it here.
 | |||
| // the following stuffs are structs defined here, so aliases are not needed.
 | |||
| // - string
 | |||
| // - offset_datetime
 | |||
| // - offset_datetime
 | |||
| // - local_datetime
 | |||
| // - local_date
 | |||
| // - local_time
 | |||
| 
 | |||
| #if defined(__GNUC__) && !defined(__clang__)
 | |||
| #  pragma GCC diagnostic pop
 | |||
| #endif
 | |||
| 
 | |||
| // default toml::value and default array/table. these are defined after defining
 | |||
| // basic_value itself.
 | |||
| // using value = basic_value<discard_comments, std::unordered_map, std::vector>;
 | |||
| // using array = typename value::array_type;
 | |||
| // using table = typename value::table_type;
 | |||
| 
 | |||
| // to avoid warnings about `value_t::integer` is "shadowing" toml::integer in
 | |||
| // GCC -Wshadow=global.
 | |||
| #if defined(__GNUC__) && !defined(__clang__)
 | |||
| #  pragma GCC diagnostic push
 | |||
| #  if 7 <= __GNUC__
 | |||
| #    pragma GCC diagnostic ignored "-Wshadow=global"
 | |||
| #  else // gcc-6 or older
 | |||
| #    pragma GCC diagnostic ignored "-Wshadow"
 | |||
| #  endif
 | |||
| #endif
 | |||
| enum class value_t : std::uint8_t | |||
| { | |||
|     empty           =  0, | |||
|     boolean         =  1, | |||
|     integer         =  2, | |||
|     floating        =  3, | |||
|     string          =  4, | |||
|     offset_datetime =  5, | |||
|     local_datetime  =  6, | |||
|     local_date      =  7, | |||
|     local_time      =  8, | |||
|     array           =  9, | |||
|     table           = 10, | |||
| }; | |||
| #if defined(__GNUC__) && !defined(__clang__)
 | |||
| #  pragma GCC diagnostic pop
 | |||
| #endif
 | |||
| 
 | |||
| template<typename charT, typename traits> | |||
| inline std::basic_ostream<charT, traits>& | |||
| operator<<(std::basic_ostream<charT, traits>& os, value_t t) | |||
| { | |||
|     switch(t) | |||
|     { | |||
|         case value_t::boolean         : os << "boolean";         return os; | |||
|         case value_t::integer         : os << "integer";         return os; | |||
|         case value_t::floating        : os << "floating";        return os; | |||
|         case value_t::string          : os << "string";          return os; | |||
|         case value_t::offset_datetime : os << "offset_datetime"; return os; | |||
|         case value_t::local_datetime  : os << "local_datetime";  return os; | |||
|         case value_t::local_date      : os << "local_date";      return os; | |||
|         case value_t::local_time      : os << "local_time";      return os; | |||
|         case value_t::array           : os << "array";           return os; | |||
|         case value_t::table           : os << "table";           return os; | |||
|         case value_t::empty           : os << "empty";           return os; | |||
|         default                       : os << "unknown";         return os; | |||
|     } | |||
| } | |||
| 
 | |||
| template<typename charT = char, | |||
|          typename traits = std::char_traits<charT>, | |||
|          typename alloc = std::allocator<charT>> | |||
| inline std::basic_string<charT, traits, alloc> stringize(value_t t) | |||
| { | |||
|     std::basic_ostringstream<charT, traits, alloc> oss; | |||
|     oss << t; | |||
|     return oss.str(); | |||
| } | |||
| 
 | |||
| namespace detail | |||
| { | |||
| 
 | |||
| // helper to define a type that represents a value_t value.
 | |||
| template<value_t V> | |||
| using value_t_constant = std::integral_constant<value_t, V>; | |||
| 
 | |||
| // meta-function that convertes from value_t to the exact toml type that corresponds to.
 | |||
| // It takes toml::basic_value type because array and table types depend on it.
 | |||
| template<value_t t, typename Value> struct enum_to_type                      {using type = void                      ;}; | |||
| template<typename Value> struct enum_to_type<value_t::empty          , Value>{using type = void                      ;}; | |||
| template<typename Value> struct enum_to_type<value_t::boolean        , Value>{using type = boolean                   ;}; | |||
| template<typename Value> struct enum_to_type<value_t::integer        , Value>{using type = integer                   ;}; | |||
| template<typename Value> struct enum_to_type<value_t::floating       , Value>{using type = floating                  ;}; | |||
| template<typename Value> struct enum_to_type<value_t::string         , Value>{using type = string                    ;}; | |||
| template<typename Value> struct enum_to_type<value_t::offset_datetime, Value>{using type = offset_datetime           ;}; | |||
| template<typename Value> struct enum_to_type<value_t::local_datetime , Value>{using type = local_datetime            ;}; | |||
| template<typename Value> struct enum_to_type<value_t::local_date     , Value>{using type = local_date                ;}; | |||
| template<typename Value> struct enum_to_type<value_t::local_time     , Value>{using type = local_time                ;}; | |||
| template<typename Value> struct enum_to_type<value_t::array          , Value>{using type = typename Value::array_type;}; | |||
| template<typename Value> struct enum_to_type<value_t::table          , Value>{using type = typename Value::table_type;}; | |||
| 
 | |||
| // meta-function that converts from an exact toml type to the enum that corresponds to.
 | |||
| template<typename T, typename Value> | |||
| struct type_to_enum : std::conditional< | |||
|     std::is_same<T, typename Value::array_type>::value, // if T == array_type,
 | |||
|     value_t_constant<value_t::array>,                   // then value_t::array
 | |||
|     typename std::conditional<                          // else...
 | |||
|         std::is_same<T, typename Value::table_type>::value, // if T == table_type
 | |||
|         value_t_constant<value_t::table>,               // then value_t::table
 | |||
|         value_t_constant<value_t::empty>                // else value_t::empty
 | |||
|         >::type | |||
|     >::type {}; | |||
| template<typename Value> struct type_to_enum<boolean        , Value>: value_t_constant<value_t::boolean        > {}; | |||
| template<typename Value> struct type_to_enum<integer        , Value>: value_t_constant<value_t::integer        > {}; | |||
| template<typename Value> struct type_to_enum<floating       , Value>: value_t_constant<value_t::floating       > {}; | |||
| template<typename Value> struct type_to_enum<string         , Value>: value_t_constant<value_t::string         > {}; | |||
| template<typename Value> struct type_to_enum<offset_datetime, Value>: value_t_constant<value_t::offset_datetime> {}; | |||
| template<typename Value> struct type_to_enum<local_datetime , Value>: value_t_constant<value_t::local_datetime > {}; | |||
| template<typename Value> struct type_to_enum<local_date     , Value>: value_t_constant<value_t::local_date     > {}; | |||
| template<typename Value> struct type_to_enum<local_time     , Value>: value_t_constant<value_t::local_time     > {}; | |||
| 
 | |||
| // meta-function that checks the type T is the same as one of the toml::* types.
 | |||
| template<typename T, typename Value> | |||
| struct is_exact_toml_type : disjunction< | |||
|     std::is_same<T, boolean        >, | |||
|     std::is_same<T, integer        >, | |||
|     std::is_same<T, floating       >, | |||
|     std::is_same<T, string         >, | |||
|     std::is_same<T, offset_datetime>, | |||
|     std::is_same<T, local_datetime >, | |||
|     std::is_same<T, local_date     >, | |||
|     std::is_same<T, local_time     >, | |||
|     std::is_same<T, typename Value::array_type>, | |||
|     std::is_same<T, typename Value::table_type> | |||
|     >{}; | |||
| template<typename T, typename V> struct is_exact_toml_type<T&, V>               : is_exact_toml_type<T, V>{}; | |||
| template<typename T, typename V> struct is_exact_toml_type<T const&, V>         : is_exact_toml_type<T, V>{}; | |||
| template<typename T, typename V> struct is_exact_toml_type<T volatile&, V>      : is_exact_toml_type<T, V>{}; | |||
| template<typename T, typename V> struct is_exact_toml_type<T const volatile&, V>: is_exact_toml_type<T, V>{}; | |||
| 
 | |||
| } // detail
 | |||
| } // toml
 | |||
| 
 | |||
| #endif// TOML11_TYPES_H
 | |||
| @ -0,0 +1,150 @@ | |||
| //     Copyright Toru Niina 2017.
 | |||
| // Distributed under the MIT License.
 | |||
| #ifndef TOML11_UTILITY_HPP
 | |||
| #define TOML11_UTILITY_HPP
 | |||
| #include <memory>
 | |||
| #include <sstream>
 | |||
| #include <utility>
 | |||
| 
 | |||
| #include "traits.hpp"
 | |||
| #include "version.hpp"
 | |||
| 
 | |||
| #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
 | |||
| #  define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]]
 | |||
| #elif defined(__GNUC__)
 | |||
| #  define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg)))
 | |||
| #elif defined(_MSC_VER)
 | |||
| #  define TOML11_MARK_AS_DEPRECATED(msg) __declspec(deprecated(msg))
 | |||
| #else
 | |||
| #  define TOML11_MARK_AS_DEPRECATED
 | |||
| #endif
 | |||
| 
 | |||
| namespace toml | |||
| { | |||
| 
 | |||
| #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
 | |||
| 
 | |||
| using std::make_unique; | |||
| 
 | |||
| #else
 | |||
| 
 | |||
| template<typename T, typename ... Ts> | |||
| inline std::unique_ptr<T> make_unique(Ts&& ... args) | |||
| { | |||
|     return std::unique_ptr<T>(new T(std::forward<Ts>(args)...)); | |||
| } | |||
| 
 | |||
| #endif // TOML11_CPLUSPLUS_STANDARD_VERSION >= 2014
 | |||
| 
 | |||
| namespace detail | |||
| { | |||
| template<typename Container> | |||
| void try_reserve_impl(Container& container, std::size_t N, std::true_type) | |||
| { | |||
|     container.reserve(N); | |||
|     return; | |||
| } | |||
| template<typename Container> | |||
| void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept | |||
| { | |||
|     return; | |||
| } | |||
| } // detail
 | |||
| 
 | |||
| template<typename Container> | |||
| void try_reserve(Container& container, std::size_t N) | |||
| { | |||
|     if(N <= container.size()) {return;} | |||
|     detail::try_reserve_impl(container, N, detail::has_reserve_method<Container>{}); | |||
|     return; | |||
| } | |||
| 
 | |||
| namespace detail | |||
| { | |||
| inline std::string concat_to_string_impl(std::ostringstream& oss) | |||
| { | |||
|     return oss.str(); | |||
| } | |||
| template<typename T, typename ... Ts> | |||
| std::string concat_to_string_impl(std::ostringstream& oss, T&& head, Ts&& ... tail) | |||
| { | |||
|     oss << std::forward<T>(head); | |||
|     return concat_to_string_impl(oss, std::forward<Ts>(tail) ... ); | |||
| } | |||
| } // detail
 | |||
| 
 | |||
| template<typename ... Ts> | |||
| std::string concat_to_string(Ts&& ... args) | |||
| { | |||
|     std::ostringstream oss; | |||
|     oss << std::boolalpha << std::fixed; | |||
|     return detail::concat_to_string_impl(oss, std::forward<Ts>(args) ...); | |||
| } | |||
| 
 | |||
| template<typename T> | |||
| T from_string(const std::string& str, T opt) | |||
| { | |||
|     T v(opt); | |||
|     std::istringstream iss(str); | |||
|     iss >> v; | |||
|     return v; | |||
| } | |||
| 
 | |||
| namespace detail | |||
| { | |||
| #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
 | |||
| template<typename T> | |||
| decltype(auto) last_one(T&& tail) noexcept | |||
| { | |||
|     return std::forward<T>(tail); | |||
| } | |||
| 
 | |||
| template<typename T, typename ... Ts> | |||
| decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept | |||
| { | |||
|     return last_one(std::forward<Ts>(tail)...); | |||
| } | |||
| #else // C++11
 | |||
| // The following code
 | |||
| // ```cpp
 | |||
| //  1 | template<typename T, typename ... Ts>
 | |||
| //  2 | auto last_one(T&& /*head*/, Ts&& ... tail)
 | |||
| //  3 |  -> decltype(last_one(std::forward<Ts>(tail)...))
 | |||
| //  4 | {
 | |||
| //  5 |     return last_one(std::forward<Ts>(tail)...);
 | |||
| //  6 | }
 | |||
| // ```
 | |||
| // does not work because the function `last_one(...)` is not yet defined at
 | |||
| // line #3, so `decltype()` cannot deduce the type returned from `last_one`.
 | |||
| // So we need to determine return type in a different way, like a meta func.
 | |||
| 
 | |||
| template<typename T, typename ... Ts> | |||
| struct last_one_in_pack | |||
| { | |||
|     using type = typename last_one_in_pack<Ts...>::type; | |||
| }; | |||
| template<typename T> | |||
| struct last_one_in_pack<T> | |||
| { | |||
|     using type = T; | |||
| }; | |||
| template<typename ... Ts> | |||
| using last_one_in_pack_t = typename last_one_in_pack<Ts...>::type; | |||
| 
 | |||
| template<typename T> | |||
| T&& last_one(T&& tail) noexcept | |||
| { | |||
|     return std::forward<T>(tail); | |||
| } | |||
| template<typename T, typename ... Ts> | |||
| enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t<Ts&& ...>> | |||
| last_one(T&& /*head*/, Ts&& ... tail) | |||
| { | |||
|     return last_one(std::forward<Ts>(tail)...); | |||
| } | |||
| 
 | |||
| #endif
 | |||
| } // detail
 | |||
| 
 | |||
| }// toml
 | |||
| #endif // TOML11_UTILITY
 | |||
						
							
						
						
							2037
	
						
						src/toml/value.hpp
						
							File diff suppressed because it is too large
							
							
								
									View File
								
							
						
					
				File diff suppressed because it is too large
							
							
								
									View File
								
							
						| @ -0,0 +1,42 @@ | |||
| #ifndef TOML11_VERSION_HPP
 | |||
| #define TOML11_VERSION_HPP
 | |||
| 
 | |||
| // This file checks C++ version.
 | |||
| 
 | |||
| #ifndef __cplusplus
 | |||
| #    error "__cplusplus is not defined"
 | |||
| #endif
 | |||
| 
 | |||
| // Since MSVC does not define `__cplusplus` correctly unless you pass
 | |||
| // `/Zc:__cplusplus` when compiling, the workaround macros are added.
 | |||
| // Those enables you to define version manually or to use MSVC specific
 | |||
| // version macro automatically.
 | |||
| //
 | |||
| // The value of `__cplusplus` macro is defined in the C++ standard spec, but
 | |||
| // MSVC ignores the value, maybe because of backward compatibility. Instead,
 | |||
| // MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in
 | |||
| // the C++ standard. First we check the manual version definition, and then
 | |||
| // we check if _MSVC_LANG is defined. If neither, use normal `__cplusplus`.
 | |||
| //
 | |||
| // FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170
 | |||
| //      https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170
 | |||
| //
 | |||
| #if   defined(TOML11_ENFORCE_CXX11)
 | |||
| #  define TOML11_CPLUSPLUS_STANDARD_VERSION 201103L
 | |||
| #elif defined(TOML11_ENFORCE_CXX14)
 | |||
| #  define TOML11_CPLUSPLUS_STANDARD_VERSION 201402L
 | |||
| #elif defined(TOML11_ENFORCE_CXX17)
 | |||
| #  define TOML11_CPLUSPLUS_STANDARD_VERSION 201703L
 | |||
| #elif defined(TOML11_ENFORCE_CXX20)
 | |||
| #  define TOML11_CPLUSPLUS_STANDARD_VERSION 202002L
 | |||
| #elif defined(_MSVC_LANG) && defined(_MSC_VER) && 1910 <= _MSC_VER
 | |||
| #  define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG
 | |||
| #else
 | |||
| #  define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus
 | |||
| #endif
 | |||
| 
 | |||
| #if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L && _MSC_VER < 1900
 | |||
| #    error "toml11 requires C++11 or later."
 | |||
| #endif
 | |||
| 
 | |||
| #endif// TOML11_VERSION_HPP
 | |||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue