release 0.1.0

- added `toml::is_number<>`
- added `toml::node_type::none`
- added initializer_list and vector relops to `toml::array`
- added constructors for `time_offset` and `date_time`
- added much to `node_view`
- added tests for `node_view` value relops
- added lots more documentation
- removed `time_offset::from_hh_mm`
- removed the handling of `\s` literals (looks like it's not going be accepted as-is)
This commit is contained in:
Mark Gillard
2020-02-20 23:08:20 +02:00
parent 0b4eca301c
commit 2219fd22bb
21 changed files with 1033 additions and 527 deletions

View File

@@ -96,7 +96,7 @@ addition of unreleased features from the [TOML master] and some sane cherry-pick
[TOML issues list] where the discussion strongly indicates inclusion in a near-future release.
The library advertises the most recent numbered language version it fully supports via the preprocessor
defines `TOML_LANG_MAJOR`, `TOML_LANG_MINOR` and `TOML_LANG_REVISION`.
defines `TOML_LANG_MAJOR`, `TOML_LANG_MINOR` and `TOML_LANG_PATCH`.
### **🔸Unreleased TOML features:**
- [#356]: Allow leading zeros in the exponent part of a float
@@ -104,7 +104,6 @@ defines `TOML_LANG_MAJOR`, `TOML_LANG_MINOR` and `TOML_LANG_REVISION`.
- [#562]: Allow hex floatingpoint values
- [#567]: Clarify that control characters are not permitted in comments
- [#571]: Allow raw tabs inside strings
- [#622]: Add short escaping alias `\s` for space (`\u0020`)
- [#644]: Support `+` in key names
- [#665]: Make arrays heterogeneous
- [#671]: Local time of day format should support `09:30` as opposed to `09:30:00`
@@ -167,7 +166,7 @@ cd ../build-clang && ninja && ninja test
UTF-8 decoding is performed using a state machine based on Bjoern Hoehrmann's '[Flexible and Economical UTF-8 Decoder]',
which is also subject to the terms of the MIT license - see [LICENSE-utf8-decoder].
[API documentation]: https://marzer.github.io/tomlplusplus/namespacetoml.html
[API documentation]: https://marzer.github.io/tomlplusplus/
[unreleased TOML language features]: https://github.com/marzer/tomlplusplus#unreleased-features
[numbered version]: https://github.com/toml-lang/toml/releases
[char8_t]: https://en.cppreference.com/w/cpp/keyword/char8_t

View File

@@ -89,7 +89,8 @@ pre.m-code + pre
{
margin-top: -1.0rem;
color: #bababa; /* is yououou */
background-color: #282e36aa;
background-color: #383e46;
border-top: 2px solid #181e26;
font-size: 0.8rem;
}

View File

@@ -52,24 +52,217 @@
#undef TOML_STRING_PREFIX
#undef TOML_UNDEF_MACROS
#undef TOML_DOXYGEN
#undef TOML_RELOPS_REORDERING
#undef TOML_ASYMMETRICAL_EQUALITY_OPS
#endif
/// \mainpage toml++
///
/// This is the home of the API documentation for toml++, a [TOML](https://github.com/toml-lang/toml) parser for C++17 and later.
/// If you're looking for information about how to add toml++ to your project etc, see the
/// see [README](https://github.com/marzer/tomlplusplus/blob/master/README.md) on GitHub.
/// Otherwise, browse the docs using the links at the top of the page. You can search from anywhere by pressing the TAB key.
/// This is the home of toml++, a header-only [TOML](https://github.com/toml-lang/toml) parser and serializer for C++17 and later.
///
/// \tableofcontents
///
/// <em>Obviously this page is pretty sparse and could do with some more content. If you have concrete suggestions for what
/// should go here, please [let me know](https://github.com/marzer/tomlplusplus/issues)!</em>
///////////////////////////////////////////////////////////////////////
///
/// \section mainpage-features Features
/// - C++17 (plus some C++20 features where supported, e.g. char8_t strings)
/// - Proper UTF-8 handling (incl. BOM)
/// - Works with or without exceptions
/// - Doesn't require RTTI
/// - First-class support for serializing to JSON
/// - Fully [TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md)-compliant
/// - Supports a number of 'unreleased' TOML features (optional; these can be disabled)
///
///////////////////////////////////////////////////////////////////////
///
/// \section mainpage-adding-lib Adding toml++ to your project
/// Clone [the repository](https://github.com/marzer/tomlplusplus/) from GitHub. It's header-only so there's not much you have to do after that,
/// other than some very minor (optional) configuration. See the [README](https://github.com/marzer/tomlplusplus/blob/master/README.md) for more info.
///
///////////////////////////////////////////////////////////////////////
///
/// \section mainpage-api-documentation API Documentation
/// You're looking at it! Browse the docs using the links at the top of the page. You can search from anywhere by pressing the TAB key.
///
/// <em>toml++ is still pretty hot off the presses so there's going to be some omissions, typos and general sparseness throughout the docs.
/// If you spot something or have a suggestion, please [let me know](https://github.com/marzer/tomlplusplus/issues)!</em>
///
///////////////////////////////////////////////////////////////////////
///
/// \section mainpage-example Basic examples
///
///////////////////////////////////
///
/// \subsection mainpage-example-parsing-files Parsing TOML files
/// toml++ works whether you have exceptions enabled or not. For the most part the usage is the same,
/// the main difference being how parsing errors are reported to the caller. When exceptions are enabled
/// a toml::parse_error is thrown directly from the site of the error:
/// \cpp
/// #include <iostream>
/// #include <fstream> //required for parse_file()
/// #include <toml++/toml.h>
/// using namespace std::string_view_literals;
///
/// int main()
/// {
/// toml::table tbl;
/// try
/// {
/// tbl = toml::parse_file("configuration.toml");
/// }
/// catch (const toml::parse_error& err)
/// {
/// std::cerr
/// << "Error parsing file '"sv << *err.source().path
/// << "':\n"sv << err.description()
/// << "\n ("sv << err.source().begin << ")"sv
/// << std::endl;
/// return 1;
/// }
///
/// do_stuff_with_your_config(tbl);
/// return 0;
/// }
///
/// \ecpp
///
/// When exceptions are disabled parsing methods return a toml::parse_error and it is up to the caller
/// to check if parsing has been successful by examining the return value:
/// \cpp
/// #include <iostream>
/// #include <fstream> //required for parse_file()
/// #include <toml++/toml.h>
/// using namespace std::string_view_literals;
///
/// int main()
/// {
/// toml::parse_result tbl = toml::parse_file("configuration.toml");
/// if (!tbl)
/// {
/// std::cerr
/// << "Error parsing file '"sv << *tbl.error().source().path
/// << "':\n"sv << tbl.error().description()
/// << "\n ("sv << tbl.error().source().begin << ")"sv
/// << std::endl;
/// return 1;
/// }
///
/// do_stuff_with_your_config(tbl); //toml::parse_result is convertible to toml::table
/// return 0;
/// }
/// \ecpp
/// \see toml::parse_file()
///
///////////////////////////////////
///
/// \subsection mainpage-example-parsing-strings Parsing TOML directly from strings
///
/// \cpp
/// #include <iostream>
/// #include <toml++/toml.h>
/// using namespace std::string_view_literals;
///
/// int main()
/// {
/// // parse error handling omitted for brevity.
/// static constexpr auto source = R"(
/// [library]
/// name = "toml++"
/// version = "0.1.0"
/// authors = ["Mark Gillard <mark@notarealwebsite.com>"]
///
/// [dependencies]
/// cpp = 17
/// )"sv;
/// auto tbl = toml::parse(source);
/// std::cout << tbl << std::endl;
/// return 0;
/// }
/// \ecpp
///
/// \out
/// [dependencies]
/// cpp = 17
///
/// [library]
/// authors = ["Mark Gillard <mark@notarealwebsite.com>"]
/// name = "toml++"
/// version = "0.1.0"
/// \eout
/// \see toml::parse()
///
///////////////////////////////////
///
/// \subsection mainpage-example-manipulations Traversing and manipulating data
///
/// \cpp
/// #include <iostream>
/// #include <toml++/toml.h>
/// using namespace std::string_view_literals;
///
/// int main()
/// {
/// static constexpr auto source = R"(
/// numbers = [ 1, 2, 3, "four", 5.0 ]
/// vegetables = [ "tomato", "onion", "mushroom", "lettuce" ]
/// minerals = [ "quartz", "iron", "copper", "diamond" ]
///
/// [animals]
/// cats = [ "tiger", "lion", "puma" ]
/// birds = [ "macaw", "pigeon", "canary" ]
/// fish = [ "salmon", "trout", "carp" ]
///
/// )"sv;
/// auto tbl = toml::parse(source);
///
/// auto numbers = tbl["numbers"];
/// std::cout << "table has 'numbers': "sv << !!numbers << std::endl;
/// if (numbers)
/// {
/// std::cout << "'numbers' is a: "sv << numbers.type() << std::endl;
/// std::cout << "'numbers': "sv << numbers << std::endl;
/// for (auto& node : *numbers.as_array())
/// {
/// node.visit([=](auto&& n) noexcept
/// {
/// if constexpr (toml::is_number<decltype(n)>)
/// (*n)++;
/// else if constexpr (toml::is_string<decltype(n)>)
/// n = "five"sv;
/// });
/// }
/// numbers.as_array()->push_back(7);
/// numbers.as_array()->emplace_back<toml::array>(8, 9);
/// std::cout << "'numbers': "sv << numbers << std::endl;
/// }
///
/// std::cout << "'cats': "sv << tbl["animals"]["cats"] << std::endl;
/// std::cout << "'dinosaurs': "sv << tbl["animals"]["dinosaurs"] << std::endl; //no dinosaurs :(
///
/// return 0;
/// }
/// \ecpp
///
/// \out
/// table has 'numbers': true
/// 'numbers' is an: array
/// 'numbers': [1, 2, 3, "four", 5.0]
/// 'numbers': [2, 3, 4, "five", 6.0, 7, [8, 9]]
/// 'cats': ["tiger", "lion", "puma"]
/// 'dinosaurs':
/// \eout
///
/// \see toml::node, toml::node_view, toml::array, toml::table
///
///////////////////////////////////
///
/// \subsection mainpage-example-serialization Serializing as TOML and JSON
/// \cpp
/// #include <iostream>
/// #include <toml++/toml.h>
///
/// int main()
/// {
/// auto tbl = toml::table{{
/// { "lib", "toml++" },
/// { "cpp", toml::array{ 17, 20, "and beyond" } },
@@ -82,21 +275,62 @@
/// }}
/// },
/// }};
///
/// std::cout << "###### TOML ######"sv << std::endl;
/// std::cout << tbl << std::endl << std::endl;
///
/// std::cout << tbl << std::endl;
/// std::cout << "###### JSON ######"sv << std::endl;
/// std::cout << toml::json_formatter{ tbl } << std::endl;
/// return 0;
/// }
/// \ecpp
///
///
/// \out
/// cpp = [ 17, 20, "and beyond" ]
/// ###### TOML ######
/// cpp = [17, 20, "and beyond"]
/// lib = "toml++"
/// repo = "https://github.com/marzer/tomlplusplus/"
/// toml = [ "0.5.0", "and beyond" ]
/// toml = ["0.5.0", "and beyond"]
///
/// [author]
/// github = "https://github.com/marzer"
/// name = "Mark Gillard"
/// twitter = "https://twitter.com/marzer8789"
/// \eout
///
/// ###### JSON ######
/// {
/// "author" : {
/// "github" : "https://github.com/marzer",
/// "name" : "Mark Gillard",
/// "twitter" : "https://twitter.com/marzer8789"
/// },
/// "cpp" : [
/// 17,
/// 20,
/// "and beyond"
/// ],
/// "lib" : "toml++",
/// "repo" : "https://github.com/marzer/tomlplusplus/",
/// "toml" : [
/// "0.5.0",
/// "and beyond"
/// ]
/// }
/// \eout
/// \see toml::default_formatter, toml::json_formatter
///
///////////////////////////////////////////////////////////////////////
///
/// \section mainpage-contributing Contributing
/// See the [Contributing](https://github.com/marzer/tomlplusplus/blob/master/README.md#contributing) section of the repository README.
///
///////////////////////////////////////////////////////////////////////
///
/// \section mainpage-license License
///
/// toml++ is licensed under the terms of the MIT license - see [LICENSE](https://github.com/marzer/tomlplusplus/blob/master/LICENSE).
///
/// UTF-8 decoding is performed using a state machine based on Bjoern Hoehrmann's 'Flexible and Economical UTF - 8 Decoder', which is also subject
/// to the terms of the MIT license - see [LICENSE-utf8-decoder](https://github.com/marzer/tomlplusplus/blob/master/LICENSE-utf8-decoder).
///
///

View File

@@ -178,15 +178,12 @@ namespace toml::impl
namespace toml
{
[[nodiscard]] bool operator == (const table& lhs, const table& rhs) noexcept;
[[nodiscard]] bool operator != (const table& lhs, const table& rhs) noexcept;
/// \brief A TOML array.
///
/// \remarks The interface of this type is modeled after std::vector, with some
/// \detail The interface of this type is modeled after std::vector, with some
/// additional considerations made for the heterogeneous nature of a
/// TOML array.
///
/// \detail \cpp
/// TOML array. \cpp
///
/// auto tbl = toml::parse("arr = [1, 2, 3, 4, 'five']"sv);
/// auto& arr = *tbl.get_as<toml::array>("arr");
@@ -196,9 +193,9 @@ namespace toml
/// {
/// arr[i].visit([=](auto&& el) noexcept
/// {
/// if constexpr (toml::is_integer<decltype(el)>)
/// if constexpr (toml::is_number<decltype(el)>)
/// (*el)++;
/// else
/// else if constexpr (toml::is_string<decltype(el)>)
/// el = "six"sv;
/// });
/// }
@@ -303,6 +300,9 @@ namespace toml
return *this;
}
array(const array&) = delete;
array& operator= (const array&) = delete;
/// \brief Always returns node_type::array for array nodes.
[[nodiscard]] node_type type() const noexcept override { return node_type::array; }
/// \brief Always returns `false` for array nodes.
@@ -807,6 +807,31 @@ namespace toml
private:
template <typename T>
[[nodiscard]] static bool container_equality(const array& lhs, const T& rhs) noexcept
{
using elem_t = std::remove_const_t<typename T::value_type>;
static_assert(
impl::is_value_or_promotable<elem_t>,
"Container element type must be (or be promotable to) one of the TOML value types"
);
if (lhs.size() != rhs.size())
return false;
if (rhs.size() == 0_sz)
return true;
size_t i{};
for (auto& list_elem : rhs)
{
const auto elem = lhs.get_as<impl::promoted<elem_t>>(i++);
if (!elem || *elem != list_elem)
return false;
}
return true;
}
[[nodiscard]] size_t total_leaf_count() const noexcept
{
size_t leaves{};
@@ -836,6 +861,22 @@ namespace toml
public:
/// \brief Initializer list equality operator.
template <typename T>
[[nodiscard]] friend bool operator == (const array& lhs, const std::initializer_list<T>& rhs) noexcept
{
return container_equality(lhs, rhs);
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const array&, const std::initializer_list<T>&, template <typename T>)
/// \brief Vector equality operator.
template <typename T>
[[nodiscard]] friend bool operator == (const array& lhs, const std::vector<T>& rhs) noexcept
{
return container_equality(lhs, rhs);
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const array&, const std::vector<T>&, template <typename T>)
/// \brief Flattens this array, recursively hoisting the contents of child arrays up into itself.
///
/// \detail \cpp

View File

@@ -90,6 +90,10 @@
#define TOML_INTERFACE __declspec(novtable)
#define TOML_EMPTY_BASES __declspec(empty_bases)
#if !defined(TOML_RELOPS_REORDERING) && defined(__cpp_impl_three_way_comparison)
#define TOML_RELOPS_REORDERING 1
#endif
#elif defined(__GNUC__)
#ifndef __cpp_exceptions
@@ -114,6 +118,10 @@
#define TOML_USE_STREAMS_FOR_FLOATS 1
#endif
#if !defined(TOML_RELOPS_REORDERING) && defined(__cpp_impl_three_way_comparison)
#define TOML_RELOPS_REORDERING 1
#endif
#endif
#ifndef TOML_CPP_VERSION
@@ -209,6 +217,17 @@
#ifndef TOML_NODISCARD_CTOR
#define TOML_NODISCARD_CTOR
#endif
#ifndef TOML_RELOPS_REORDERING
#define TOML_RELOPS_REORDERING 0
#endif
#if TOML_RELOPS_REORDERING
#define TOML_ASYMMETRICAL_EQUALITY_OPS(...)
#else
#define TOML_ASYMMETRICAL_EQUALITY_OPS(LHS, RHS, ...) \
__VA_ARGS__ [[nodiscard]] friend bool operator == (RHS rhs, LHS lhs) noexcept { return lhs == rhs; } \
__VA_ARGS__ [[nodiscard]] friend bool operator != (LHS lhs, RHS rhs) noexcept { return !(lhs == rhs); } \
__VA_ARGS__ [[nodiscard]] friend bool operator != (RHS rhs, LHS lhs) noexcept { return !(lhs == rhs); }
#endif
#include "toml_version.h"
@@ -217,10 +236,10 @@
#if TOML_UNRELEASED_FEATURES
#define TOML_LANG_EFFECTIVE_VERSION \
TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_REVISION+1)
TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH+1)
#else
#define TOML_LANG_EFFECTIVE_VERSION \
TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_REVISION)
TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH)
#endif
#define TOML_LANG_HIGHER_THAN(maj, min, rev) \
@@ -232,6 +251,7 @@
#define TOML_LANG_EXACTLY(maj, min, rev) \
(TOML_LANG_EFFECTIVE_VERSION == TOML_MAKE_VERSION(maj, min, rev))
////////// INCLUDES
TOML_PUSH_WARNINGS
@@ -321,6 +341,7 @@ namespace toml
/// \brief TOML node type identifiers.
enum class node_type : uint8_t
{
none, ///< Not-a-node.
table, ///< The node is a toml::table.
array, ///< The node is a toml::array.
string, ///< The node is a toml::value<toml::string>.
@@ -745,7 +766,7 @@ namespace toml::impl
template <> struct node_type_of_<date_time> { static constexpr auto value = node_type::date_time; };
template <typename T>
inline constexpr auto node_type_of = node_type_of_<promoted<typename node_unwrapper<T>::type>>::value;
inline constexpr auto node_type_of = node_type_of_<promoted<typename node_unwrapper<remove_cvref_t<T>>::type>>::value;
inline constexpr toml::string_view low_character_escape_table[] =
{
@@ -785,6 +806,7 @@ namespace toml::impl
inline constexpr std::string_view node_type_friendly_names[] =
{
"none"sv,
"table"sv,
"array"sv,
"string"sv,
@@ -836,6 +858,9 @@ namespace toml
/// \brief Metafunction for determining if a type is a double or toml::value<double>.
template <typename T>
inline constexpr bool is_floating_point = std::is_same_v<impl::node_of<impl::remove_cvref_t<T>>, value<double>>;
/// \brief Metafunction for determining if a type satisfies `toml::is_integer || toml::is_floating_point`.
template <typename T>
inline constexpr bool is_number = is_integer<T> || is_floating_point<T>;
/// \brief Metafunction for determining if a type is a bool toml::value<bool>.
template <typename T>
inline constexpr bool is_boolean = std::is_same_v<impl::node_of<impl::remove_cvref_t<T>>, value<bool>>;

View File

@@ -13,7 +13,6 @@ namespace toml
/// \brief The day component, from 1 - 31.
uint8_t day;
/// \brief Equality operator.
///
/// \param lhs The LHS date.
@@ -44,6 +43,13 @@ namespace toml
/// \brief Prints a date out to a stream as `YYYY-MM-DD` (per RFC 3339).
/// \detail \cpp
/// std::cout << toml::date{ 1987, 3, 16 } << std::endl;
/// \ecpp
///
/// \out
/// 1987-03-16
/// \eout
template <typename CHAR>
friend inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const date& rhs)
TOML_MAY_THROW
@@ -93,6 +99,15 @@ namespace toml
}
/// \brief Prints a time out to a stream as `HH:MM:SS.FFFFFF` (per RFC 3339).
/// \detail \cpp
/// std::cout << toml::time{ 10, 20, 34 } << std::endl;
/// std::cout << toml::time{ 10, 20, 34, 500000000 } << std::endl;
/// \ecpp
///
/// \out
/// 10:20:34
/// 10:20:34.5
/// \eout
template <typename CHAR>
friend inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const time& rhs)
TOML_MAY_THROW
@@ -108,13 +123,19 @@ namespace toml
/// \brief Offset from UTC+0, in minutes.
int16_t minutes;
/// \brief Creates a timezone offset from separate hour and minute totals.
/// \brief Default-constructs a zero time-offset.
TOML_NODISCARD_CTOR
constexpr time_offset() noexcept
: minutes{}
{}
/// \brief Constructs a timezone offset from separate hour and minute totals.
///
/// \detail \cpp
/// std::cout << time_offset::from_hh_mm(2, 30) << std::endl;
/// std::cout << time_offset::from_hh_mm(-2, 30) << std::endl;
/// std::cout << time_offset::from_hh_mm(-2, -30) << std::endl;
/// std::cout << time_offset::from_hh_mm(0,0) << std::endl;
/// std::cout << toml::time_offset{ 2, 30 } << std::endl;
/// std::cout << toml::time_offset{ -2, 30 } << std::endl;
/// std::cout << toml::time_offset{ -2, -30 } << std::endl;
/// std::cout << toml::time_offset{ 0, 0 } << std::endl;
///
/// \ecpp
///
@@ -129,11 +150,10 @@ namespace toml
/// \param minutes The total minutes.
///
/// \returns A time_offset.
[[nodiscard]]
static constexpr time_offset from_hh_mm(int8_t hours, int8_t minutes) noexcept
{
return time_offset{ static_cast<int16_t>(hours * 60 + minutes) };
}
TOML_NODISCARD_CTOR
constexpr time_offset(int8_t hours, int8_t minutes) noexcept
: minutes{ static_cast<int16_t>(hours * 60 + minutes) }
{}
/// \brief Equality operator.
///
@@ -160,6 +180,21 @@ namespace toml
}
/// \brief Prints a time_offset out to a stream as `+-HH:MM or Z` (per RFC 3339).
/// \detail \cpp
/// std::cout << toml::time_offset{ 2, 30 } << std::endl;
/// std::cout << toml::time_offset{ 2, -30 } << std::endl;
/// std::cout << toml::time_offset{} << std::endl;
/// std::cout << toml::time_offset{ -2, 30 } << std::endl;
/// std::cout << toml::time_offset{ -2, -30 } << std::endl;
/// \ecpp
///
/// \out
/// +02:30
/// +01:30
/// Z
/// -01:30
/// -02:30
/// \eout
template <typename CHAR>
friend inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const time_offset& rhs)
TOML_MAY_THROW
@@ -181,6 +216,35 @@ namespace toml
/// \remarks The date_time is said to be 'local' if the time_offset is empty.
std::optional<toml::time_offset> time_offset;
/// \brief Default-constructs a zero date-time.
TOML_NODISCARD_CTOR
constexpr date_time() noexcept
: date{},
time{}
{}
/// \brief Constructs a local date-time.
///
/// \param d The date component.
/// \param t The time component.
TOML_NODISCARD_CTOR
constexpr date_time(toml::date d, toml::time t) noexcept
: date{ d },
time{ t }
{}
/// \brief Constructs an offset date-time.
///
/// \param d The date component.
/// \param t The time component.
/// \param offset The timezone offset.
TOML_NODISCARD_CTOR
constexpr date_time(toml::date d, toml::time t, toml::time_offset offset) noexcept
: date{ d },
time{ t },
time_offset{ offset }
{}
/// \brief Returns true if this date_time does not contain timezone offset information.
[[nodiscard]]
constexpr bool is_local() const noexcept
@@ -217,6 +281,17 @@ namespace toml
}
/// \brief Prints a date_time out to a stream in RFC 3339 format.
/// \detail \cpp
/// std::cout << toml::date_time{ { 1987, 3, 16 }, { 10, 20, 34 } } << std::endl;
/// std::cout << toml::date_time{ { 1987, 3, 16 }, { 10, 20, 34 }, { -2, -30 } } << std::endl;
/// std::cout << toml::date_time{ { 1987, 3, 16 }, { 10, 20, 34 }, {} } << std::endl;
/// \ecpp
///
/// \out
/// 1987-03-16T10:20:34
/// 1987-03-16T10:20:34-02:30
/// 1987-03-16T10:20:34Z
/// \eout
template <typename CHAR>
friend inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const date_time& rhs)
TOML_MAY_THROW

View File

@@ -82,9 +82,9 @@ namespace toml
/// \brief Checks if a node is a specific type.
///
/// \tparam T The
/// \tparam T A TOML node or value type.
///
/// \returns Returns true if this node is an instance
/// \returns Returns true if this node is an instance of the specified type.
template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE
bool is() const noexcept

View File

@@ -3,191 +3,173 @@
#include "toml_array.h"
#include "toml_value.h"
namespace toml::impl
{
template <typename T>
struct node_view_traits;
template <>
struct node_view_traits<const table>
{
using haystack_type = const table*;
using key_type = string_view;
[[nodiscard]] static const node* get(const table* tbl, key_type key) noexcept
{
return tbl->get(key);
}
template <typename U>
[[nodiscard]] static const node_of<U>* as(const table* tbl, key_type key) noexcept
{
return tbl->get_as<U>(key);
}
};
template <>
struct node_view_traits<table>
{
using haystack_type = table*;
using key_type = string_view;
[[nodiscard]] static node* get(table* tbl, key_type key) noexcept
{
return tbl->get(key);
}
[[nodiscard]] static const node* get(const table* tbl, key_type key) noexcept
{
return tbl->get(key);
}
template <typename T>
[[nodiscard]] static node_of<T>* as(table* tbl, key_type key) noexcept
{
return tbl->get_as<T>(key);
}
template <typename T>
[[nodiscard]] static const node_of<T>* as(const table* tbl, key_type key) noexcept
{
return tbl->get_as<T>(key);
}
};
template <typename T, typename K>
struct sub_view final { };
template <typename T>
struct node_view_traits<sub_view<T, string_view>>
{
using haystack_type = T;
using key_type = string_view;
[[nodiscard]] static auto get(haystack_type& view, string_view key) noexcept
{
auto parent = view.as_table();
return parent ? parent->get(key) : nullptr;
}
[[nodiscard]] static const node* get(const haystack_type& view, string_view key) noexcept
{
auto parent = view.as_table();
return parent ? parent->get(key) : nullptr;
}
template <typename U>
[[nodiscard]] static auto as(haystack_type& view, string_view key) noexcept
{
auto parent = view.as_table();
return parent ? parent->template get_as<U>(key) : nullptr;
}
template <typename U>
[[nodiscard]] static const node_of<U>* as(const haystack_type& view, string_view key) noexcept
{
auto parent = view.as_table();
return parent ? parent->template get_as<U>(key) : nullptr;
}
};
template <typename T>
struct node_view_traits<sub_view<T, size_t>>
{
using haystack_type = T;
using key_type = size_t;
[[nodiscard]] static auto get(haystack_type& view, size_t index) noexcept
{
auto parent = view.as_array();
return parent ? parent->get(index) : nullptr;
}
[[nodiscard]] static const node* get(const haystack_type& view, size_t index) noexcept
{
auto parent = view.as_array();
return parent ? parent->get(index) : nullptr;
}
template <typename U>
[[nodiscard]] static auto as(haystack_type& view, size_t index) noexcept
{
auto parent = view.as_array();
return parent ? parent->template get_as<U>(index) : nullptr;
}
template <typename U>
[[nodiscard]] static const node_of<U>* as(const haystack_type& view, size_t index) noexcept
{
auto parent = view.as_array();
return parent ? parent->template get_as<U>(index) : nullptr;
}
};
}
namespace toml
{
/// \brief A read-only view into a node.
/// \brief A view of a node.
///
/// \detail A node_view is like a std::optional<toml::node> with lots of toml-specific stuff built-in.
/// It _may_ represent a node, and allows you to do many of the same operations that you'd do
/// on nodes directly, as well as easily traversing the node tree by creating
/// subviews (via node_view::operator[]). \cpp
///
/// auto tbl = toml::parse(R"(
///
/// title = "my hardware store"
///
/// [[products]]
/// name = "Hammer"
/// sku = 738594937
/// keywords = [ "hammer", "construction", "build" ]
///
/// \warning This type is experimental. Functionality may change radically between versions
/// until I decide that I'm happy with it. Use it at your own risk.
/// [[products]]
/// name = "Nail"
/// sku = 284758393
/// color = "gray"
///
/// )"sv);
///
/// std::cout << tbl["title"] << std::endl;
/// std::cout << tbl["products"][0]["name"] << std::endl;
/// std::cout << tbl["products"][0]["keywords"] << std::endl;
/// std::cout << tbl["products"][0]["keywords"][2] << std::endl;
///
/// tbl["products"][0]["keywords"].as_array()->push_back("heavy");
/// std::cout << tbl["products"][0]["keywords"] << std::endl;
/// std::cout << "has third product: "sv << !!tbl["products"][2] << std::endl;
/// std::cout << tbl["products"][2] << std::endl; // no-op
///
/// \ecpp
///
/// \out
/// "my hardware store"
/// "Hammer"
/// [ "hammer", "construction", "build" ]
/// [ "hammer", "construction", "build", "heavy" ]
/// has third product: false
/// \eout
template <typename T>
class node_view final
{
public:
using traits = impl::node_view_traits<T>;
using key_type = typename traits::key_type;
using viewed_type = T;
private:
using haystack_type = typename traits::haystack_type;
haystack_type haystack_;
key_type key_;
friend class toml::table;
viewed_type* node_;
TOML_NODISCARD_CTOR
node_view(viewed_type* node) noexcept
: node_{ node }
{}
public:
TOML_NODISCARD_CTOR
node_view(haystack_type obj, key_type key) noexcept
: haystack_{ obj },
key_{ key }
{}
/// \brief Returns true if the view references a node.
[[nodiscard]] explicit operator bool() const noexcept { return node_ != nullptr; }
/// \brief Returns the node that's being referenced by the view.
[[nodiscard]] viewed_type* get() noexcept { return node_; }
/// \brief Returns the node that's being referenced by the view (const overload).
[[nodiscard]] const viewed_type* get() const noexcept { return node_; }
[[nodiscard]] auto get() noexcept { return traits::get(haystack_, key_); }
[[nodiscard]] const node* get() const noexcept { return traits::get(haystack_, key_); }
[[nodiscard]] explicit operator bool() const noexcept { return !!get(); }
/// \brief Returns the type identifier for the viewed node.
[[nodiscard]] node_type type() const noexcept { return node_ ? node_->type() : node_type::none; }
/// \brief Returns true if the viewed node is a toml::table.
[[nodiscard]] bool is_table() const noexcept { return type() == node_type::table; }
/// \brief Returns true if the viewed node is a toml::array.
[[nodiscard]] bool is_array() const noexcept { return type() == node_type::array; }
/// \brief Returns true if the viewed node is a toml::value<>.
[[nodiscard]] bool is_value() const noexcept { return type() > node_type::array; }
/// \brief Returns true if the viewed node is a toml::value<string>.
[[nodiscard]] bool is_string() const noexcept { return type() == node_type::string; }
/// \brief Returns true if the viewed node is a toml::value<int64_t>.
[[nodiscard]] bool is_integer() const noexcept { return type() == node_type::integer; }
/// \brief Returns true if the viewed node is a toml::value<double>.
[[nodiscard]] bool is_floating_point() const noexcept { return type() == node_type::floating_point; }
/// \brief Returns true if the viewed node is a toml::value<bool>.
[[nodiscard]] bool is_boolean() const noexcept { return type() == node_type::boolean; }
/// \brief Returns true if the viewed node is a toml::value<date>.
[[nodiscard]] bool is_date() const noexcept { return type() == node_type::date; }
/// \brief Returns true if the viewed node is a toml::value<time>.
[[nodiscard]] bool is_time() const noexcept { return type() == node_type::time; }
/// \brief Returns true if the viewed node is a toml::value<date_time>.
[[nodiscard]] bool is_date_time() const noexcept { return type() == node_type::date_time; }
/// \brief Returns true if the viewed node is a toml::array that contains only tables.
[[nodiscard]] bool is_array_of_tables() const noexcept
{
return node_ ? node_->is_array_of_tables() : false;
}
/// \brief Checks if this view references a node of a specific type.
///
/// \tparam U A TOML node or value type.
///
/// \returns Returns true if the viewed node is an instance of the specified type.
///
/// \see toml::node::is()
template <typename U>
[[nodiscard]] auto as() noexcept
[[nodiscard]]
bool is() const noexcept
{
return node_ ? node_->template is<U>() : false;
}
/// \brief Gets a pointer to the viewed node as a more specific node type.
///
/// \tparam U The node type or TOML value type to cast to.
///
/// \returns A pointer to the node as the given type, or nullptr if it was a different type.
///
/// \see toml::node::as()
template <typename U>
[[nodiscard]]
auto as() noexcept
{
static_assert(
impl::is_value_or_node<impl::unwrapped<U>>,
"Template type parameter must be one of the basic value types, a toml::table, or a toml::array"
);
return traits::template as<U>(haystack_, key_);
return node_ ? node_->template as<U>() : nullptr;
}
/// \brief Gets a pointer to the viewed node as a more specific node type (const overload).
template <typename U>
[[nodiscard]] const impl::node_of<U>* as() const noexcept
[[nodiscard]]
const impl::node_of<U>* as() const noexcept
{
static_assert(
impl::is_value_or_node<impl::unwrapped<U>>,
"Template type parameter must be one of the basic value types, a toml::table, or a toml::array"
);
return traits::template as<U>(haystack_, key_);
return node_ ? node_->template as<U>() : nullptr;
}
[[nodiscard]] auto as_string() noexcept { return as<string>(); }
[[nodiscard]] auto as_integer() noexcept { return as<int64_t>(); }
[[nodiscard]] auto as_floating_point() noexcept { return as<double>(); }
[[nodiscard]] auto as_boolean() noexcept { return as<bool>(); }
[[nodiscard]] auto as_date() noexcept { return as<date>(); }
[[nodiscard]] auto as_time() noexcept { return as<time>(); }
[[nodiscard]] auto as_date_time() noexcept { return as<date_time>(); }
[[nodiscard]] auto as_array() noexcept { return as<array>(); }
/// \brief Returns a pointer to the viewed node as a toml::table, if it is one.
[[nodiscard]] auto as_table() noexcept { return as<table>(); }
/// \brief Returns a pointer to the viewed node as a toml::array, if it is one.
[[nodiscard]] auto as_array() noexcept { return as<array>(); }
/// \brief Returns a pointer to the viewed node as a toml::value<string>, if it is one.
[[nodiscard]] auto as_string() noexcept { return as<string>(); }
/// \brief Returns a pointer to the viewed node as a toml::value<int64_t>, if it is one.
[[nodiscard]] auto as_integer() noexcept { return as<int64_t>(); }
/// \brief Returns a pointer to the viewed node as a toml::value<double>, if it is one.
[[nodiscard]] auto as_floating_point() noexcept { return as<double>(); }
/// \brief Returns a pointer to the viewed node as a toml::value<bool>, if it is one.
[[nodiscard]] auto as_boolean() noexcept { return as<bool>(); }
/// \brief Returns a pointer to the viewed node as a toml::value<date>, if it is one.
[[nodiscard]] auto as_date() noexcept { return as<date>(); }
/// \brief Returns a pointer to the viewed node as a toml::value<time>, if it is one.
[[nodiscard]] auto as_time() noexcept { return as<time>(); }
/// \brief Returns a pointer to the viewed node as a toml::value<date_time>, if it is one.
[[nodiscard]] auto as_date_time() noexcept { return as<date_time>(); }
[[nodiscard]] const table* as_table() const noexcept { return as<table>(); }
[[nodiscard]] const array* as_array() const noexcept { return as<array>(); }
[[nodiscard]] const value<string>* as_string() const noexcept { return as<string>(); }
[[nodiscard]] const value<int64_t>* as_integer() const noexcept { return as<int64_t>(); }
[[nodiscard]] const value<double>* as_floating_point() const noexcept { return as<double>(); }
@@ -195,97 +177,174 @@ namespace toml
[[nodiscard]] const value<date>* as_date() const noexcept { return as<date>(); }
[[nodiscard]] const value<time>* as_time() const noexcept { return as<time>(); }
[[nodiscard]] const value<date_time>* as_date_time() const noexcept { return as<date_time>(); }
[[nodiscard]] const array* as_array() const noexcept { return as<array>(); }
[[nodiscard]] const table* as_table() const noexcept { return as<table>(); }
private:
template <typename U>
[[nodiscard]] static bool value_equality(const node_view& lhs, const U& rhs) noexcept
template <typename N, typename FUNC>
static decltype(auto) do_visit(N* node, FUNC&& visitor)
TOML_MAY_THROW_UNLESS(noexcept(std::declval<N*>()->visit(std::declval<FUNC&&>())))
{
const auto val = lhs.as<impl::promoted<U>>();
return val && val->get() == rhs;
using return_type = decltype(node->visit(std::forward<FUNC>(visitor)));
if (node)
return node->visit(std::forward<FUNC>(visitor));
if constexpr (!std::is_void_v<return_type>)
return return_type{};
}
template <typename U>
[[nodiscard]] static bool container_equality(const node_view& lhs, const U& rhs) noexcept
{
using elem_t = std::remove_const_t<typename U::value_type>;
static_assert(
impl::is_value_or_promotable<elem_t>,
"Container element type must be (or be promotable to) one of the TOML value types"
);
const array* arr = lhs.as<array>();
if (!arr || arr->size() != rhs.size())
return false;
if (rhs.size() == 0_sz)
return true;
size_t i{};
for (auto& list_elem : rhs)
{
const auto elem = arr->get_as<impl::promoted<elem_t>>(i++);
if (!elem || elem->get() != list_elem)
return false;
}
return true;
}
template <typename FUNC, typename N>
static constexpr bool visit_is_nothrow =
noexcept(do_visit(std::declval<N*>(), std::declval<FUNC&&>()));
public:
[[nodiscard]] bool operator == (string_view rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (int64_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (int32_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (int16_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (int8_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (uint32_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (uint16_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (uint8_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (double rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (float rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (bool rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (const date& rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (const time& rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (const date_time& rhs) const noexcept { return value_equality(*this, rhs); }
/// \brief Invokes a visitor on the viewed node based on its concrete type.
///
/// \remarks Has no effect if the view does not reference a node.
///
/// \see node::visit()
template <typename FUNC>
decltype(auto) visit(FUNC&& visitor)
TOML_MAY_THROW_UNLESS(visit_is_nothrow<FUNC&&, viewed_type>)
{
return do_visit(node_, std::forward<FUNC>(visitor));
}
/// \brief Invokes a visitor on the viewed node based on its concrete type (const overload).
///
/// \remarks Has no effect if the view does not reference a node.
///
/// \see node::visit()
template <typename FUNC>
decltype(auto) visit(FUNC&& visitor) const
TOML_MAY_THROW_UNLESS(visit_is_nothrow<FUNC&&, const viewed_type>)
{
return do_visit(node_, std::forward<FUNC>(visitor));
}
/// \brief Returns true if the viewed node is a table with the same contents as RHS.
[[nodiscard]] friend bool operator == (const node_view& lhs, const table& rhs) noexcept
{
if (lhs.node_ == &rhs)
return true;
const auto tbl = lhs.as<table>();
return tbl && *tbl == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const table&, )
/// \brief Returns true if the viewed node is an array with the same contents as RHS.
[[nodiscard]] friend bool operator == (const node_view& lhs, const array& rhs) noexcept
{
if (lhs.node_ == &rhs)
return true;
const auto arr = lhs.as<array>();
return arr && *arr == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const array&, )
/// \brief Returns true if the viewed node is a value with the same value as RHS.
template <typename U>
[[nodiscard]] bool operator == (const std::initializer_list<U>& rhs) const noexcept
[[nodiscard]] friend bool operator == (const node_view& lhs, const value<U>& rhs) noexcept
{
return container_equality(*this, rhs);
if (lhs.node_ == &rhs)
return true;
const auto val = lhs.as<U>();
return val && *val == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const value<U>&, template <typename U>)
/// \brief Returns true if the viewed node is a value with the same value as RHS.
template <typename U, typename = std::enable_if_t<impl::is_value_or_promotable<U>>>
[[nodiscard]] friend bool operator == (const node_view& lhs, const U& rhs) noexcept
{
const auto val = lhs.as<impl::promoted<U>>();
return val && *val == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(
const node_view&,
const U&,
template <typename U, typename = std::enable_if_t<impl::is_value_or_promotable<U>>>
)
/// \brief Returns true if the viewed node is an array with the same contents as the RHS initializer list.
template <typename U>
[[nodiscard]] bool operator == (const std::vector<U>& rhs) const noexcept
[[nodiscard]] friend bool operator == (const node_view& lhs, const std::initializer_list<U>& rhs) noexcept
{
return container_equality(*this, rhs);
const auto arr = lhs.as<array>();
return arr && *arr == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const std::initializer_list<U>&, template <typename U>)
/// \brief Returns true if the viewed node is an array with the same contents as the RHS vector.
template <typename U>
[[nodiscard]] friend bool operator == (const U& lhs, const node_view& rhs) noexcept
[[nodiscard]] friend bool operator == (const node_view& lhs, const std::vector<U>& rhs) noexcept
{
return rhs == lhs;
const auto arr = lhs.as<array>();
return arr && *arr == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const std::vector<U>&, template <typename U>)
/// \brief Returns a view of the selected subnode.
///
/// \param key The key of the node to retrieve
///
/// \returns A view of the selected node if this node represented a table and it contained a
/// value at the given key, or an empty view.
[[nodiscard]] node_view<viewed_type> operator[] (string_view key) noexcept
{
if (auto tbl = this->as_table())
return { tbl->get(key) };
return { nullptr };
}
[[nodiscard]] node_view<impl::sub_view<node_view<T>, string_view>> operator[] (string_view key) noexcept
/// \brief Returns a view of the selected subnode.
///
/// \param index The index of the node to retrieve
///
/// \returns A view of the selected node if this node represented an array and it contained a
/// value at the given index, or an empty view.
[[nodiscard]] node_view<viewed_type> operator[] (size_t index) noexcept
{
return { *this, key };
if (auto tbl = this->as_array())
return { tbl->get(index) };
return { nullptr };
}
[[nodiscard]] node_view<impl::sub_view<node_view<T>, size_t>> operator[] (size_t index) noexcept
/// \brief Returns a view of the selected subnode (const overload).
[[nodiscard]] node_view<const viewed_type> operator[] (string_view key) const noexcept
{
return { *this, index };
if (auto tbl = this->as_table())
return { tbl->get(key) };
return { nullptr };
}
/// \brief Returns a view of the selected subnode (const overload).
[[nodiscard]] node_view<const viewed_type> operator[] (size_t index) const noexcept
{
if (auto tbl = this->as_array())
return { tbl->get(index) };
return { nullptr };
}
/// \brief Prints the viewed node out to a stream.
template <typename CHAR>
friend std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& os, const node_view& nv) TOML_MAY_THROW
{
nv.visit([&](const auto& node) TOML_MAY_THROW
{
os << node;
});
return os;
}
};
inline node_view<table> table::operator[] (string_view key) noexcept
inline node_view<node> table::operator[] (string_view key) noexcept
{
return { this, key };
return { this->get(key) };
}
inline node_view<const table> table::operator[] (string_view key) const noexcept
inline node_view<const node> table::operator[] (string_view key) const noexcept
{
return { this, key };
return { this->get(key) };
}
}

View File

@@ -597,6 +597,7 @@ namespace toml::impl
case U'n': str += TOML_STRING_PREFIX('\n'); break;
case U'r': str += TOML_STRING_PREFIX('\r'); break;
#if 0
case U's':
{
if constexpr (!TOML_LANG_HIGHER_THAN(0, 5, 0)) // toml/issues/622
@@ -612,6 +613,7 @@ namespace toml::impl
break;
}
}
#endif
case U't': str += TOML_STRING_PREFIX('\t'); break;
case U'"': str += TOML_STRING_PREFIX('"'); break;
@@ -1925,7 +1927,8 @@ namespace toml::impl
" offset; expected minute between 0 and 59 (inclusive), saw "sv, hour
);
offset.emplace(time_offset{ static_cast<int16_t>((hour * 60 + minute) * sign) });
offset.emplace();
offset->minutes = static_cast<int16_t>((hour * 60 + minute) * sign);
}
}
@@ -1937,11 +1940,10 @@ namespace toml::impl
);
TOML_ERROR_CHECK({});
return {
date,
time,
offset
};
if (offset)
return { date, time, *offset };
else
return { date, time };
}
// TOML_DISABLE_SWITCH_WARNINGS

View File

@@ -123,7 +123,6 @@ namespace toml
{
private:
friend class impl::parser;
friend class node_view<table>;
impl::string_map<std::unique_ptr<node>> values;
bool inline_ = false;
@@ -198,6 +197,9 @@ namespace toml
return *this;
}
table(const table&) = delete;
table& operator= (const table&) = delete;
/// \brief Always returns `node_type::table` for table nodes.
[[nodiscard]] node_type type() const noexcept override { return node_type::table; }
/// \brief Always returns `true` for table nodes.
@@ -256,17 +258,17 @@ namespace toml
///
/// \param key The key used for the lookup.
///
/// \returns A node_view.
/// \returns A view of the value at the given key if one existed, or an empty node view.
///
/// \remarks std::map::operator[]'s behaviour of default-constructing a value at a key if it
/// didn't exist is a crazy bug factory so I've deliberately chosen not to emulate it.
/// <strong>This is not an error.</strong>
///
/// \see toml::node_view
[[nodiscard]] inline node_view<table> operator[] (string_view key) noexcept;
[[nodiscard]] inline node_view<node> operator[] (string_view key) noexcept;
/// \brief Gets a node_view for the selected key-value pair (const overload).
[[nodiscard]] inline node_view<const table> operator[] (string_view key) const noexcept;
[[nodiscard]] inline node_view<const node> operator[] (string_view key) const noexcept;
/// \brief Returns an iterator to the first key-value pair.
[[nodiscard]] iterator begin() noexcept { return { values.begin() }; }
@@ -302,7 +304,7 @@ namespace toml
/// for (auto k : { "a", "d" })
/// {
/// auto result = tbl.insert(k, 42);
/// std::cout << "inserted a value with key '"sv << k << "': "sv << result.second << std::endl;
/// std::cout << "inserted with key '"sv << k << "': "sv << result.second << std::endl;
/// }
/// std::cout << tbl << std::endl;
///
@@ -310,8 +312,8 @@ namespace toml
///
/// \out
/// { a = 1, b = 2, c = 3 }
/// inserted a value with key 'a': false
/// inserted a value with key 'd': true
/// inserted with key 'a': false
/// inserted with key 'd': true
/// { a = 1, b = 2, c = 3, d = 42 }
/// \eout
///
@@ -365,7 +367,7 @@ namespace toml
/// \param first An iterator to the first value in the input collection.
/// \param last An iterator to one-past-the-last value in the input collection.
///
/// \remarks This function is morally equivalent to calling insert(key, value) for each
/// \remarks This function is morally equivalent to calling `insert(key, value)` for each
/// key-value pair covered by the iterator range, so any values with keys already found in the
/// table will not be replaced.
template <typename ITER, typename = std::enable_if_t<
@@ -449,7 +451,7 @@ namespace toml
/// {
/// // add a string using std::string's substring constructor
/// auto result = tbl.emplace<std::string>(k, "this is not a drill"sv, 14, 5);
/// std::cout << "emplaced a value with key '"sv << k << "': "sv << result.second << std::endl;
/// std::cout << "emplaced with key '"sv << k << "': "sv << result.second << std::endl;
/// }
/// std::cout << tbl << std::endl;
///
@@ -457,8 +459,8 @@ namespace toml
///
/// \out
/// { a = 1, b = 2, c = 3 }
/// emplaced a value with key 'a': false
/// emplaced a value with key 'd': true
/// emplaced with key 'a': false
/// emplaced with key 'd': true
/// { a = 1, b = 2, c = 3, d = "drill" }
/// \eout
///

View File

@@ -6,8 +6,6 @@ namespace toml
{
/// \brief A TOML value.
///
/// \extends ::toml::node
///
/// \tparam T The value's data type. Can be one of:
/// - toml::string
/// - int64_t
@@ -69,6 +67,9 @@ namespace toml
return *this;
}
value(const value&) = delete;
value& operator= (const value&) = delete;
/// \brief Returns the value's node type identifier.
///
/// \returns One of:
@@ -195,12 +196,7 @@ namespace toml
/// \brief Value equality operator.
[[nodiscard]] friend bool operator == (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ == rhs; }
/// \brief Value equality operator.
[[nodiscard]] friend bool operator == (value_arg_t lhs, const value& rhs) noexcept { return lhs == rhs.val_; }
/// \brief Value inequality operator.
[[nodiscard]] friend bool operator != (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ != rhs; }
/// \brief Value inequality operator.
[[nodiscard]] friend bool operator != (value_arg_t lhs, const value& rhs) noexcept { return lhs != rhs.val_; }
TOML_ASYMMETRICAL_EQUALITY_OPS(const value&, value_arg_t, )
/// \brief Value less-than operator.
[[nodiscard]] friend bool operator < (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ < rhs; }
@@ -244,10 +240,7 @@ namespace toml
template <typename U>
[[nodiscard]] friend bool operator != (const value& lhs, const value<U>& rhs) noexcept
{
if constexpr (std::is_same_v<T, U>)
return lhs.val_ != rhs.val_;
else
return true;
return !(lhs == rhs);
}
/// \brief Less-than operator.

View File

@@ -2,8 +2,8 @@
#define TOML_LIB_MAJOR 0
#define TOML_LIB_MINOR 1
#define TOML_LIB_REVISION 0
#define TOML_LIB_PATCH 0
#define TOML_LANG_MAJOR 0
#define TOML_LANG_MINOR 5
#define TOML_LANG_REVISION 0
#define TOML_LANG_PATCH 0

View File

@@ -13,7 +13,7 @@ project(
compiler = meson.get_compiler('cpp')
if compiler.get_id() == 'gcc'
add_project_arguments(['-fmax-errors=5', '-Wno-attributes'], language : 'cpp')
add_project_arguments(['-fmax-errors=5', '-Wno-attributes', '-Wno-init-list-lifetime' ], language : 'cpp')
endif
if compiler.get_id() == 'clang'

View File

@@ -28,6 +28,7 @@ type_names = [
'date',
'time',
'date_time',
'time_offset',
'string',
'string_view',
'string_char',
@@ -844,11 +845,14 @@ def preprocess_xml(xml_dir):
toml_value_text = read_all_text_from_file(toml_value_path)
toml_value_search_string = '<compoundname>toml::value</compoundname>'
toml_value_insert_pos = toml_value_text.find(toml_value_search_string)
changed = False
if (toml_value_insert_pos != -1):
toml_value_insert_pos += len(toml_value_search_string)
toml_value_text = toml_value_text[:toml_value_insert_pos] \
+ '\n <basecompoundref refid="classtoml_1_1node" prot="public" virt="non-virtual">toml::node</basecompoundref>' \
+ toml_value_text[toml_value_insert_pos:]
changed = True
if changed:
with open(toml_value_path,'w', encoding='utf-8', newline='\n') as output_file:
print(toml_value_text, file=output_file)

View File

@@ -114,7 +114,7 @@ def main():
match = re.search(r'^\s*#\s*define\s+TOML_LIB_MINOR\s+([0-9]+)\s*$', source_text, re.I | re.M)
if match is not None:
library_version[1] = match.group(1)
match = re.search(r'^\s*#\s*define\s+TOML_LIB_REVISION\s+([0-9]+)\s*$', source_text, re.I | re.M)
match = re.search(r'^\s*#\s*define\s+TOML_LIB_(?:REVISION|PATCH)\s+([0-9]+)\s*$', source_text, re.I | re.M)
if match is not None:
library_version[2] = match.group(1)

View File

@@ -96,6 +96,23 @@ TEST_CASE("arrays - equality")
{
array arr1{ 1, 2, 3 };
CHECK(arr1 == arr1);
{
auto ilist = { 1, 2, 3 };
CHECK(arr1 == ilist);
CHECK(ilist == arr1);
ilist = { 2, 3, 4 };
CHECK(arr1 != ilist);
CHECK(ilist != arr1);
auto ivec = std::vector{ 1, 2, 3 };
CHECK(arr1 == ivec);
CHECK(ivec == arr1);
ivec = std::vector{ 2, 3, 4 };
CHECK(arr1 != ivec);
CHECK(ivec != arr1);
}
array arr2{ 1, 2, 3 };
CHECK(arr1 == arr2);

View File

@@ -18,13 +18,13 @@ lt2 = 00:32:00.999999
)"sv),
[](table&& tbl) noexcept
{
static constexpr auto odt1 = date_time{ { 1979, 5, 27 }, { 7, 32 }, time_offset{} };
static constexpr auto odt1 = date_time{ { 1979, 5, 27 }, { 7, 32 }, {} };
CHECK(tbl[S("odt1")] == odt1);
static constexpr auto odt2 = date_time{ { 1979, 5, 27 }, { 0, 32 }, time_offset::from_hh_mm(-7, 0) };
static constexpr auto odt2 = date_time{ { 1979, 5, 27 }, { 0, 32 }, { -7, 0 } };
CHECK(tbl[S("odt2")] == odt2);
static constexpr auto odt3 = date_time{ { 1979, 5, 27 }, { 0, 32, 0, 999999000u }, time_offset::from_hh_mm(-7, 0) };
static constexpr auto odt3 = date_time{ { 1979, 5, 27 }, { 0, 32, 0, 999999000u }, { -7, 0 } };
CHECK(tbl[S("odt3")] == odt3);
static constexpr auto odt4 = date_time{ { 1979, 5, 27 }, { 7, 32 }, time_offset{} };
static constexpr auto odt4 = date_time{ { 1979, 5, 27 }, { 7, 32 }, {} };
CHECK(tbl[S("odt4")] == odt4);
static constexpr auto ldt1 = date_time{ { 1979, 5, 27 }, { 7, 32 } };
CHECK(tbl[S("ldt1")] == ldt1);
@@ -49,12 +49,12 @@ lt2 = 00:32:00.999999
parse_expected_value("1987-03-16 10:20:30"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, time_offset::from_hh_mm( -9, -30 ) };
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, { -9, -30 } };
parse_expected_value("1987-03-16T10:20:30-09:30"sv, val);
parse_expected_value("1987-03-16 10:20:30-09:30"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, time_offset::from_hh_mm( 9, 30 ) };
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, { 9, 30 } };
parse_expected_value("1987-03-16T10:20:30+09:30"sv, val);
parse_expected_value("1987-03-16 10:20:30+09:30"sv, val);
}
@@ -64,22 +64,22 @@ lt2 = 00:32:00.999999
parse_expected_value("1987-03-16 10:20:30.04"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, time_offset::from_hh_mm( -9, -30 ) };
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, { -9, -30 } };
parse_expected_value("1987-03-16T10:20:30.04-09:30"sv, val);
parse_expected_value("1987-03-16 10:20:30.04-09:30"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, time_offset::from_hh_mm( 9, 30 ) };
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, { 9, 30 } };
parse_expected_value("1987-03-16T10:20:30.04+09:30"sv, val);
parse_expected_value("1987-03-16 10:20:30.04+09:30"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, time_offset{} };
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, {} };
parse_expected_value("1987-03-16T10:20:30Z"sv, val);
parse_expected_value("1987-03-16 10:20:30Z"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, time_offset{} };
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, {} };
parse_expected_value("1987-03-16T10:20:30.04Z"sv, val);
parse_expected_value("1987-03-16 10:20:30.04Z"sv, val);
}
@@ -94,17 +94,17 @@ lt2 = 00:32:00.999999
parse_expected_value("1987-03-16 10:20"sv, val );
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, time_offset::from_hh_mm( -9, -30 ) };
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, { -9, -30 } };
parse_expected_value("1987-03-16T10:20-09:30"sv, val);
parse_expected_value("1987-03-16 10:20-09:30"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, time_offset::from_hh_mm( 9, 30 ) };
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, { 9, 30 } };
parse_expected_value("1987-03-16T10:20+09:30"sv, val);
parse_expected_value("1987-03-16 10:20+09:30"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, time_offset{} };
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, {} };
parse_expected_value("1987-03-16T10:20Z"sv, val);
parse_expected_value("1987-03-16 10:20Z"sv, val);
}

