added proper non-exhaustive visitor support to node::visit

also:
- fixed preprocessor snafu
- fixed bool `print_to_stream` overload not being selected
- added `TOML_SMALL_FLOAT_TYPE`
- added is_XXXXX node type metafunctions
- added additional API documentation
This commit is contained in:
Mark Gillard 2020-01-13 08:31:49 +02:00
parent 635dec5c8e
commit abdd4f9993
14 changed files with 586 additions and 342 deletions

1
.gitattributes vendored
View File

@ -15,3 +15,4 @@
*.sln text encoding=UTF-8-BOM eol=crlf
*.runsettings text encoding=UTF-8 eol=crlf
*.md text encoding=UTF-8 eol=lf
*.css text encoding=UTF-8 eol=lf

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 Mark Gillard
Copyright (c) 2019-2020 Mark Gillard
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

View File

@ -1,10 +1,24 @@
# toml++ (tomlplusplus)
Header-only [TOML v0.5.0] parsing lib for C++17 and later.
![c++version](https://img.shields.io/badge/c%2B%2B-17%2C%2020-informational)
[![tomlversion](https://img.shields.io/badge/TOML-v0.5.0-informational)][v0.5.0]
[![CircleCI](https://circleci.com/gh/marzer/tomlplusplus.svg?style=shield)](https://circleci.com/gh/marzer/tomlplusplus)
[![GitHub](https://img.shields.io/github/license/marzer/tomlplusplus)](https://github.com/marzer/tomlplusplus/blob/master/LICENSE)
`toml++` is a header-only toml parser and serializer for C++17, C++20 and whatever comes after.
<br>
# Usage
# Example
```cpp
/// example goes here.
```
You'll find some more code examples in
`examples` directory and plenty more as part of the [API documentation].
<br>
# Adding toml++ to your project
`toml++` comes in two flavours: Regular and Single-header.
### Regular mode
@ -12,27 +26,25 @@ Header-only [TOML v0.5.0] parsing lib for C++17 and later.
2. `#include <toml++/toml.h>`
### Single-header mode
1. Drop `toml.hpp` somewhere in your source tree
1. Drop `toml.hpp` wherever you like in your source tree
2. There is no step two
The API is the same regardless of how you consume the library. You'll find code examples in
the `examples` directory.
The API is the same regardless of how you consume the library.
<br>
# Configuration
### Configuration
A number of configurable options are exposed in the form of preprocessor macros. Most likely you
won't need to mess with these at all, but in the event you do, set your overrides prior to including
toml++.
| Option | Type | Default | Description |
|----------------------------|:--------------:|-----------------------------------|-----------------------------------------------------------------------------------------------|
| `TOML_ASSERT(expr)` | function macro | `assert(expr)`<br>(or undefined) | Sets the assert function used by the library. |
| `TOML_CHAR_8_STRINGS` | boolean | `0` | Uses C++20 [char8_t]-based strings as the toml string data type. |
| `TOML_CONFIG_HEADER` | string literal | undefined | Includes the given header file before the rest of the library. |
| `TOML_LARGE_FILES` | boolean | `0` | Uses 32-bit integers for line and column indices (instead of 16-bit). |
| `TOML_UNDEF_MACROS` | boolean | `1` | `#undefs` the library's internal macros at the end of the header. |
| `TOML_UNRELEASED_FEATURES` | boolean | `1` | Enables support for [unreleased TOML language features] not yet part of a [numbered version]. |
| Option | Type | Default | Description |
|----------------------------|:--------------:|-----------------------------------|----------------------------------------------------------------------------------------------------------|
| `TOML_ASSERT(expr)` | function macro | `assert(expr)`<br>(or undefined) | Sets the assert function used by the library. |
| `TOML_CHAR_8_STRINGS` | boolean | `0` | Uses C++20 [char8_t]-based strings as the toml string data type. |
| `TOML_CONFIG_HEADER` | string literal | undefined | Includes the given header file before the rest of the library. |
| `TOML_LARGE_FILES` | boolean | `0` | Uses 32-bit integers for line and column indices (instead of 16-bit). |
| `TOML_SMALL_FLOAT_TYPE` | type name | undefined | If your codebase has an additional 'small' float type (e.g. half-precision), this tells toml++ about it. |
| `TOML_UNDEF_MACROS` | boolean | `1` | `#undefs` the library's internal macros at the end of the header. |
| `TOML_UNRELEASED_FEATURES` | boolean | `1` | Enables support for [unreleased TOML language features] not yet part of a [numbered version]. |
<br>
@ -44,7 +56,7 @@ addition of unreleased features from the [TOML master] and some sane cherry-pick
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`.
### **🔸Unreleased features:**
### **🔸Unreleased TOML features:**
- [#356]: Allow leading zeros in the exponent part of a float
- [#516]: Allow newlines and trailing commas in inline tables
- [#562]: Allow hex floatingpoint values
@ -59,7 +71,7 @@ defines `TOML_LANG_MAJOR`, `TOML_LANG_MINOR` and `TOML_LANG_REVISION`.
_These can be disabled (and thus strict [TOML v0.5.0] compliance enforced) by specifying
`TOML_UNRELEASED_FEATURES = 0` (see [Configuration](#Configuration))._
### **🔹[v0.5.0](https://github.com/toml-lang/toml/releases/tag/v0.5.0) and earlier:**
### **🔹TOML v0.5.0 and earlier:**
- All features as of `<< release date >>`.
<br>
@ -69,8 +81,8 @@ Contributions are welcome, either by [reporting issues](https://github.com/marze
or submitting pull requests. If you wish to submit a PR, please be aware that:
- The single-header file `toml.hpp` is generated by a script; make your changes in the files in
`include`, **not** in `toml.hpp`.
- Your changes should compile warning-free on at least gcc 8.3.0, clang 8.0, and MSVC 19.2X
(Visual Studio 2019).
- Your changes should compile warning-free on at least one of gcc 8.3.0, clang 8.0, and MSVC 19.2X
(Visual Studio 2019). All three is a bonus.
- You should regenerate the single-header file as part of your PR (a CI check will fail if you don't).
### Regenerating toml.hpp
@ -80,10 +92,10 @@ or submitting pull requests. If you wish to submit a PR, please be aware that:
3. Run `python/generate_single_header.py`
### Building and testing
Testing is done using [catch2], included in the respository as a submodule under `tests/catch2`.
Testing is done using [Catch2], included in the respository as a submodule under `extern/Catch2`.
The first time you want to begin testing you'll need to ensure submodules have been fetched:
```bash
git submodule update --init --recursive
git submodule update --init --recursive extern/Catch2
```
#### Windows
@ -100,10 +112,8 @@ Install [meson] and [ninja] if necessary, then test with both gcc and clang:
```bash
CXX=g++ meson build-gcc
CXX=clang++ meson build-clang
cd build-gcc
ninja && ninja test
cd ../build-clang
ninja && ninja test
cd build-gcc && ninja && ninja test
cd ../build-clang && ninja && ninja test
```
<br>
@ -113,21 +123,23 @@ ninja && ninja test
`toml++` is licensed under the terms of the MIT license - See [LICENSE].
UTF-8 decoding is performed using a state machine based on Bjoern Hoehrmann's '[Flexible and Economical UTF-8 Decoder]',
which is itself subject to the terms of the MIT license. The license text is included in the
which is itself subject to the terms of (what appears to be) the MIT license. The license text is included in the
[relevant part](https://github.com/marzer/tomlplusplus/blob/master/include/toml%2B%2B/toml_utf8.h)
of the toml++ source.
[API documentation]: https://marzer.github.io/tomlplusplus/namespacetoml.html
[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
[TOML master]: https://github.com/toml-lang/toml/blob/master/README.md
[TOML issues list]: https://github.com/toml-lang/toml/issues
[TOML v0.5.0]: https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md
[v0.5.0]: https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md
[LICENSE]: https://github.com/marzer/tomlplusplus/blob/master/LICENSE
[Flexible and Economical UTF-8 Decoder]: http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
[meson]: https://mesonbuild.com/Getting-meson.html
[ninja]: https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages
[catch2]: https://github.com/catchorg/Catch2
[Catch2]: https://github.com/catchorg/Catch2
[Test Adapter for Catch2]: https://marketplace.visualstudio.com/items?itemName=JohnnyHendriks.ext01
[Visual Studio 2019]: https://visualstudio.microsoft.com/vs/
[#356]: https://github.com/toml-lang/toml/issues/356

View File

@ -10,8 +10,12 @@ TAB_SIZE = 4
HTML_EXTRA_FILES = tomlplusplus.js
SHOW_INCLUDE_FILES = NO
##! M_THEME_COLOR = #22272e
##! M_LINKS_NAVBAR1 = pages
##! M_LINKS_NAVBAR2 = annotated
##! M_LINKS_NAVBAR1 = \
##! pages
##! M_LINKS_NAVBAR2 = \
##! namespaces \
##! annotated \
##! "<a target="_blank" href="https://github.com/marzer/tomlplusplus/">Github</a>"
##! M_SEARCH_DOWNLOAD_BINARY = NO
##! M_CLASS_TREE_EXPAND_LEVELS = 3
##! M_FILE_TREE_EXPAND_LEVELS = 3
@ -19,3 +23,4 @@ SHOW_INCLUDE_FILES = NO
##! <a target="_blank" href="https://github.com/marzer/tomlplusplus/issues">Report an issue</a> \
##! <br><br>Documentation generated using <a href="https://mcss.mosra.cz/">m.css</a>
##! M_HTML_HEADER = <script src="tomlplusplus.js"></script>
##! M_MAIN_PROJECT_URL = https://github.com/marzer/tomlplusplus/

View File

@ -38,7 +38,7 @@
#error toml++ is a C++ library.
#endif
#if defined(__clang__) or defined(__GNUC__)
#if defined(__clang__) || defined(__GNUC__)
#define TOML_GCC_ATTR(attr) __attribute__((attr))
#else
#define TOML_GCC_ATTR(attr)
@ -426,51 +426,12 @@ namespace toml
TOML_PUSH_WARNINGS
TOML_DISABLE_INIT_WARNINGS
#if !TOML_DOXYGEN && TOML_EXCEPTIONS
class parse_error final
: public std::runtime_error
{
private:
source_region source_;
public:
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* desc, source_region&& src) noexcept
: std::runtime_error{ desc },
source_{ std::move(src) }
{}
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* desc, const source_region& src) noexcept
: parse_error{ desc, source_region{ src } }
{}
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* desc, const source_position& position, const source_path_ptr& path = {}) noexcept
: parse_error{ desc, source_region{ position, position, path } }
{}
[[nodiscard]]
std::string_view description() const noexcept
{
return std::string_view{ what() };
}
[[nodiscard]]
const source_region& source() const noexcept
{
return source_;
}
};
#else
#if TOML_DOXYGEN || !TOML_EXCEPTIONS
/// \brief An error thrown/returned when parsing fails.
///
/// \remarks This class inherits from `std::runtime_error` when `TOML_EXCEPTIONS` is `1`.
/// The public interface remains the same regardless of exception mode.
/// \remarks This class inherits from `std::runtime_error` when exceptions are enabled.
/// The public interface is the same regardless of exception mode.
class parse_error final
{
private:
@ -511,6 +472,45 @@ namespace toml
}
};
#else
class parse_error final
: public std::runtime_error
{
private:
source_region source_;
public:
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* desc, source_region&& src) noexcept
: std::runtime_error{ desc },
source_{ std::move(src) }
{}
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* desc, const source_region& src) noexcept
: parse_error{ desc, source_region{ src } }
{}
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* desc, const source_position& position, const source_path_ptr& path = {}) noexcept
: parse_error{ desc, source_region{ position, position, path } }
{}
[[nodiscard]]
std::string_view description() const noexcept
{
return std::string_view{ what() };
}
[[nodiscard]]
const source_region& source() const noexcept
{
return source_;
}
};
#endif
TOML_POP_WARNINGS
@ -577,7 +577,11 @@ namespace toml::impl
|| std::is_same_v<T, uint32_t>
|| std::is_same_v<T, uint16_t>
|| std::is_same_v<T, uint8_t>
|| std::is_same_v<T, float>;
|| std::is_same_v<T, float>
#ifdef TOML_SMALL_FLOAT_TYPE
|| std::is_same_v<T, TOML_SMALL_FLOAT_TYPE>
#endif
;
template <typename T>
inline constexpr bool is_value_or_node =
@ -606,6 +610,9 @@ namespace toml::impl
template <> struct value_promoter<uint16_t> { using type = int64_t; };
template <> struct value_promoter<uint8_t> { using type = int64_t; };
template <> struct value_promoter<float> { using type = double; };
#ifdef TOML_SMALL_FLOAT_TYPE
template <> struct value_promoter<TOML_SMALL_FLOAT_TYPE> { using type = double; };
#endif
template <typename T> using promoted = typename impl::value_promoter<T>::type;
inline constexpr toml::string_view low_character_escape_table[] =
@ -682,7 +689,6 @@ namespace toml::impl
namespace toml
{
/// \brief Helper alias that wraps a type up as it's TOML node equivalent.
template <typename T>
using node_of = typename impl::node_wrapper<T>::type;
@ -691,6 +697,33 @@ namespace toml
template <typename T>
using value_of = typename impl::node_unwrapper<T>::type;
/// \brief Metafunction for determining if a type is a toml::table.
template <typename T>
inline constexpr bool is_table = std::is_same_v<impl::remove_cvref_t<T>, table>;
/// \brief Metafunction for determining if a type is a toml::array.
template <typename T>
inline constexpr bool is_array = std::is_same_v<impl::remove_cvref_t<T>, array>;
/// \brief Metafunction for determining if a type is a toml::value<string>.
template <typename T>
inline constexpr bool is_string = std::is_same_v<node_of<impl::remove_cvref_t<T>>, value<string>>;
/// \brief Metafunction for determining if a type is a toml::value<int64_t>.
template <typename T>
inline constexpr bool is_integer = std::is_same_v<node_of<impl::remove_cvref_t<T>>, value<int64_t>>;
/// \brief Metafunction for determining if a type is a toml::value<double>.
template <typename T>
inline constexpr bool is_floating_point = std::is_same_v<node_of<impl::remove_cvref_t<T>>, value<double>>;
/// \brief Metafunction for determining if a type is a toml::value<bool>.
template <typename T>
inline constexpr bool is_boolean = std::is_same_v<node_of<impl::remove_cvref_t<T>>, value<bool>>;
/// \brief Metafunction for determining if a type is a toml::value<toml::date>.
template <typename T>
inline constexpr bool is_date = std::is_same_v<node_of<impl::remove_cvref_t<T>>, value<date>>;
/// \brief Metafunction for determining if a type is a toml::value<toml::time>.
template <typename T>
inline constexpr bool is_time = std::is_same_v<node_of<impl::remove_cvref_t<T>>, value<time>>;
/// \brief Metafunction for determining if a type is a toml::value<toml::date_time>.
template <typename T>
inline constexpr bool is_date_time = std::is_same_v<node_of<impl::remove_cvref_t<T>>, value<date_time>>;
/// \brief Pretty-prints the value of a node_type to a stream.
template <typename CHAR>

View File

@ -3,10 +3,14 @@
namespace toml
{
/// \brief A local date.
struct date final
{
/// \brief The year component.
uint16_t year;
/// \brief The month component, from 1 - 12.
uint8_t month;
/// \brief The day component, from 1 - 31.
uint8_t day;
[[nodiscard]]
@ -34,11 +38,16 @@ namespace toml
}
};
/// \brief A local time-of-day.
struct time final
{
/// \brief The hour component, from 0 - 23.
uint8_t hour;
/// \brief The minute component, from 0 - 59.
uint8_t minute;
/// \brief The second component, from 0 - 59.
uint8_t second;
/// \brief The fractional nanoseconds component, from 0 - 999999999.
uint32_t nanosecond;
[[nodiscard]]
@ -65,8 +74,10 @@ namespace toml
}
};
/// \brief A timezone offset.
struct time_offset final
{
/// \brief Offset from UTC+0, in minutes.
int16_t minutes;
[[nodiscard]]
@ -96,10 +107,14 @@ namespace toml
}
};
/// \brief A date-time.
struct date_time final
{
/// \brief The date component.
toml::date date;
/// \brief The time component.
toml::time time;
/// \brief The timezone offset component.
std::optional<toml::time_offset> time_offset;
[[nodiscard]]

View File

@ -61,8 +61,7 @@ namespace toml::impl
return node.visit([](const auto& n) noexcept
-> size_t
{
using node_t = impl::remove_cvref_t<decltype(n)>;
if constexpr (std::is_same_v<node_t, table>)
if constexpr (is_table<decltype(n)>)
{
if (n.empty())
return 2_sz; // "{}"
@ -71,7 +70,7 @@ namespace toml::impl
weight += k.length() + default_formatter_inline_columns(v) + 2_sz; // + ", "
return weight;
}
else if constexpr (std::is_same_v<node_t, array>)
else if constexpr (is_array<decltype(n)>)
{
if (n.empty())
return 2_sz; // "[]"
@ -80,11 +79,11 @@ namespace toml::impl
weight += default_formatter_inline_columns(elem) + 2_sz; // + ", "
return weight;
}
else if constexpr (std::is_same_v<node_t, value<string>>)
else if constexpr (is_string<decltype(n)>)
{
return n.get().length() + 2_sz; // + ""
}
else if constexpr (std::is_same_v<node_t, value<int64_t>>)
else if constexpr (is_integer<decltype(n)>)
{
auto v = n.get();
if (!v)
@ -97,7 +96,7 @@ namespace toml::impl
}
return weight + static_cast<size_t>(std::log10(static_cast<double>(v)));
}
else if constexpr (std::is_same_v<node_t, value<double>>)
else if constexpr (is_floating_point<decltype(n)>)
{
auto v = n.get();
if (v == 0.0)
@ -110,15 +109,15 @@ namespace toml::impl
}
return weight + static_cast<size_t>(std::log10(v));
}
else if constexpr (std::is_same_v<node_t, value<bool>>)
else if constexpr (is_boolean<decltype(n)>)
{
return 5_sz;
}
else if constexpr (std::is_same_v<node_t, value<date>> || std::is_same_v<node_t, value<time>>)
else if constexpr (is_date<decltype(n)> || is_time<decltype(n)>)
{
return 10_sz;
}
else if constexpr (std::is_same_v<node_t, value<date_time>>)
else if constexpr (is_date_time<decltype(n)>)
{
return 30_sz;
}

View File

@ -98,12 +98,12 @@ namespace toml::impl
}
else
{
static constexpr auto is_date_time =
static constexpr auto is_dt =
std::is_same_v<T, date>
|| std::is_same_v<T, time>
|| std::is_same_v<T, date_time>;
if constexpr (is_date_time)
if constexpr (is_dt)
{
if ((flags_ & format_flags::quote_dates_and_times) != format_flags::none)
print_to_stream('"', *stream_);
@ -111,7 +111,7 @@ namespace toml::impl
*stream_ << val;
if constexpr (is_date_time)
if constexpr (is_dt)
{
if ((flags_ & format_flags::quote_dates_and_times) != format_flags::none)
print_to_stream('"', *stream_);

View File

@ -27,17 +27,18 @@ namespace toml
template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE
node_of<T>* reinterpret_as() noexcept
{
return reinterpret_cast<node_of<T>*>(this);
}
node_of<T>& ref_cast() & noexcept { return *reinterpret_cast<node_of<T>*>(this); }
template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE
const node_of<T>* reinterpret_as() const noexcept
{
return reinterpret_cast<const node_of<T>*>(this);
}
node_of<T>&& ref_cast() && noexcept { return std::move(*reinterpret_cast<node_of<T>*>(this)); }
template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE
const node_of<T>& ref_cast() const & noexcept { return *reinterpret_cast<const node_of<T>*>(this); }
template <typename N, typename T>
using ref_cast_type = decltype(std::declval<N>().template ref_cast<T>());
node() noexcept = default;
node(const node&) = delete;
@ -47,11 +48,9 @@ namespace toml
virtual ~node() noexcept = default;
/// \brief Returns the node's type identifier.
[[nodiscard]] virtual node_type type() const noexcept = 0;
/// \brief Returns true if this node is a table.
[[nodiscard]] virtual bool is_table() const noexcept = 0;
/// \brief Returns true if this node is an array.
@ -76,7 +75,6 @@ namespace toml
/// \brief Returns true if this node is an array containing only tables.
[[nodiscard]] virtual bool is_array_of_tables() const noexcept { return false; }
/// \brief Checks if a node is a specific type.
///
/// \tparam T The
@ -102,15 +100,24 @@ namespace toml
else if constexpr (std::is_same_v<type, time>) return is_time();
else if constexpr (std::is_same_v<type, date_time>) return is_date_time();
}
/// \brief Returns a pointer to the node as a toml::table, if it is one.
[[nodiscard]] virtual table* as_table() noexcept { return nullptr; }
/// \brief Returns a pointer to the node as a toml::array, if it is one.
[[nodiscard]] virtual array* as_array() noexcept { return nullptr; }
/// \brief Returns a pointer to the node as a toml::value<string>, if it is one.
[[nodiscard]] virtual value<string>* as_string() noexcept { return nullptr; }
/// \brief Returns a pointer to the node as a toml::value<int64_t>, if it is one.
[[nodiscard]] virtual value<int64_t>* as_integer() noexcept { return nullptr; }
/// \brief Returns a pointer to the node as a toml::value<double>, if it is one.
[[nodiscard]] virtual value<double>* as_floating_point() noexcept { return nullptr; }
/// \brief Returns a pointer to the node as a toml::value<bool>, if it is one.
[[nodiscard]] virtual value<bool>* as_boolean() noexcept { return nullptr; }
/// \brief Returns a pointer to the node as a toml::value<date>, if it is one.
[[nodiscard]] virtual value<date>* as_date() noexcept { return nullptr; }
/// \brief Returns a pointer to the node as a toml::value<time>, if it is one.
[[nodiscard]] virtual value<time>* as_time() noexcept { return nullptr; }
/// \brief Returns a pointer to the node as a toml::value<date_time>, if it is one.
[[nodiscard]] virtual value<date_time>* as_date_time() noexcept { return nullptr; }
[[nodiscard]] virtual const table* as_table() const noexcept { return nullptr; }
@ -123,6 +130,27 @@ namespace toml
[[nodiscard]] virtual const value<time>* as_time() const noexcept { return nullptr; }
[[nodiscard]] virtual const value<date_time>* as_date_time() const noexcept { return nullptr; }
/// \brief Gets a pointer to the node as a more specific node type.
///
/// \details \cpp
///
/// auto int_value = node->as<int64_t>();
/// auto tbl = node->as<toml::table>();
/// if (int_value)
/// std::cout << "Node is a value<int64_t>" << std::endl;
/// else if (tbl)
/// std::cout << "Node is a table" << std::endl;
///
/// // fully-qualified value node types also work:
/// auto int_value2 = node->as<toml::value<int64_t>>();
/// if (int_value2)
/// std::cout << "Node is a value<int64_t>" << std::endl;
///
/// \ecpp
///
/// \tparam T 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.
template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE
node_of<T>* as() noexcept
@ -144,6 +172,7 @@ namespace toml
else if constexpr (std::is_same_v<type, date_time>) return as_date_time();
}
/// \brief Gets a pointer to the node as a more specific node type (const overload).
template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE
const node_of<T>* as() const noexcept
@ -165,6 +194,7 @@ namespace toml
else if constexpr (std::is_same_v<type, date_time>) return as_date_time();
}
/// \brief Returns the source region responsible for generating this node during parsing.
[[nodiscard]] const source_region& source() const noexcept
{
return source_;
@ -172,94 +202,166 @@ namespace toml
private:
// this is done using a static helper to preserve const categories
// (otherwise I'd have to implement this function twice)
// (const propagation in C++: a modern horror story)
template <typename FUNC, typename N, typename T, bool = std::is_invocable_v<FUNC, ref_cast_type<N, T>>>
struct visit_return_type final
{
using type = decltype(std::declval<FUNC>()(std::declval<ref_cast_type<N, T>>()));
};
template <typename FUNC, typename N, typename T>
struct visit_return_type<FUNC, N, T, false> final
{
using type = void;
};
template <typename A, typename B>
using nonvoid = std::conditional_t<std::is_void_v<A>, B, A>;
// this is done using a static helper to preserve const and ref categories
// (otherwise I'd have to implement this function thrice)
// ((propagation in C++: a modern horror story))
template <typename N, typename FUNC>
TOML_GCC_ATTR(nonnull)
static decltype(auto) do_visit(N* node, FUNC&& visitor) TOML_MAY_THROW
static decltype(auto) do_visit(N&& node, FUNC&& visitor) TOML_MAY_THROW
{
static_assert(
std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<table>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<array>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<string>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<int64_t>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<double>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<bool>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<time>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date_time>())>,
std::is_invocable_v<FUNC&&, ref_cast_type<N&&, table>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, array>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, string>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, int64_t>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, double>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, bool>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, time>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date_time>>,
"Visitors must be invocable for at least one of the toml::node specializations"
);
static constexpr auto is_exhaustive =
std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<table>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<array>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<string>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<int64_t>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<double>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<bool>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<time>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date_time>())>;
switch (node->type())
switch (node.type())
{
case node_type::table:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<table>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<table>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, table>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<table>());
break;
case node_type::array:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<array>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<array>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, array>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<array>());
break;
case node_type::string:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<string>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<string>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, string>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<string>());
break;
case node_type::integer:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<int64_t>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<int64_t>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, int64_t>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<int64_t>());
break;
case node_type::floating_point:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<double>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<double>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, double>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<double>());
break;
case node_type::boolean:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<bool>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<bool>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, bool>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<bool>());
break;
case node_type::date:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<date>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<date>());
break;
case node_type::time:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<time>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<time>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, time>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<time>());
break;
case node_type::date_time:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date_time>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<date_time>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date_time>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<date_time>());
break;
TOML_NO_DEFAULT_CASE;
}
static constexpr auto is_exhaustive =
std::is_invocable_v<FUNC&&, ref_cast_type<N&&, table>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, array>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, string>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, int64_t>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, double>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, bool>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, time>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date_time>>;
if constexpr (is_exhaustive)
TOML_UNREACHABLE;
else
{
using return_type =
nonvoid<typename visit_return_type<FUNC&&, N&&, table>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, array>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, string>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, int64_t>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, double>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, bool>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, date>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, time>::type,
typename visit_return_type<FUNC&&, N&&, date_time>::type
>>>>>>>>;
if constexpr (!std::is_void_v<return_type>)
{
static_assert(
std::is_default_constructible_v<return_type>,
"Non-exhaustive visitors must return a default-constructible type, or void"
);
return return_type{};
}
}
}
public:
/// \brief Invokes a visitor on the node based on the node's concrete type.
///
/// \details Visitation is useful when you expect
/// a node to be one of a set number of types and need
/// to handle these types differently. Using `visit()` allows
/// you to eliminate some of the casting/conversion boilerplate: \cpp
///
/// node.visit([](auto&& n)
/// {
/// if constexpr (toml::is_string<decltype(n)>)
/// do_something_with_a_string(n.get());
/// else if constexpr (toml::is_floating_point<decltype(n)>)
/// do_something_with_a_float(n.get());
/// else
/// throw std::exception("Expected string or integer")
/// });
///
/// \ecpp
///
/// \tparam FUNC A callable type invocable with one or more of the
/// toml++ node types.
///
/// \param visitor The visitor object.
///
/// \returns The return value of the visitor.
/// Must be default-constructible if non-void and your visitor is not exhaustive.
///
/// \see https://en.wikipedia.org/wiki/Visitor_pattern
template <typename FUNC>
decltype(auto) visit(FUNC&& visitor) TOML_MAY_THROW
decltype(auto) visit(FUNC&& visitor) & TOML_MAY_THROW
{
return do_visit(this, std::forward<FUNC>(visitor));
return do_visit(*this, std::forward<FUNC>(visitor));
}
/// \brief Invokes a visitor on the node based on the node's concrete type (rvalue overload).
template <typename FUNC>
decltype(auto) visit(FUNC&& visitor) const TOML_MAY_THROW
decltype(auto) visit(FUNC&& visitor) && TOML_MAY_THROW
{
return do_visit(this, std::forward<FUNC>(visitor));
return do_visit(std::move(*this), std::forward<FUNC>(visitor));
}
/// \brief Invokes a visitor on the node based on the node's concrete type (const overload).
template <typename FUNC>
decltype(auto) visit(FUNC&& visitor) const& TOML_MAY_THROW
{
return do_visit(*this, std::forward<FUNC>(visitor));
}
};
}

View File

@ -6,12 +6,12 @@
namespace toml
{
#if TOML_EXCEPTIONS
using parse_result = table;
#else
#if TOML_DOXYGEN || !TOML_EXCEPTIONS
/// \brief The result of a parsing operation.
///
/// \remarks This type only exists when exceptions are disabled,
/// otherwise `parse_result` is a simple alias for `toml::table`.
class parse_result final
{
private:
@ -69,7 +69,7 @@ namespace toml
[[nodiscard]] table& operator* () & noexcept { return get(); }
[[nodiscard]] table&& operator* () && noexcept { return std::move(get()); }
[[nodiscard]] const table& operator* () const & noexcept { return get(); }
[[nodiscard]] const table& operator* () const& noexcept { return get(); }
[[nodiscard]] table* operator-> () noexcept { return &get(); }
[[nodiscard]] const table* operator-> () const noexcept { return &get(); }
@ -133,6 +133,10 @@ namespace toml
}
};
#else
using parse_result = table;
#endif
}
@ -1938,7 +1942,7 @@ namespace toml::impl
if constexpr (!TOML_LANG_HIGHER_THAN(0, 5, 0)) // toml/issues/665
{
// arrays must be homogeneous in toml 0.5.0 and earlier
if (!val->reinterpret_as<array>()->is_homogeneous())
if (!val->ref_cast<array>().is_homogeneous())
TOML_ERROR(
"Arrays cannot contain values of different types in TOML 0.5.0 and earlier.",
begin_pos,
@ -2406,7 +2410,7 @@ namespace toml::impl
const auto header_begin_pos = cp->position;
source_position header_end_pos;
key key;
bool is_array = false;
bool is_arr = false;
//parse header
{
@ -2426,7 +2430,7 @@ namespace toml::impl
{
advance();
eof_check();
is_array = true;
is_arr = true;
}
// skip past any whitespace that followed the '['
@ -2464,12 +2468,12 @@ namespace toml::impl
if (*cp != U']')
abort_with_error(
"Encountered unexpected character while parsing table"sv,
(is_array ? " array"sv : ""sv), " header; expected ']', saw '"sv, *cp, '\''
(is_arr ? " array"sv : ""sv), " header; expected ']', saw '"sv, *cp, '\''
);
advance();
// consume the second closing ']'
if (is_array)
if (is_arr)
{
eof_check();
@ -2488,7 +2492,7 @@ namespace toml::impl
{
if (!consume_comment() && !consume_line_break())
abort_with_error(
"Encountered unexpected character after table"sv, (is_array ? " array"sv : ""sv),
"Encountered unexpected character after table"sv, (is_arr ? " array"sv : ""sv),
" header; expected a comment or whitespace, saw '"sv, *cp, '\''
);
}
@ -2507,31 +2511,31 @@ namespace toml::impl
key.segments[i],
std::make_unique<table>()
).first->second.get();
implicit_tables.push_back(child->reinterpret_as<table>());
implicit_tables.push_back(&child->ref_cast<table>());
child->source_ = { header_begin_pos, header_end_pos, reader.source_path() };
parent = child->reinterpret_as<table>();
parent = &child->ref_cast<table>();
}
else if (child->is_table())
{
parent = child->reinterpret_as<table>();
parent = &child->ref_cast<table>();
}
else if (child->is_array() && find(table_arrays, child->reinterpret_as<array>()))
else if (child->is_array() && find(table_arrays, &child->ref_cast<array>()))
{
// table arrays are a special case;
// the spec dictates we select the most recently declared element in the array.
TOML_ASSERT(!child->reinterpret_as<array>()->values.empty());
TOML_ASSERT(child->reinterpret_as<array>()->values.back()->is_table());
parent = child->reinterpret_as<array>()->values.back()->reinterpret_as<table>();
TOML_ASSERT(!child->ref_cast<array>().values.empty());
TOML_ASSERT(child->ref_cast<array>().values.back()->is_table());
parent = &child->ref_cast<array>().values.back()->ref_cast<table>();
}
else
{
if (!is_array && child->type() == node_type::table)
if (!is_arr && child->type() == node_type::table)
abort_with_error("Attempt to redefine existing table '"sv, recording_buffer, '\'');
else
abort_with_error(
"Attempt to redefine existing "sv, child->type(),
" '"sv, recording_buffer,
"' as "sv, is_array ? "array-of-tables"sv : "table"sv
"' as "sv, is_arr ? "array-of-tables"sv : "table"sv
);
}
}
@ -2545,23 +2549,23 @@ namespace toml::impl
{
// if it's an array we need to make the array and it's first table element,
// set the starting regions, and return the table element
if (is_array)
if (is_arr)
{
auto tab_arr = parent->values.emplace(key.segments.back(),std::make_unique<array>())
.first->second->reinterpret_as<array>();
auto tab_arr = &parent->values.emplace(key.segments.back(),std::make_unique<array>())
.first->second->ref_cast<array>();
table_arrays.push_back(tab_arr);
tab_arr->source_ = { header_begin_pos, header_end_pos, reader.source_path() };
tab_arr->values.push_back(std::make_unique<table>());
tab_arr->values.back()->source_ = { header_begin_pos, header_end_pos, reader.source_path() };
return tab_arr->values.back()->reinterpret_as<table>();
return &tab_arr->values.back()->ref_cast<table>();
}
//otherwise we're just making a table
else
{
auto tab = parent->values.emplace(key.segments.back(),std::make_unique<table>())
.first->second->reinterpret_as<table>();
auto tab = &parent->values.emplace(key.segments.back(),std::make_unique<table>())
.first->second->ref_cast<table>();
tab->source_ = { header_begin_pos, header_end_pos, reader.source_path() };
return tab;
}
@ -2573,19 +2577,19 @@ namespace toml::impl
// otherwise this is a redefinition error.
else
{
if (is_array && matching_node->is_array() && find(table_arrays, matching_node->reinterpret_as<array>()))
if (is_arr && matching_node->is_array() && find(table_arrays, &matching_node->ref_cast<array>()))
{
auto tab_arr = matching_node->reinterpret_as<array>();
auto tab_arr = &matching_node->ref_cast<array>();
tab_arr->values.push_back(std::make_unique<table>());
tab_arr->values.back()->source_ = { header_begin_pos, header_end_pos, reader.source_path() };
return tab_arr->values.back()->reinterpret_as<table>();
return &tab_arr->values.back()->ref_cast<table>();
}
else if (!is_array
else if (!is_arr
&& matching_node->is_table()
&& !implicit_tables.empty())
{
auto tbl = matching_node->reinterpret_as<table>();
auto tbl = &matching_node->ref_cast<table>();
const auto idx = find(implicit_tables, tbl);
if (idx)
{
@ -2597,13 +2601,13 @@ namespace toml::impl
}
//if we get here it's a redefinition error.
if (!is_array && matching_node->type() == node_type::table)
if (!is_arr && matching_node->type() == node_type::table)
abort_with_error("Attempt to redefine existing table '"sv, recording_buffer, '\'');
else
abort_with_error(
"Attempt to redefine existing "sv, matching_node->type(),
" '"sv, recording_buffer,
"' as "sv, is_array ? "array-of-tables"sv : "table"sv
"' as "sv, is_arr ? "array-of-tables"sv : "table"sv
);
}
TOML_ERROR_CHECK({});
@ -2629,18 +2633,18 @@ namespace toml::impl
std::move(kvp.key.segments[i]),
std::make_unique<table>()
).first->second.get();
dotted_key_tables.push_back(child->reinterpret_as<table>());
dotted_key_tables.push_back(&child->ref_cast<table>());
dotted_key_tables.back()->inline_ = true;
child->source_ = kvp.value->source_;
}
else if (!child->is_table() || !find(dotted_key_tables, child->reinterpret_as<table>()))
else if (!child->is_table() || !find(dotted_key_tables, &child->ref_cast<table>()))
{
abort_with_error("Attempt to redefine "sv, child->type(), " as dotted key-value pair"sv);
}
else
child->source_.end = kvp.value->source_.end;
TOML_ERROR_CHECK();
tab = child->reinterpret_as<table>();
tab = &child->ref_cast<table>();
}
}
@ -2739,7 +2743,7 @@ namespace toml::impl
if (type == node_type::table)
{
auto& tbl = *nde.reinterpret_as<table>();
auto& tbl = nde.ref_cast<table>();
if (tbl.inline_) //inline tables (and all their inline descendants) are already correctly terminated
return;
@ -2753,7 +2757,7 @@ namespace toml::impl
}
else //arrays
{
auto& arr = *nde.reinterpret_as<array>();
auto& arr = nde.ref_cast<array>();
auto end = nde.source_.end;
for (auto& v : arr.values)
{

View File

@ -160,7 +160,7 @@ namespace toml::impl
template <typename CHAR>
TOML_ALWAYS_INLINE
void print_to_stream(bool& val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
void print_to_stream(bool val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
{
static_assert(sizeof(CHAR) == 1);
print_to_stream(val ? "true"sv : "false"sv, stream);

View File

@ -132,4 +132,7 @@ namespace toml
value(uint8_t) -> value<int64_t>;
value(uint16_t) -> value<int64_t>;
value(uint32_t) -> value<int64_t>;
#ifdef TOML_SMALL_FLOAT_TYPE
value(TOML_SMALL_FLOAT_TYPE) -> value<double>;
#endif
}

View File

@ -708,8 +708,8 @@ def main():
# post-process html files
fixes = [
CustomTagsFix()
, NavBarFix()
, IndexHrefFix()
# , NavBarFix()
# , IndexHrefFix()
, ModifiersFix1()
, ModifiersFix2()
, InlineNamespaceFix1()

374
toml.hpp
View File

@ -22,7 +22,7 @@
//
// MIT License
//
// Copyright (c) 2019 Mark Gillard
// Copyright (c) 2019-2020 Mark Gillard
//
// 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
@ -81,7 +81,7 @@
#error toml++ is a C++ library.
#endif
#if defined(__clang__) or defined(__GNUC__)
#if defined(__clang__) || defined(__GNUC__)
#define TOML_GCC_ATTR(attr) __attribute__((attr))
#else
#define TOML_GCC_ATTR(attr)
@ -441,46 +441,7 @@ namespace toml
TOML_PUSH_WARNINGS
TOML_DISABLE_INIT_WARNINGS
#if !TOML_DOXYGEN && TOML_EXCEPTIONS
class parse_error final
: public std::runtime_error
{
private:
source_region source_;
public:
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* desc, source_region&& src) noexcept
: std::runtime_error{ desc },
source_{ std::move(src) }
{}
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* desc, const source_region& src) noexcept
: parse_error{ desc, source_region{ src } }
{}
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* desc, const source_position& position, const source_path_ptr& path = {}) noexcept
: parse_error{ desc, source_region{ position, position, path } }
{}
[[nodiscard]]
std::string_view description() const noexcept
{
return std::string_view{ what() };
}
[[nodiscard]]
const source_region& source() const noexcept
{
return source_;
}
};
#else
#if TOML_DOXYGEN || !TOML_EXCEPTIONS
class parse_error final
{
@ -519,6 +480,45 @@ namespace toml
}
};
#else
class parse_error final
: public std::runtime_error
{
private:
source_region source_;
public:
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* desc, source_region&& src) noexcept
: std::runtime_error{ desc },
source_{ std::move(src) }
{}
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* desc, const source_region& src) noexcept
: parse_error{ desc, source_region{ src } }
{}
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* desc, const source_position& position, const source_path_ptr& path = {}) noexcept
: parse_error{ desc, source_region{ position, position, path } }
{}
[[nodiscard]]
std::string_view description() const noexcept
{
return std::string_view{ what() };
}
[[nodiscard]]
const source_region& source() const noexcept
{
return source_;
}
};
#endif
TOML_POP_WARNINGS
@ -584,7 +584,11 @@ namespace toml::impl
|| std::is_same_v<T, uint32_t>
|| std::is_same_v<T, uint16_t>
|| std::is_same_v<T, uint8_t>
|| std::is_same_v<T, float>;
|| std::is_same_v<T, float>
#ifdef TOML_SMALL_FLOAT_TYPE
|| std::is_same_v<T, TOML_SMALL_FLOAT_TYPE>
#endif
;
template <typename T>
inline constexpr bool is_value_or_node =
@ -613,6 +617,9 @@ namespace toml::impl
template <> struct value_promoter<uint16_t> { using type = int64_t; };
template <> struct value_promoter<uint8_t> { using type = int64_t; };
template <> struct value_promoter<float> { using type = double; };
#ifdef TOML_SMALL_FLOAT_TYPE
template <> struct value_promoter<TOML_SMALL_FLOAT_TYPE> { using type = double; };
#endif
template <typename T> using promoted = typename impl::value_promoter<T>::type;
inline constexpr toml::string_view low_character_escape_table[] =
@ -688,13 +695,31 @@ namespace toml::impl
namespace toml
{
template <typename T>
using node_of = typename impl::node_wrapper<T>::type;
template <typename T>
using value_of = typename impl::node_unwrapper<T>::type;
template <typename T>
inline constexpr bool is_table = std::is_same_v<impl::remove_cvref_t<T>, table>;
template <typename T>
inline constexpr bool is_array = std::is_same_v<impl::remove_cvref_t<T>, array>;
template <typename T>
inline constexpr bool is_string = std::is_same_v<node_of<impl::remove_cvref_t<T>>, value<string>>;
template <typename T>
inline constexpr bool is_integer = std::is_same_v<node_of<impl::remove_cvref_t<T>>, value<int64_t>>;
template <typename T>
inline constexpr bool is_floating_point = std::is_same_v<node_of<impl::remove_cvref_t<T>>, value<double>>;
template <typename T>
inline constexpr bool is_boolean = std::is_same_v<node_of<impl::remove_cvref_t<T>>, value<bool>>;
template <typename T>
inline constexpr bool is_date = std::is_same_v<node_of<impl::remove_cvref_t<T>>, value<date>>;
template <typename T>
inline constexpr bool is_time = std::is_same_v<node_of<impl::remove_cvref_t<T>>, value<time>>;
template <typename T>
inline constexpr bool is_date_time = std::is_same_v<node_of<impl::remove_cvref_t<T>>, value<date_time>>;
template <typename CHAR>
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, node_type rhs) TOML_MAY_THROW
{
@ -1015,7 +1040,7 @@ namespace toml::impl
template <typename CHAR>
TOML_ALWAYS_INLINE
void print_to_stream(bool& val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
void print_to_stream(bool val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
{
static_assert(sizeof(CHAR) == 1);
print_to_stream(val ? "true"sv : "false"sv, stream);
@ -1159,17 +1184,18 @@ namespace toml
template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE
node_of<T>* reinterpret_as() noexcept
{
return reinterpret_cast<node_of<T>*>(this);
}
node_of<T>& ref_cast() & noexcept { return *reinterpret_cast<node_of<T>*>(this); }
template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE
const node_of<T>* reinterpret_as() const noexcept
{
return reinterpret_cast<const node_of<T>*>(this);
}
node_of<T>&& ref_cast() && noexcept { return std::move(*reinterpret_cast<node_of<T>*>(this)); }
template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE
const node_of<T>& ref_cast() const & noexcept { return *reinterpret_cast<const node_of<T>*>(this); }
template <typename N, typename T>
using ref_cast_type = decltype(std::declval<N>().template ref_cast<T>());
node() noexcept = default;
node(const node&) = delete;
@ -1281,94 +1307,136 @@ namespace toml
private:
// this is done using a static helper to preserve const categories
// (otherwise I'd have to implement this function twice)
// (const propagation in C++: a modern horror story)
template <typename FUNC, typename N, typename T, bool = std::is_invocable_v<FUNC, ref_cast_type<N, T>>>
struct visit_return_type final
{
using type = decltype(std::declval<FUNC>()(std::declval<ref_cast_type<N, T>>()));
};
template <typename FUNC, typename N, typename T>
struct visit_return_type<FUNC, N, T, false> final
{
using type = void;
};
template <typename A, typename B>
using nonvoid = std::conditional_t<std::is_void_v<A>, B, A>;
// this is done using a static helper to preserve const and ref categories
// (otherwise I'd have to implement this function thrice)
// ((propagation in C++: a modern horror story))
template <typename N, typename FUNC>
TOML_GCC_ATTR(nonnull)
static decltype(auto) do_visit(N* node, FUNC&& visitor) TOML_MAY_THROW
static decltype(auto) do_visit(N&& node, FUNC&& visitor) TOML_MAY_THROW
{
static_assert(
std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<table>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<array>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<string>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<int64_t>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<double>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<bool>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<time>())>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date_time>())>,
std::is_invocable_v<FUNC&&, ref_cast_type<N&&, table>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, array>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, string>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, int64_t>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, double>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, bool>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, time>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date_time>>,
"Visitors must be invocable for at least one of the toml::node specializations"
);
static constexpr auto is_exhaustive =
std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<table>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<array>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<string>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<int64_t>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<double>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<bool>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<time>())>
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date_time>())>;
switch (node->type())
switch (node.type())
{
case node_type::table:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<table>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<table>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, table>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<table>());
break;
case node_type::array:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<array>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<array>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, array>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<array>());
break;
case node_type::string:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<string>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<string>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, string>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<string>());
break;
case node_type::integer:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<int64_t>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<int64_t>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, int64_t>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<int64_t>());
break;
case node_type::floating_point:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<double>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<double>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, double>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<double>());
break;
case node_type::boolean:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<bool>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<bool>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, bool>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<bool>());
break;
case node_type::date:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<date>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<date>());
break;
case node_type::time:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<time>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<time>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, time>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<time>());
break;
case node_type::date_time:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date_time>())>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<date_time>());
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date_time>>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<date_time>());
break;
TOML_NO_DEFAULT_CASE;
}
static constexpr auto is_exhaustive =
std::is_invocable_v<FUNC&&, ref_cast_type<N&&, table>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, array>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, string>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, int64_t>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, double>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, bool>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, time>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date_time>>;
if constexpr (is_exhaustive)
TOML_UNREACHABLE;
else
{
using return_type =
nonvoid<typename visit_return_type<FUNC&&, N&&, table>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, array>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, string>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, int64_t>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, double>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, bool>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, date>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, time>::type,
typename visit_return_type<FUNC&&, N&&, date_time>::type
>>>>>>>>;
if constexpr (!std::is_void_v<return_type>)
{
static_assert(
std::is_default_constructible_v<return_type>,
"Non-exhaustive visitors must return a default-constructible type, or void"
);
return return_type{};
}
}
}
public:
template <typename FUNC>
decltype(auto) visit(FUNC&& visitor) TOML_MAY_THROW
decltype(auto) visit(FUNC&& visitor) & TOML_MAY_THROW
{
return do_visit(this, std::forward<FUNC>(visitor));
return do_visit(*this, std::forward<FUNC>(visitor));
}
template <typename FUNC>
decltype(auto) visit(FUNC&& visitor) const TOML_MAY_THROW
decltype(auto) visit(FUNC&& visitor) && TOML_MAY_THROW
{
return do_visit(this, std::forward<FUNC>(visitor));
return do_visit(std::move(*this), std::forward<FUNC>(visitor));
}
template <typename FUNC>
decltype(auto) visit(FUNC&& visitor) const& TOML_MAY_THROW
{
return do_visit(*this, std::forward<FUNC>(visitor));
}
};
}
@ -1934,6 +2002,9 @@ namespace toml
value(uint8_t) -> value<int64_t>;
value(uint16_t) -> value<int64_t>;
value(uint32_t) -> value<int64_t>;
#ifdef TOML_SMALL_FLOAT_TYPE
value(TOML_SMALL_FLOAT_TYPE) -> value<double>;
#endif
}
#pragma endregion
@ -3816,11 +3887,7 @@ namespace toml::impl
namespace toml
{
#if TOML_EXCEPTIONS
using parse_result = table;
#else
#if TOML_DOXYGEN || !TOML_EXCEPTIONS
class parse_result final
{
@ -3878,7 +3945,7 @@ namespace toml
[[nodiscard]] table& operator* () & noexcept { return get(); }
[[nodiscard]] table&& operator* () && noexcept { return std::move(get()); }
[[nodiscard]] const table& operator* () const & noexcept { return get(); }
[[nodiscard]] const table& operator* () const& noexcept { return get(); }
[[nodiscard]] table* operator-> () noexcept { return &get(); }
[[nodiscard]] const table* operator-> () const noexcept { return &get(); }
[[nodiscard]] operator table& () noexcept { return get(); }
@ -3939,6 +4006,10 @@ namespace toml
}
};
#else
using parse_result = table;
#endif
}
@ -5742,7 +5813,7 @@ namespace toml::impl
if constexpr (!TOML_LANG_HIGHER_THAN(0, 5, 0)) // toml/issues/665
{
// arrays must be homogeneous in toml 0.5.0 and earlier
if (!val->reinterpret_as<array>()->is_homogeneous())
if (!val->ref_cast<array>().is_homogeneous())
TOML_ERROR(
"Arrays cannot contain values of different types in TOML 0.5.0 and earlier.",
begin_pos,
@ -6210,7 +6281,7 @@ namespace toml::impl
const auto header_begin_pos = cp->position;
source_position header_end_pos;
key key;
bool is_array = false;
bool is_arr = false;
//parse header
{
@ -6230,7 +6301,7 @@ namespace toml::impl
{
advance();
eof_check();
is_array = true;
is_arr = true;
}
// skip past any whitespace that followed the '['
@ -6268,12 +6339,12 @@ namespace toml::impl
if (*cp != U']')
abort_with_error(
"Encountered unexpected character while parsing table"sv,
(is_array ? " array"sv : ""sv), " header; expected ']', saw '"sv, *cp, '\''
(is_arr ? " array"sv : ""sv), " header; expected ']', saw '"sv, *cp, '\''
);
advance();
// consume the second closing ']'
if (is_array)
if (is_arr)
{
eof_check();
@ -6292,7 +6363,7 @@ namespace toml::impl
{
if (!consume_comment() && !consume_line_break())
abort_with_error(
"Encountered unexpected character after table"sv, (is_array ? " array"sv : ""sv),
"Encountered unexpected character after table"sv, (is_arr ? " array"sv : ""sv),
" header; expected a comment or whitespace, saw '"sv, *cp, '\''
);
}
@ -6311,31 +6382,31 @@ namespace toml::impl
key.segments[i],
std::make_unique<table>()
).first->second.get();
implicit_tables.push_back(child->reinterpret_as<table>());
implicit_tables.push_back(&child->ref_cast<table>());
child->source_ = { header_begin_pos, header_end_pos, reader.source_path() };
parent = child->reinterpret_as<table>();
parent = &child->ref_cast<table>();
}
else if (child->is_table())
{
parent = child->reinterpret_as<table>();
parent = &child->ref_cast<table>();
}
else if (child->is_array() && find(table_arrays, child->reinterpret_as<array>()))
else if (child->is_array() && find(table_arrays, &child->ref_cast<array>()))
{
// table arrays are a special case;
// the spec dictates we select the most recently declared element in the array.
TOML_ASSERT(!child->reinterpret_as<array>()->values.empty());
TOML_ASSERT(child->reinterpret_as<array>()->values.back()->is_table());
parent = child->reinterpret_as<array>()->values.back()->reinterpret_as<table>();
TOML_ASSERT(!child->ref_cast<array>().values.empty());
TOML_ASSERT(child->ref_cast<array>().values.back()->is_table());
parent = &child->ref_cast<array>().values.back()->ref_cast<table>();
}
else
{
if (!is_array && child->type() == node_type::table)
if (!is_arr && child->type() == node_type::table)
abort_with_error("Attempt to redefine existing table '"sv, recording_buffer, '\'');
else
abort_with_error(
"Attempt to redefine existing "sv, child->type(),
" '"sv, recording_buffer,
"' as "sv, is_array ? "array-of-tables"sv : "table"sv
"' as "sv, is_arr ? "array-of-tables"sv : "table"sv
);
}
}
@ -6349,23 +6420,23 @@ namespace toml::impl
{
// if it's an array we need to make the array and it's first table element,
// set the starting regions, and return the table element
if (is_array)
if (is_arr)
{
auto tab_arr = parent->values.emplace(key.segments.back(),std::make_unique<array>())
.first->second->reinterpret_as<array>();
auto tab_arr = &parent->values.emplace(key.segments.back(),std::make_unique<array>())
.first->second->ref_cast<array>();
table_arrays.push_back(tab_arr);
tab_arr->source_ = { header_begin_pos, header_end_pos, reader.source_path() };
tab_arr->values.push_back(std::make_unique<table>());
tab_arr->values.back()->source_ = { header_begin_pos, header_end_pos, reader.source_path() };
return tab_arr->values.back()->reinterpret_as<table>();
return &tab_arr->values.back()->ref_cast<table>();
}
//otherwise we're just making a table
else
{
auto tab = parent->values.emplace(key.segments.back(),std::make_unique<table>())
.first->second->reinterpret_as<table>();
auto tab = &parent->values.emplace(key.segments.back(),std::make_unique<table>())
.first->second->ref_cast<table>();
tab->source_ = { header_begin_pos, header_end_pos, reader.source_path() };
return tab;
}
@ -6377,19 +6448,19 @@ namespace toml::impl
// otherwise this is a redefinition error.
else
{
if (is_array && matching_node->is_array() && find(table_arrays, matching_node->reinterpret_as<array>()))
if (is_arr && matching_node->is_array() && find(table_arrays, &matching_node->ref_cast<array>()))
{
auto tab_arr = matching_node->reinterpret_as<array>();
auto tab_arr = &matching_node->ref_cast<array>();
tab_arr->values.push_back(std::make_unique<table>());
tab_arr->values.back()->source_ = { header_begin_pos, header_end_pos, reader.source_path() };
return tab_arr->values.back()->reinterpret_as<table>();
return &tab_arr->values.back()->ref_cast<table>();
}
else if (!is_array
else if (!is_arr
&& matching_node->is_table()
&& !implicit_tables.empty())
{
auto tbl = matching_node->reinterpret_as<table>();
auto tbl = &matching_node->ref_cast<table>();
const auto idx = find(implicit_tables, tbl);
if (idx)
{
@ -6401,13 +6472,13 @@ namespace toml::impl
}
//if we get here it's a redefinition error.
if (!is_array && matching_node->type() == node_type::table)
if (!is_arr && matching_node->type() == node_type::table)
abort_with_error("Attempt to redefine existing table '"sv, recording_buffer, '\'');
else
abort_with_error(
"Attempt to redefine existing "sv, matching_node->type(),
" '"sv, recording_buffer,
"' as "sv, is_array ? "array-of-tables"sv : "table"sv
"' as "sv, is_arr ? "array-of-tables"sv : "table"sv
);
}
TOML_ERROR_CHECK({});
@ -6433,18 +6504,18 @@ namespace toml::impl
std::move(kvp.key.segments[i]),
std::make_unique<table>()
).first->second.get();
dotted_key_tables.push_back(child->reinterpret_as<table>());
dotted_key_tables.push_back(&child->ref_cast<table>());
dotted_key_tables.back()->inline_ = true;
child->source_ = kvp.value->source_;
}
else if (!child->is_table() || !find(dotted_key_tables, child->reinterpret_as<table>()))
else if (!child->is_table() || !find(dotted_key_tables, &child->ref_cast<table>()))
{
abort_with_error("Attempt to redefine "sv, child->type(), " as dotted key-value pair"sv);
}
else
child->source_.end = kvp.value->source_.end;
TOML_ERROR_CHECK();
tab = child->reinterpret_as<table>();
tab = &child->ref_cast<table>();
}
}
@ -6543,7 +6614,7 @@ namespace toml::impl
if (type == node_type::table)
{
auto& tbl = *nde.reinterpret_as<table>();
auto& tbl = nde.ref_cast<table>();
if (tbl.inline_) //inline tables (and all their inline descendants) are already correctly terminated
return;
@ -6557,7 +6628,7 @@ namespace toml::impl
}
else //arrays
{
auto& arr = *nde.reinterpret_as<array>();
auto& arr = nde.ref_cast<array>();
auto end = nde.source_.end;
for (auto& v : arr.values)
{
@ -6978,12 +7049,12 @@ namespace toml::impl
}
else
{
static constexpr auto is_date_time =
static constexpr auto is_dt =
std::is_same_v<T, date>
|| std::is_same_v<T, time>
|| std::is_same_v<T, date_time>;
if constexpr (is_date_time)
if constexpr (is_dt)
{
if ((flags_ & format_flags::quote_dates_and_times) != format_flags::none)
print_to_stream('"', *stream_);
@ -6991,7 +7062,7 @@ namespace toml::impl
*stream_ << val;
if constexpr (is_date_time)
if constexpr (is_dt)
{
if ((flags_ & format_flags::quote_dates_and_times) != format_flags::none)
print_to_stream('"', *stream_);
@ -7086,8 +7157,7 @@ namespace toml::impl
return node.visit([](const auto& n) noexcept
-> size_t
{
using node_t = impl::remove_cvref_t<decltype(n)>;
if constexpr (std::is_same_v<node_t, table>)
if constexpr (is_table<decltype(n)>)
{
if (n.empty())
return 2_sz; // "{}"
@ -7096,7 +7166,7 @@ namespace toml::impl
weight += k.length() + default_formatter_inline_columns(v) + 2_sz; // + ", "
return weight;
}
else if constexpr (std::is_same_v<node_t, array>)
else if constexpr (is_array<decltype(n)>)
{
if (n.empty())
return 2_sz; // "[]"
@ -7105,11 +7175,11 @@ namespace toml::impl
weight += default_formatter_inline_columns(elem) + 2_sz; // + ", "
return weight;
}
else if constexpr (std::is_same_v<node_t, value<string>>)
else if constexpr (is_string<decltype(n)>)
{
return n.get().length() + 2_sz; // + ""
}
else if constexpr (std::is_same_v<node_t, value<int64_t>>)
else if constexpr (is_integer<decltype(n)>)
{
auto v = n.get();
if (!v)
@ -7122,7 +7192,7 @@ namespace toml::impl
}
return weight + static_cast<size_t>(std::log10(static_cast<double>(v)));
}
else if constexpr (std::is_same_v<node_t, value<double>>)
else if constexpr (is_floating_point<decltype(n)>)
{
auto v = n.get();
if (v == 0.0)
@ -7135,15 +7205,15 @@ namespace toml::impl
}
return weight + static_cast<size_t>(std::log10(v));
}
else if constexpr (std::is_same_v<node_t, value<bool>>)
else if constexpr (is_boolean<decltype(n)>)
{
return 5_sz;
}
else if constexpr (std::is_same_v<node_t, value<date>> || std::is_same_v<node_t, value<time>>)
else if constexpr (is_date<decltype(n)> || is_time<decltype(n)>)
{
return 10_sz;
}
else if constexpr (std::is_same_v<node_t, value<date_time>>)
else if constexpr (is_date_time<decltype(n)>)
{
return 30_sz;
}