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 *.sln text encoding=UTF-8-BOM eol=crlf
*.runsettings text encoding=UTF-8 eol=crlf *.runsettings text encoding=UTF-8 eol=crlf
*.md text encoding=UTF-8 eol=lf *.md text encoding=UTF-8 eol=lf
*.css text encoding=UTF-8 eol=lf

View File

@ -1,6 +1,6 @@
MIT License 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 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 documentation files (the "Software"), to deal in the Software without restriction, including without limitation the

View File

@ -1,10 +1,24 @@
# toml++ (tomlplusplus) # 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) [![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> <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. `toml++` comes in two flavours: Regular and Single-header.
### Regular mode ### Regular mode
@ -12,27 +26,25 @@ Header-only [TOML v0.5.0] parsing lib for C++17 and later.
2. `#include <toml++/toml.h>` 2. `#include <toml++/toml.h>`
### Single-header mode ### 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 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 API is the same regardless of how you consume the library.
the `examples` directory.
<br> ### Configuration
# Configuration
A number of configurable options are exposed in the form of preprocessor macros. Most likely you 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 won't need to mess with these at all, but in the event you do, set your overrides prior to including
toml++. toml++.
| Option | Type | Default | Description | | Option | Type | Default | Description |
|----------------------------|:--------------:|-----------------------------------|-----------------------------------------------------------------------------------------------| |----------------------------|:--------------:|-----------------------------------|----------------------------------------------------------------------------------------------------------|
| `TOML_ASSERT(expr)` | function macro | `assert(expr)`<br>(or undefined) | Sets the assert function used by the library. | | `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_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_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_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_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_UNRELEASED_FEATURES` | boolean | `1` | Enables support for [unreleased TOML language features] not yet part of a [numbered version]. | | `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> <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 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_REVISION`.
### **🔸Unreleased features:** ### **🔸Unreleased TOML features:**
- [#356]: Allow leading zeros in the exponent part of a float - [#356]: Allow leading zeros in the exponent part of a float
- [#516]: Allow newlines and trailing commas in inline tables - [#516]: Allow newlines and trailing commas in inline tables
- [#562]: Allow hex floatingpoint values - [#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 _These can be disabled (and thus strict [TOML v0.5.0] compliance enforced) by specifying
`TOML_UNRELEASED_FEATURES = 0` (see [Configuration](#Configuration))._ `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 >>`. - All features as of `<< release date >>`.
<br> <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: 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 - The single-header file `toml.hpp` is generated by a script; make your changes in the files in
`include`, **not** in `toml.hpp`. `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 - 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). (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). - You should regenerate the single-header file as part of your PR (a CI check will fail if you don't).
### Regenerating toml.hpp ### 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` 3. Run `python/generate_single_header.py`
### Building and testing ### 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: The first time you want to begin testing you'll need to ensure submodules have been fetched:
```bash ```bash
git submodule update --init --recursive git submodule update --init --recursive extern/Catch2
``` ```
#### Windows #### Windows
@ -100,10 +112,8 @@ Install [meson] and [ninja] if necessary, then test with both gcc and clang:
```bash ```bash
CXX=g++ meson build-gcc CXX=g++ meson build-gcc
CXX=clang++ meson build-clang CXX=clang++ meson build-clang
cd build-gcc cd build-gcc && ninja && ninja test
ninja && ninja test cd ../build-clang && ninja && ninja test
cd ../build-clang
ninja && ninja test
``` ```
<br> <br>
@ -113,21 +123,23 @@ ninja && ninja test
`toml++` is licensed under the terms of the MIT license - See [LICENSE]. `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]', 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) [relevant part](https://github.com/marzer/tomlplusplus/blob/master/include/toml%2B%2B/toml_utf8.h)
of the toml++ source. of the toml++ source.
[API documentation]: https://marzer.github.io/tomlplusplus/namespacetoml.html
[unreleased TOML language features]: https://github.com/marzer/tomlplusplus#unreleased-features [unreleased TOML language features]: https://github.com/marzer/tomlplusplus#unreleased-features
[numbered version]: https://github.com/toml-lang/toml/releases [numbered version]: https://github.com/toml-lang/toml/releases
[char8_t]: https://en.cppreference.com/w/cpp/keyword/char8_t [char8_t]: https://en.cppreference.com/w/cpp/keyword/char8_t
[TOML master]: https://github.com/toml-lang/toml/blob/master/README.md [TOML master]: https://github.com/toml-lang/toml/blob/master/README.md
[TOML issues list]: https://github.com/toml-lang/toml/issues [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 [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 [LICENSE]: https://github.com/marzer/tomlplusplus/blob/master/LICENSE
[Flexible and Economical UTF-8 Decoder]: http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ [Flexible and Economical UTF-8 Decoder]: http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
[meson]: https://mesonbuild.com/Getting-meson.html [meson]: https://mesonbuild.com/Getting-meson.html
[ninja]: https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages [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 [Test Adapter for Catch2]: https://marketplace.visualstudio.com/items?itemName=JohnnyHendriks.ext01
[Visual Studio 2019]: https://visualstudio.microsoft.com/vs/ [Visual Studio 2019]: https://visualstudio.microsoft.com/vs/
[#356]: https://github.com/toml-lang/toml/issues/356 [#356]: https://github.com/toml-lang/toml/issues/356

View File

@ -10,8 +10,12 @@ TAB_SIZE = 4
HTML_EXTRA_FILES = tomlplusplus.js HTML_EXTRA_FILES = tomlplusplus.js
SHOW_INCLUDE_FILES = NO SHOW_INCLUDE_FILES = NO
##! M_THEME_COLOR = #22272e ##! M_THEME_COLOR = #22272e
##! M_LINKS_NAVBAR1 = pages ##! M_LINKS_NAVBAR1 = \
##! M_LINKS_NAVBAR2 = annotated ##! pages
##! M_LINKS_NAVBAR2 = \
##! namespaces \
##! annotated \
##! "<a target="_blank" href="https://github.com/marzer/tomlplusplus/">Github</a>"
##! M_SEARCH_DOWNLOAD_BINARY = NO ##! M_SEARCH_DOWNLOAD_BINARY = NO
##! M_CLASS_TREE_EXPAND_LEVELS = 3 ##! M_CLASS_TREE_EXPAND_LEVELS = 3
##! M_FILE_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> \ ##! <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> ##! <br><br>Documentation generated using <a href="https://mcss.mosra.cz/">m.css</a>
##! M_HTML_HEADER = <script src="tomlplusplus.js"></script> ##! 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. #error toml++ is a C++ library.
#endif #endif
#if defined(__clang__) or defined(__GNUC__) #if defined(__clang__) || defined(__GNUC__)
#define TOML_GCC_ATTR(attr) __attribute__((attr)) #define TOML_GCC_ATTR(attr) __attribute__((attr))
#else #else
#define TOML_GCC_ATTR(attr) #define TOML_GCC_ATTR(attr)
@ -426,51 +426,12 @@ namespace toml
TOML_PUSH_WARNINGS TOML_PUSH_WARNINGS
TOML_DISABLE_INIT_WARNINGS TOML_DISABLE_INIT_WARNINGS
#if !TOML_DOXYGEN && TOML_EXCEPTIONS #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
/// \brief An error thrown/returned when parsing fails. /// \brief An error thrown/returned when parsing fails.
/// ///
/// \remarks This class inherits from `std::runtime_error` when `TOML_EXCEPTIONS` is `1`. /// \remarks This class inherits from `std::runtime_error` when exceptions are enabled.
/// The public interface remains the same regardless of exception mode. /// The public interface is the same regardless of exception mode.
class parse_error final class parse_error final
{ {
private: 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 #endif
TOML_POP_WARNINGS TOML_POP_WARNINGS
@ -577,7 +577,11 @@ namespace toml::impl
|| std::is_same_v<T, uint32_t> || std::is_same_v<T, uint32_t>
|| std::is_same_v<T, uint16_t> || std::is_same_v<T, uint16_t>
|| std::is_same_v<T, uint8_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> template <typename T>
inline constexpr bool is_value_or_node = 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<uint16_t> { using type = int64_t; };
template <> struct value_promoter<uint8_t> { using type = int64_t; }; template <> struct value_promoter<uint8_t> { using type = int64_t; };
template <> struct value_promoter<float> { using type = double; }; 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; template <typename T> using promoted = typename impl::value_promoter<T>::type;
inline constexpr toml::string_view low_character_escape_table[] = inline constexpr toml::string_view low_character_escape_table[] =
@ -682,7 +689,6 @@ namespace toml::impl
namespace toml namespace toml
{ {
/// \brief Helper alias that wraps a type up as it's TOML node equivalent. /// \brief Helper alias that wraps a type up as it's TOML node equivalent.
template <typename T> template <typename T>
using node_of = typename impl::node_wrapper<T>::type; using node_of = typename impl::node_wrapper<T>::type;
@ -691,6 +697,33 @@ namespace toml
template <typename T> template <typename T>
using value_of = typename impl::node_unwrapper<T>::type; 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. /// \brief Pretty-prints the value of a node_type to a stream.
template <typename CHAR> template <typename CHAR>

View File

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

View File

@ -61,8 +61,7 @@ namespace toml::impl
return node.visit([](const auto& n) noexcept return node.visit([](const auto& n) noexcept
-> size_t -> size_t
{ {
using node_t = impl::remove_cvref_t<decltype(n)>; if constexpr (is_table<decltype(n)>)
if constexpr (std::is_same_v<node_t, table>)
{ {
if (n.empty()) if (n.empty())
return 2_sz; // "{}" return 2_sz; // "{}"
@ -71,7 +70,7 @@ namespace toml::impl
weight += k.length() + default_formatter_inline_columns(v) + 2_sz; // + ", " weight += k.length() + default_formatter_inline_columns(v) + 2_sz; // + ", "
return weight; return weight;
} }
else if constexpr (std::is_same_v<node_t, array>) else if constexpr (is_array<decltype(n)>)
{ {
if (n.empty()) if (n.empty())
return 2_sz; // "[]" return 2_sz; // "[]"
@ -80,11 +79,11 @@ namespace toml::impl
weight += default_formatter_inline_columns(elem) + 2_sz; // + ", " weight += default_formatter_inline_columns(elem) + 2_sz; // + ", "
return weight; 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; // + "" 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(); auto v = n.get();
if (!v) if (!v)
@ -97,7 +96,7 @@ namespace toml::impl
} }
return weight + static_cast<size_t>(std::log10(static_cast<double>(v))); 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(); auto v = n.get();
if (v == 0.0) if (v == 0.0)
@ -110,15 +109,15 @@ namespace toml::impl
} }
return weight + static_cast<size_t>(std::log10(v)); 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; 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; 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; return 30_sz;
} }

View File

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

View File

@ -27,17 +27,18 @@ namespace toml
template <typename T> template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE [[nodiscard]] TOML_ALWAYS_INLINE
node_of<T>* reinterpret_as() noexcept node_of<T>& ref_cast() & noexcept { return *reinterpret_cast<node_of<T>*>(this); }
{
return reinterpret_cast<node_of<T>*>(this);
}
template <typename T> template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE [[nodiscard]] TOML_ALWAYS_INLINE
const node_of<T>* reinterpret_as() const noexcept node_of<T>&& ref_cast() && noexcept { return std::move(*reinterpret_cast<node_of<T>*>(this)); }
{
return reinterpret_cast<const 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() noexcept = default;
node(const node&) = delete; node(const node&) = delete;
@ -47,11 +48,9 @@ namespace toml
virtual ~node() noexcept = default; virtual ~node() noexcept = default;
/// \brief Returns the node's type identifier. /// \brief Returns the node's type identifier.
[[nodiscard]] virtual node_type type() const noexcept = 0; [[nodiscard]] virtual node_type type() const noexcept = 0;
/// \brief Returns true if this node is a table. /// \brief Returns true if this node is a table.
[[nodiscard]] virtual bool is_table() const noexcept = 0; [[nodiscard]] virtual bool is_table() const noexcept = 0;
/// \brief Returns true if this node is an array. /// \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. /// \brief Returns true if this node is an array containing only tables.
[[nodiscard]] virtual bool is_array_of_tables() const noexcept { return false; } [[nodiscard]] virtual bool is_array_of_tables() const noexcept { return false; }
/// \brief Checks if a node is a specific type. /// \brief Checks if a node is a specific type.
/// ///
/// \tparam T The /// \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, time>) return is_time();
else if constexpr (std::is_same_v<type, date_time>) return is_date_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; } [[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; } [[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; } [[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; } [[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; } [[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; } [[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; } [[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; } [[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 value<date_time>* as_date_time() noexcept { return nullptr; }
[[nodiscard]] virtual const table* as_table() const 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<time>* as_time() const noexcept { return nullptr; }
[[nodiscard]] virtual const value<date_time>* as_date_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> template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE [[nodiscard]] TOML_ALWAYS_INLINE
node_of<T>* as() noexcept 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(); 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> template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE [[nodiscard]] TOML_ALWAYS_INLINE
const node_of<T>* as() const noexcept 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(); 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 [[nodiscard]] const source_region& source() const noexcept
{ {
return source_; return source_;
@ -172,94 +202,166 @@ namespace toml
private: private:
// this is done using a static helper to preserve const categories template <typename FUNC, typename N, typename T, bool = std::is_invocable_v<FUNC, ref_cast_type<N, T>>>
// (otherwise I'd have to implement this function twice) struct visit_return_type final
// (const propagation in C++: a modern horror story) {
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> 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( static_assert(
std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<table>())> std::is_invocable_v<FUNC&&, ref_cast_type<N&&, table>>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<array>())> || std::is_invocable_v<FUNC&&, ref_cast_type<N&&, array>>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<string>())> || std::is_invocable_v<FUNC&&, ref_cast_type<N&&, string>>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<int64_t>())> || std::is_invocable_v<FUNC&&, ref_cast_type<N&&, int64_t>>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<double>())> || std::is_invocable_v<FUNC&&, ref_cast_type<N&&, double>>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<bool>())> || std::is_invocable_v<FUNC&&, ref_cast_type<N&&, bool>>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date>())> || std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date>>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<time>())> || std::is_invocable_v<FUNC&&, ref_cast_type<N&&, time>>
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date_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" "Visitors must be invocable for at least one of the toml::node specializations"
); );
static constexpr auto is_exhaustive = switch (node.type())
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())
{ {
case node_type::table: case node_type::table:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<table>())>) if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, table>>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<table>()); return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<table>());
break; break;
case node_type::array: case node_type::array:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<array>())>) if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, array>>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<array>()); return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<array>());
break; break;
case node_type::string: case node_type::string:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<string>())>) if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, string>>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<string>()); return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<string>());
break; break;
case node_type::integer: case node_type::integer:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<int64_t>())>) if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, int64_t>>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<int64_t>()); return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<int64_t>());
break; break;
case node_type::floating_point: case node_type::floating_point:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<double>())>) if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, double>>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<double>()); return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<double>());
break; break;
case node_type::boolean: case node_type::boolean:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<bool>())>) if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, bool>>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<bool>()); return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<bool>());
break; break;
case node_type::date: case node_type::date:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date>())>) if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date>>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<date>()); return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<date>());
break; break;
case node_type::time: case node_type::time:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<time>())>) if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, time>>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<time>()); return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<time>());
break; break;
case node_type::date_time: case node_type::date_time:
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date_time>())>) if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date_time>>)
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<date_time>()); return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<date_time>());
break; break;
TOML_NO_DEFAULT_CASE; 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) if constexpr (is_exhaustive)
TOML_UNREACHABLE; 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: 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> 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> 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 namespace toml
{ {
#if TOML_EXCEPTIONS #if TOML_DOXYGEN || !TOML_EXCEPTIONS
using parse_result = table;
#else
/// \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 class parse_result final
{ {
private: private:
@ -69,7 +69,7 @@ namespace toml
[[nodiscard]] table& operator* () & noexcept { return get(); } [[nodiscard]] table& operator* () & noexcept { return get(); }
[[nodiscard]] table&& operator* () && noexcept { return std::move(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]] table* operator-> () noexcept { return &get(); }
[[nodiscard]] const table* operator-> () const noexcept { return &get(); } [[nodiscard]] const table* operator-> () const noexcept { return &get(); }
@ -133,6 +133,10 @@ namespace toml
} }
}; };
#else
using parse_result = table;
#endif #endif
} }
@ -1938,7 +1942,7 @@ namespace toml::impl
if constexpr (!TOML_LANG_HIGHER_THAN(0, 5, 0)) // toml/issues/665 if constexpr (!TOML_LANG_HIGHER_THAN(0, 5, 0)) // toml/issues/665
{ {
// arrays must be homogeneous in toml 0.5.0 and earlier // 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( TOML_ERROR(
"Arrays cannot contain values of different types in TOML 0.5.0 and earlier.", "Arrays cannot contain values of different types in TOML 0.5.0 and earlier.",
begin_pos, begin_pos,
@ -2406,7 +2410,7 @@ namespace toml::impl
const auto header_begin_pos = cp->position; const auto header_begin_pos = cp->position;
source_position header_end_pos; source_position header_end_pos;
key key; key key;
bool is_array = false; bool is_arr = false;
//parse header //parse header
{ {
@ -2426,7 +2430,7 @@ namespace toml::impl
{ {
advance(); advance();
eof_check(); eof_check();
is_array = true; is_arr = true;
} }
// skip past any whitespace that followed the '[' // skip past any whitespace that followed the '['
@ -2464,12 +2468,12 @@ namespace toml::impl
if (*cp != U']') if (*cp != U']')
abort_with_error( abort_with_error(
"Encountered unexpected character while parsing table"sv, "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(); advance();
// consume the second closing ']' // consume the second closing ']'
if (is_array) if (is_arr)
{ {
eof_check(); eof_check();
@ -2488,7 +2492,7 @@ namespace toml::impl
{ {
if (!consume_comment() && !consume_line_break()) if (!consume_comment() && !consume_line_break())
abort_with_error( 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, '\'' " header; expected a comment or whitespace, saw '"sv, *cp, '\''
); );
} }
@ -2507,31 +2511,31 @@ namespace toml::impl
key.segments[i], key.segments[i],
std::make_unique<table>() std::make_unique<table>()
).first->second.get(); ).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() }; 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()) 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; // table arrays are a special case;
// the spec dictates we select the most recently declared element in the array. // the spec dictates we select the most recently declared element in the array.
TOML_ASSERT(!child->reinterpret_as<array>()->values.empty()); TOML_ASSERT(!child->ref_cast<array>().values.empty());
TOML_ASSERT(child->reinterpret_as<array>()->values.back()->is_table()); TOML_ASSERT(child->ref_cast<array>().values.back()->is_table());
parent = child->reinterpret_as<array>()->values.back()->reinterpret_as<table>(); parent = &child->ref_cast<array>().values.back()->ref_cast<table>();
} }
else 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, '\''); abort_with_error("Attempt to redefine existing table '"sv, recording_buffer, '\'');
else else
abort_with_error( abort_with_error(
"Attempt to redefine existing "sv, child->type(), "Attempt to redefine existing "sv, child->type(),
" '"sv, recording_buffer, " '"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, // 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 // 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>()) auto tab_arr = &parent->values.emplace(key.segments.back(),std::make_unique<array>())
.first->second->reinterpret_as<array>(); .first->second->ref_cast<array>();
table_arrays.push_back(tab_arr); table_arrays.push_back(tab_arr);
tab_arr->source_ = { header_begin_pos, header_end_pos, reader.source_path() }; tab_arr->source_ = { header_begin_pos, header_end_pos, reader.source_path() };
tab_arr->values.push_back(std::make_unique<table>()); tab_arr->values.push_back(std::make_unique<table>());
tab_arr->values.back()->source_ = { header_begin_pos, header_end_pos, reader.source_path() }; 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 //otherwise we're just making a table
else else
{ {
auto tab = parent->values.emplace(key.segments.back(),std::make_unique<table>()) auto tab = &parent->values.emplace(key.segments.back(),std::make_unique<table>())
.first->second->reinterpret_as<table>(); .first->second->ref_cast<table>();
tab->source_ = { header_begin_pos, header_end_pos, reader.source_path() }; tab->source_ = { header_begin_pos, header_end_pos, reader.source_path() };
return tab; return tab;
} }
@ -2573,19 +2577,19 @@ namespace toml::impl
// otherwise this is a redefinition error. // otherwise this is a redefinition error.
else 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.push_back(std::make_unique<table>());
tab_arr->values.back()->source_ = { header_begin_pos, header_end_pos, reader.source_path() }; 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() && matching_node->is_table()
&& !implicit_tables.empty()) && !implicit_tables.empty())
{ {
auto tbl = matching_node->reinterpret_as<table>(); auto tbl = &matching_node->ref_cast<table>();
const auto idx = find(implicit_tables, tbl); const auto idx = find(implicit_tables, tbl);
if (idx) if (idx)
{ {
@ -2597,13 +2601,13 @@ namespace toml::impl
} }
//if we get here it's a redefinition error. //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, '\''); abort_with_error("Attempt to redefine existing table '"sv, recording_buffer, '\'');
else else
abort_with_error( abort_with_error(
"Attempt to redefine existing "sv, matching_node->type(), "Attempt to redefine existing "sv, matching_node->type(),
" '"sv, recording_buffer, " '"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({}); TOML_ERROR_CHECK({});
@ -2629,18 +2633,18 @@ namespace toml::impl
std::move(kvp.key.segments[i]), std::move(kvp.key.segments[i]),
std::make_unique<table>() std::make_unique<table>()
).first->second.get(); ).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; dotted_key_tables.back()->inline_ = true;
child->source_ = kvp.value->source_; 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); abort_with_error("Attempt to redefine "sv, child->type(), " as dotted key-value pair"sv);
} }
else else
child->source_.end = kvp.value->source_.end; child->source_.end = kvp.value->source_.end;
TOML_ERROR_CHECK(); 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) 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 if (tbl.inline_) //inline tables (and all their inline descendants) are already correctly terminated
return; return;
@ -2753,7 +2757,7 @@ namespace toml::impl
} }
else //arrays else //arrays
{ {
auto& arr = *nde.reinterpret_as<array>(); auto& arr = nde.ref_cast<array>();
auto end = nde.source_.end; auto end = nde.source_.end;
for (auto& v : arr.values) for (auto& v : arr.values)
{ {

View File

@ -160,7 +160,7 @@ namespace toml::impl
template <typename CHAR> template <typename CHAR>
TOML_ALWAYS_INLINE 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); static_assert(sizeof(CHAR) == 1);
print_to_stream(val ? "true"sv : "false"sv, stream); print_to_stream(val ? "true"sv : "false"sv, stream);

View File

@ -132,4 +132,7 @@ namespace toml
value(uint8_t) -> value<int64_t>; value(uint8_t) -> value<int64_t>;
value(uint16_t) -> value<int64_t>; value(uint16_t) -> value<int64_t>;
value(uint32_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 # post-process html files
fixes = [ fixes = [
CustomTagsFix() CustomTagsFix()
, NavBarFix() # , NavBarFix()
, IndexHrefFix() # , IndexHrefFix()
, ModifiersFix1() , ModifiersFix1()
, ModifiersFix2() , ModifiersFix2()
, InlineNamespaceFix1() , InlineNamespaceFix1()

374
toml.hpp
View File

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