View File

@@ -49,7 +49,7 @@ hosts = [
CHECK(tbl[S("owner")]);
CHECK(tbl[S("owner")].as<table>());
CHECK(tbl[S("owner")][S("name")] == S("Tom Preston-Werner"sv));
const auto dob = date_time{ { 1979, 5, 27 }, { 7, 32 }, time_offset::from_hh_mm(-8, 0) };
const auto dob = date_time{ { 1979, 5, 27 }, { 7, 32 }, { -8, 0 } };
CHECK(tbl[S("owner")][S("dob")] == dob);
CHECK(tbl[S("database")].as<table>());

View File

@@ -131,7 +131,7 @@ str = ''''That's still pointless', she said.'''
S("\"\u03B1\u03B2\u03B3\""sv));
// toml/issues/622 - escaping alias for spaces
#if TOML_LANG_HIGHER_THAN(0, 5, 0)
#if 0 && TOML_LANG_HIGHER_THAN(0, 5, 0)
parse_expected_value(
R"("The\squick\sbrown\sfox\sjumps\sover\sthe\slazy\sdog")"sv,
S("The quick brown fox jumps over the lazy dog"sv));

View File

@@ -201,10 +201,28 @@ void parse_expected_value(std::string_view value_str, const T& expected) noexcep
parsing_should_succeed(std::string_view{ value }, [&](table&& tbl) noexcept
{
CHECK(tbl.size() == 1);
REQUIRE(tbl[S("val"sv)].as<impl::promoted<T>>());
REQUIRE(tbl[S("val"sv)].get()->type() == impl::node_type_of<T>);
CHECK(tbl[S("val"sv)].as<impl::promoted<T>>()->get() == expected);
CHECK(tbl[S("val"sv)].get()->source().begin == begin);
CHECK(tbl[S("val"sv)].get()->source().end == end);
const auto nv = tbl[S("val"sv)];
REQUIRE(nv);
REQUIRE(nv.as<impl::promoted<T>>());
REQUIRE(nv.get()->type() == impl::node_type_of<T>);
//check the raw value
CHECK(nv.as<impl::promoted<T>>()->get() == expected);
//check the value relops
CHECK(*nv.as<impl::promoted<T>>() == expected);
CHECK(expected == *nv.as<impl::promoted<T>>());
CHECK(!(*nv.as<impl::promoted<T>>() != expected));
CHECK(!(expected != *nv.as<impl::promoted<T>>()));
//check the node_view relops
CHECK(nv == expected);
CHECK(expected == nv);
CHECK(!(nv != expected));
CHECK(!(expected != nv));
//make sure source info is correct
CHECK(nv.get()->source().begin == begin);
CHECK(nv.get()->source().end == end);
});
}

490
toml.hpp
View File

@@ -154,6 +154,10 @@
#define TOML_INTERFACE __declspec(novtable)
#define TOML_EMPTY_BASES __declspec(empty_bases)
#if !defined(TOML_RELOPS_REORDERING) && defined(__cpp_impl_three_way_comparison)
#define TOML_RELOPS_REORDERING 1
#endif
#elif defined(__GNUC__)
#ifndef __cpp_exceptions
@@ -178,6 +182,10 @@
#define TOML_USE_STREAMS_FOR_FLOATS 1
#endif
#if !defined(TOML_RELOPS_REORDERING) && defined(__cpp_impl_three_way_comparison)
#define TOML_RELOPS_REORDERING 1
#endif
#endif
#ifndef TOML_CPP_VERSION
@@ -273,24 +281,35 @@
#ifndef TOML_NODISCARD_CTOR
#define TOML_NODISCARD_CTOR
#endif
#ifndef TOML_RELOPS_REORDERING
#define TOML_RELOPS_REORDERING 0
#endif
#if TOML_RELOPS_REORDERING
#define TOML_ASYMMETRICAL_EQUALITY_OPS(...)
#else
#define TOML_ASYMMETRICAL_EQUALITY_OPS(LHS, RHS, ...) \
__VA_ARGS__ [[nodiscard]] friend bool operator == (RHS rhs, LHS lhs) noexcept { return lhs == rhs; } \
__VA_ARGS__ [[nodiscard]] friend bool operator != (LHS lhs, RHS rhs) noexcept { return !(lhs == rhs); } \
__VA_ARGS__ [[nodiscard]] friend bool operator != (RHS rhs, LHS lhs) noexcept { return !(lhs == rhs); }
#endif
#define TOML_LIB_MAJOR 0
#define TOML_LIB_MINOR 1
#define TOML_LIB_REVISION 0
#define TOML_LIB_PATCH 0
#define TOML_LANG_MAJOR 0
#define TOML_LANG_MINOR 5
#define TOML_LANG_REVISION 0
#define TOML_LANG_PATCH 0
#define TOML_MAKE_VERSION(maj, min, rev) \
((maj) * 1000 + (min) * 25 + (rev))
#if TOML_UNRELEASED_FEATURES
#define TOML_LANG_EFFECTIVE_VERSION \
TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_REVISION+1)
TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH+1)
#else
#define TOML_LANG_EFFECTIVE_VERSION \
TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_REVISION)
TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH)
#endif
#define TOML_LANG_HIGHER_THAN(maj, min, rev) \
@@ -375,6 +394,7 @@ namespace toml
enum class node_type : uint8_t
{
none,
table,
array,
string,
@@ -682,7 +702,7 @@ namespace toml::impl
template <> struct node_type_of_<date_time> { static constexpr auto value = node_type::date_time; };
template <typename T>
inline constexpr auto node_type_of = node_type_of_<promoted<typename node_unwrapper<T>::type>>::value;
inline constexpr auto node_type_of = node_type_of_<promoted<typename node_unwrapper<remove_cvref_t<T>>::type>>::value;
inline constexpr toml::string_view low_character_escape_table[] =
{
@@ -722,6 +742,7 @@ namespace toml::impl
inline constexpr std::string_view node_type_friendly_names[] =
{
"none"sv,
"table"sv,
"array"sv,
"string"sv,
@@ -768,6 +789,8 @@ namespace toml
template <typename T>
inline constexpr bool is_floating_point = std::is_same_v<impl::node_of<impl::remove_cvref_t<T>>, value<double>>;
template <typename T>
inline constexpr bool is_number = is_integer<T> || is_floating_point<T>;
template <typename T>
inline constexpr bool is_boolean = std::is_same_v<impl::node_of<impl::remove_cvref_t<T>>, value<bool>>;
template <typename T>
inline constexpr bool is_date = std::is_same_v<impl::node_of<impl::remove_cvref_t<T>>, value<date>>;
@@ -867,11 +890,15 @@ namespace toml
{
int16_t minutes;
[[nodiscard]]
static constexpr time_offset from_hh_mm(int8_t hours, int8_t minutes) noexcept
{
return time_offset{ static_cast<int16_t>(hours * 60 + minutes) };
}
TOML_NODISCARD_CTOR
constexpr time_offset() noexcept
: minutes{}
{}
TOML_NODISCARD_CTOR
constexpr time_offset(int8_t hours, int8_t minutes) noexcept
: minutes{ static_cast<int16_t>(hours * 60 + minutes) }
{}
[[nodiscard]]
friend constexpr bool operator == (time_offset lhs, time_offset rhs) noexcept
@@ -900,6 +927,25 @@ namespace toml
toml::time time;
std::optional<toml::time_offset> time_offset;
TOML_NODISCARD_CTOR
constexpr date_time() noexcept
: date{},
time{}
{}
TOML_NODISCARD_CTOR
constexpr date_time(toml::date d, toml::time t) noexcept
: date{ d },
time{ t }
{}
TOML_NODISCARD_CTOR
constexpr date_time(toml::date d, toml::time t, toml::time_offset offset) noexcept
: date{ d },
time{ t },
time_offset{ offset }
{}
[[nodiscard]]
constexpr bool is_local() const noexcept
{
@@ -1628,6 +1674,9 @@ namespace toml
return *this;
}
value(const value&) = delete;
value& operator= (const value&) = delete;
[[nodiscard]] node_type type() const noexcept override { return impl::node_type_of<T>; }
[[nodiscard]] bool is_table() const noexcept override { return false; }
[[nodiscard]] bool is_array() const noexcept override { return false; }
@@ -1704,9 +1753,7 @@ namespace toml
}
[[nodiscard]] friend bool operator == (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ == rhs; }
[[nodiscard]] friend bool operator == (value_arg_t lhs, const value& rhs) noexcept { return lhs == rhs.val_; }
[[nodiscard]] friend bool operator != (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ != rhs; }
[[nodiscard]] friend bool operator != (value_arg_t lhs, const value& rhs) noexcept { return lhs != rhs.val_; }
TOML_ASYMMETRICAL_EQUALITY_OPS(const value&, value_arg_t, )
[[nodiscard]] friend bool operator < (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ < rhs; }
[[nodiscard]] friend bool operator < (value_arg_t lhs, const value& rhs) noexcept { return lhs < rhs.val_; }
[[nodiscard]] friend bool operator <= (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ <= rhs; }
@@ -1728,10 +1775,7 @@ namespace toml
template <typename U>
[[nodiscard]] friend bool operator != (const value& lhs, const value<U>& rhs) noexcept
{
if constexpr (std::is_same_v<T, U>)
return lhs.val_ != rhs.val_;
else
return true;
return !(lhs == rhs);
}
template <typename U>
@@ -1980,7 +2024,6 @@ namespace toml::impl
namespace toml
{
[[nodiscard]] bool operator == (const table& lhs, const table& rhs) noexcept;
[[nodiscard]] bool operator != (const table& lhs, const table& rhs) noexcept;
class array final
: public node
@@ -2042,6 +2085,9 @@ namespace toml
return *this;
}
array(const array&) = delete;
array& operator= (const array&) = delete;
[[nodiscard]] node_type type() const noexcept override { return node_type::array; }
[[nodiscard]] bool is_table() const noexcept override { return false; }
[[nodiscard]] bool is_array() const noexcept override { return true; }
@@ -2266,6 +2312,31 @@ namespace toml
private:
template <typename T>
[[nodiscard]] static bool container_equality(const array& lhs, const T& rhs) noexcept
{
using elem_t = std::remove_const_t<typename T::value_type>;
static_assert(
impl::is_value_or_promotable<elem_t>,
"Container element type must be (or be promotable to) one of the TOML value types"
);
if (lhs.size() != rhs.size())
return false;
if (rhs.size() == 0_sz)
return true;
size_t i{};
for (auto& list_elem : rhs)
{
const auto elem = lhs.get_as<impl::promoted<elem_t>>(i++);
if (!elem || *elem != list_elem)
return false;
}
return true;
}
[[nodiscard]] size_t total_leaf_count() const noexcept
{
size_t leaves{};
@@ -2295,6 +2366,19 @@ namespace toml
public:
template <typename T>
[[nodiscard]] friend bool operator == (const array& lhs, const std::initializer_list<T>& rhs) noexcept
{
return container_equality(lhs, rhs);
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const array&, const std::initializer_list<T>&, template <typename T>)
template <typename T>
[[nodiscard]] friend bool operator == (const array& lhs, const std::vector<T>& rhs) noexcept
{
return container_equality(lhs, rhs);
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const array&, const std::vector<T>&, template <typename T>)
void flatten() TOML_MAY_THROW
{
if (values.empty())
@@ -2468,7 +2552,6 @@ namespace toml
{
private:
friend class impl::parser;
friend class node_view<table>;
impl::string_map<std::unique_ptr<node>> values;
bool inline_ = false;
@@ -2509,6 +2592,9 @@ namespace toml
return *this;
}
table(const table&) = delete;
table& operator= (const table&) = delete;
[[nodiscard]] node_type type() const noexcept override { return node_type::table; }
[[nodiscard]] bool is_table() const noexcept override { return true; }
[[nodiscard]] bool is_array() const noexcept override { return false; }
@@ -2517,8 +2603,8 @@ namespace toml
[[nodiscard]] const table* as_table() const noexcept override { return this; }
[[nodiscard]] bool is_inline() const noexcept { return inline_; }
void is_inline(bool val) noexcept { inline_ = val; }
[[nodiscard]] inline node_view<table> operator[] (string_view key) noexcept;
[[nodiscard]] inline node_view<const table> operator[] (string_view key) const noexcept;
[[nodiscard]] inline node_view<node> operator[] (string_view key) noexcept;
[[nodiscard]] inline node_view<const node> operator[] (string_view key) const noexcept;
[[nodiscard]] iterator begin() noexcept { return { values.begin() }; }
[[nodiscard]] const_iterator begin() const noexcept { return { values.begin() }; }
[[nodiscard]] const_iterator cbegin() const noexcept { return { values.cbegin() }; }
@@ -2713,176 +2799,79 @@ namespace toml
//------------------------------------------------------------------------------------------ ↓ toml_node_view.h ------
#pragma region
namespace toml::impl
{
template <typename T>
struct node_view_traits;
template <>
struct node_view_traits<const table>
{
using haystack_type = const table*;
using key_type = string_view;
[[nodiscard]] static const node* get(const table* tbl, key_type key) noexcept
{
return tbl->get(key);
}
template <typename U>
[[nodiscard]] static const node_of<U>* as(const table* tbl, key_type key) noexcept
{
return tbl->get_as<U>(key);
}
};
template <>
struct node_view_traits<table>
{
using haystack_type = table*;
using key_type = string_view;
[[nodiscard]] static node* get(table* tbl, key_type key) noexcept
{
return tbl->get(key);
}
[[nodiscard]] static const node* get(const table* tbl, key_type key) noexcept
{
return tbl->get(key);
}
template <typename T>
[[nodiscard]] static node_of<T>* as(table* tbl, key_type key) noexcept
{
return tbl->get_as<T>(key);
}
template <typename T>
[[nodiscard]] static const node_of<T>* as(const table* tbl, key_type key) noexcept
{
return tbl->get_as<T>(key);
}
};
template <typename T, typename K>
struct sub_view final { };
template <typename T>
struct node_view_traits<sub_view<T, string_view>>
{
using haystack_type = T;
using key_type = string_view;
[[nodiscard]] static auto get(haystack_type& view, string_view key) noexcept
{
auto parent = view.as_table();
return parent ? parent->get(key) : nullptr;
}
[[nodiscard]] static const node* get(const haystack_type& view, string_view key) noexcept
{
auto parent = view.as_table();
return parent ? parent->get(key) : nullptr;
}
template <typename U>
[[nodiscard]] static auto as(haystack_type& view, string_view key) noexcept
{
auto parent = view.as_table();
return parent ? parent->template get_as<U>(key) : nullptr;
}
template <typename U>
[[nodiscard]] static const node_of<U>* as(const haystack_type& view, string_view key) noexcept
{
auto parent = view.as_table();
return parent ? parent->template get_as<U>(key) : nullptr;
}
};
template <typename T>
struct node_view_traits<sub_view<T, size_t>>
{
using haystack_type = T;
using key_type = size_t;
[[nodiscard]] static auto get(haystack_type& view, size_t index) noexcept
{
auto parent = view.as_array();
return parent ? parent->get(index) : nullptr;
}
[[nodiscard]] static const node* get(const haystack_type& view, size_t index) noexcept
{
auto parent = view.as_array();
return parent ? parent->get(index) : nullptr;
}
template <typename U>
[[nodiscard]] static auto as(haystack_type& view, size_t index) noexcept
{
auto parent = view.as_array();
return parent ? parent->template get_as<U>(index) : nullptr;
}
template <typename U>
[[nodiscard]] static const node_of<U>* as(const haystack_type& view, size_t index) noexcept
{
auto parent = view.as_array();
return parent ? parent->template get_as<U>(index) : nullptr;
}
};
}
namespace toml
{
template <typename T>
class node_view final
{
public:
using traits = impl::node_view_traits<T>;
using key_type = typename traits::key_type;
using viewed_type = T;
private:
using haystack_type = typename traits::haystack_type;
haystack_type haystack_;
key_type key_;
friend class toml::table;
viewed_type* node_;
TOML_NODISCARD_CTOR
node_view(viewed_type* node) noexcept
: node_{ node }
{}
public:
TOML_NODISCARD_CTOR
node_view(haystack_type obj, key_type key) noexcept
: haystack_{ obj },
key_{ key }
{}
[[nodiscard]] explicit operator bool() const noexcept { return node_ != nullptr; }
[[nodiscard]] viewed_type* get() noexcept { return node_; }
[[nodiscard]] const viewed_type* get() const noexcept { return node_; }
[[nodiscard]] auto get() noexcept { return traits::get(haystack_, key_); }
[[nodiscard]] const node* get() const noexcept { return traits::get(haystack_, key_); }
[[nodiscard]] explicit operator bool() const noexcept { return !!get(); }
[[nodiscard]] node_type type() const noexcept { return node_ ? node_->type() : node_type::none; }
[[nodiscard]] bool is_table() const noexcept { return type() == node_type::table; }
[[nodiscard]] bool is_array() const noexcept { return type() == node_type::array; }
[[nodiscard]] bool is_value() const noexcept { return type() > node_type::array; }
[[nodiscard]] bool is_string() const noexcept { return type() == node_type::string; }
[[nodiscard]] bool is_integer() const noexcept { return type() == node_type::integer; }
[[nodiscard]] bool is_floating_point() const noexcept { return type() == node_type::floating_point; }
[[nodiscard]] bool is_boolean() const noexcept { return type() == node_type::boolean; }
[[nodiscard]] bool is_date() const noexcept { return type() == node_type::date; }
[[nodiscard]] bool is_time() const noexcept { return type() == node_type::time; }
[[nodiscard]] bool is_date_time() const noexcept { return type() == node_type::date_time; }
[[nodiscard]] bool is_array_of_tables() const noexcept
{
return node_ ? node_->is_array_of_tables() : false;
}
template <typename U>
[[nodiscard]] auto as() noexcept
[[nodiscard]]
bool is() const noexcept
{
return node_ ? node_->template is<U>() : false;
}
template <typename U>
[[nodiscard]]
auto as() noexcept
{
static_assert(
impl::is_value_or_node<impl::unwrapped<U>>,
"Template type parameter must be one of the basic value types, a toml::table, or a toml::array"
);
return traits::template as<U>(haystack_, key_);
return node_ ? node_->template as<U>() : nullptr;
}
template <typename U>
[[nodiscard]] const impl::node_of<U>* as() const noexcept
[[nodiscard]]
const impl::node_of<U>* as() const noexcept
{
static_assert(
impl::is_value_or_node<impl::unwrapped<U>>,
"Template type parameter must be one of the basic value types, a toml::table, or a toml::array"
);
return traits::template as<U>(haystack_, key_);
return node_ ? node_->template as<U>() : nullptr;
}
[[nodiscard]] auto as_table() noexcept { return as<table>(); }
[[nodiscard]] auto as_array() noexcept { return as<array>(); }
[[nodiscard]] auto as_string() noexcept { return as<string>(); }
[[nodiscard]] auto as_integer() noexcept { return as<int64_t>(); }
[[nodiscard]] auto as_floating_point() noexcept { return as<double>(); }
@@ -2890,8 +2879,8 @@ namespace toml
[[nodiscard]] auto as_date() noexcept { return as<date>(); }
[[nodiscard]] auto as_time() noexcept { return as<time>(); }
[[nodiscard]] auto as_date_time() noexcept { return as<date_time>(); }
[[nodiscard]] auto as_array() noexcept { return as<array>(); }
[[nodiscard]] auto as_table() noexcept { return as<table>(); }
[[nodiscard]] const table* as_table() const noexcept { return as<table>(); }
[[nodiscard]] const array* as_array() const noexcept { return as<array>(); }
[[nodiscard]] const value<string>* as_string() const noexcept { return as<string>(); }
[[nodiscard]] const value<int64_t>* as_integer() const noexcept { return as<int64_t>(); }
[[nodiscard]] const value<double>* as_floating_point() const noexcept { return as<double>(); }
@@ -2899,98 +2888,141 @@ namespace toml
[[nodiscard]] const value<date>* as_date() const noexcept { return as<date>(); }
[[nodiscard]] const value<time>* as_time() const noexcept { return as<time>(); }
[[nodiscard]] const value<date_time>* as_date_time() const noexcept { return as<date_time>(); }
[[nodiscard]] const array* as_array() const noexcept { return as<array>(); }
[[nodiscard]] const table* as_table() const noexcept { return as<table>(); }
private:
template <typename U>
[[nodiscard]] static bool value_equality(const node_view& lhs, const U& rhs) noexcept
template <typename N, typename FUNC>
static decltype(auto) do_visit(N* node, FUNC&& visitor)
TOML_MAY_THROW_UNLESS(noexcept(std::declval<N*>()->visit(std::declval<FUNC&&>())))
{
const auto val = lhs.as<impl::promoted<U>>();
return val && val->get() == rhs;
using return_type = decltype(node->visit(std::forward<FUNC>(visitor)));
if (node)
return node->visit(std::forward<FUNC>(visitor));
if constexpr (!std::is_void_v<return_type>)
return return_type{};
}
template <typename U>
[[nodiscard]] static bool container_equality(const node_view& lhs, const U& rhs) noexcept
{
using elem_t = std::remove_const_t<typename U::value_type>;
static_assert(
impl::is_value_or_promotable<elem_t>,
"Container element type must be (or be promotable to) one of the TOML value types"
);
const array* arr = lhs.as<array>();
if (!arr || arr->size() != rhs.size())
return false;
if (rhs.size() == 0_sz)
return true;
size_t i{};
for (auto& list_elem : rhs)
{
const auto elem = arr->get_as<impl::promoted<elem_t>>(i++);
if (!elem || elem->get() != list_elem)
return false;
}
return true;
}
template <typename FUNC, typename N>
static constexpr bool visit_is_nothrow =
noexcept(do_visit(std::declval<N*>(), std::declval<FUNC&&>()));
public:
[[nodiscard]] bool operator == (string_view rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (int64_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (int32_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (int16_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (int8_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (uint32_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (uint16_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (uint8_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (double rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (float rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (bool rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (const date& rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (const time& rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (const date_time& rhs) const noexcept { return value_equality(*this, rhs); }
template <typename FUNC>
decltype(auto) visit(FUNC&& visitor)
TOML_MAY_THROW_UNLESS(visit_is_nothrow<FUNC&&, viewed_type>)
{
return do_visit(node_, std::forward<FUNC>(visitor));
}
template <typename FUNC>
decltype(auto) visit(FUNC&& visitor) const
TOML_MAY_THROW_UNLESS(visit_is_nothrow<FUNC&&, const viewed_type>)
{
return do_visit(node_, std::forward<FUNC>(visitor));
}
[[nodiscard]] friend bool operator == (const node_view& lhs, const table& rhs) noexcept
{
if (lhs.node_ == &rhs)
return true;
const auto tbl = lhs.as<table>();
return tbl && *tbl == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const table&, )
[[nodiscard]] friend bool operator == (const node_view& lhs, const array& rhs) noexcept
{
if (lhs.node_ == &rhs)
return true;
const auto arr = lhs.as<array>();
return arr && *arr == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const array&, )
template <typename U>
[[nodiscard]] bool operator == (const std::initializer_list<U>& rhs) const noexcept
[[nodiscard]] friend bool operator == (const node_view& lhs, const value<U>& rhs) noexcept
{
return container_equality(*this, rhs);
if (lhs.node_ == &rhs)
return true;
const auto val = lhs.as<U>();
return val && *val == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const value<U>&, template <typename U>)
template <typename U, typename = std::enable_if_t<impl::is_value_or_promotable<U>>>
[[nodiscard]] friend bool operator == (const node_view& lhs, const U& rhs) noexcept
{
const auto val = lhs.as<impl::promoted<U>>();
return val && *val == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(
const node_view&,
const U&,
template <typename U, typename = std::enable_if_t<impl::is_value_or_promotable<U>>>
)
template <typename U>
[[nodiscard]] bool operator == (const std::vector<U>& rhs) const noexcept
[[nodiscard]] friend bool operator == (const node_view& lhs, const std::initializer_list<U>& rhs) noexcept
{
return container_equality(*this, rhs);
const auto arr = lhs.as<array>();
return arr && *arr == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const std::initializer_list<U>&, template <typename U>)
template <typename U>
[[nodiscard]] friend bool operator == (const U& lhs, const node_view& rhs) noexcept
[[nodiscard]] friend bool operator == (const node_view& lhs, const std::vector<U>& rhs) noexcept
{
return rhs == lhs;
const auto arr = lhs.as<array>();
return arr && *arr == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const std::vector<U>&, template <typename U>)
[[nodiscard]] node_view<viewed_type> operator[] (string_view key) noexcept
{
if (auto tbl = this->as_table())
return { tbl->get(key) };
return { nullptr };
}
[[nodiscard]] node_view<impl::sub_view<node_view<T>, string_view>> operator[] (string_view key) noexcept
[[nodiscard]] node_view<viewed_type> operator[] (size_t index) noexcept
{
return { *this, key };
if (auto tbl = this->as_array())
return { tbl->get(index) };
return { nullptr };
}
[[nodiscard]] node_view<impl::sub_view<node_view<T>, size_t>> operator[] (size_t index) noexcept
[[nodiscard]] node_view<const viewed_type> operator[] (string_view key) const noexcept
{
return { *this, index };
if (auto tbl = this->as_table())
return { tbl->get(key) };
return { nullptr };
}
[[nodiscard]] node_view<const viewed_type> operator[] (size_t index) const noexcept
{
if (auto tbl = this->as_array())
return { tbl->get(index) };
return { nullptr };
}
template <typename CHAR>
friend std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& os, const node_view& nv) TOML_MAY_THROW
{
nv.visit([&](const auto& node) TOML_MAY_THROW
{
os << node;
});
return os;
}
};
inline node_view<table> table::operator[] (string_view key) noexcept
inline node_view<node> table::operator[] (string_view key) noexcept
{
return { this, key };
return { this->get(key) };
}
inline node_view<const table> table::operator[] (string_view key) const noexcept
inline node_view<const node> table::operator[] (string_view key) const noexcept
{
return { this, key };
return { this->get(key) };
}
}
@@ -5124,6 +5156,7 @@ namespace toml::impl
case U'n': str += TOML_STRING_PREFIX('\n'); break;
case U'r': str += TOML_STRING_PREFIX('\r'); break;
#if 0
case U's':
{
if constexpr (!TOML_LANG_HIGHER_THAN(0, 5, 0)) // toml/issues/622
@@ -5139,6 +5172,7 @@ namespace toml::impl
break;
}
}
#endif
case U't': str += TOML_STRING_PREFIX('\t'); break;
case U'"': str += TOML_STRING_PREFIX('"'); break;
@@ -6451,7 +6485,8 @@ namespace toml::impl
" offset; expected minute between 0 and 59 (inclusive), saw "sv, hour
);
offset.emplace(time_offset{ static_cast<int16_t>((hour * 60 + minute) * sign) });
offset.emplace();
offset->minutes = static_cast<int16_t>((hour * 60 + minute) * sign);
}
}
@@ -6462,11 +6497,10 @@ namespace toml::impl
);
TOML_ERROR_CHECK({});
return {
date,
time,
offset
};
if (offset)
return { date, time, *offset };
else
return { date, time };
}
// TOML_DISABLE_SWITCH_WARNINGS
@@ -8447,6 +8481,8 @@ namespace toml
#undef TOML_STRING_PREFIX
#undef TOML_UNDEF_MACROS
#undef TOML_DOXYGEN
#undef TOML_RELOPS_REORDERING
#undef TOML_ASYMMETRICAL_EQUALITY_OPS
#endif
#ifdef __GNUC__