expanded allowable conversion semantics of value and value_or

also:
- fixed infinity and NaN-related code breaking when using -ffast-math and friends
- added much more detail to many static_assert error messages
- added more test permutations of various compiler flags
- added many more static checks to test code
This commit is contained in:
Mark Gillard 2020-07-17 16:33:56 +03:00
parent de07ba7187
commit cb791fe0ef
48 changed files with 2475 additions and 968 deletions

3
.gitattributes vendored
View File

@ -17,6 +17,9 @@
*.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 *.css text encoding=UTF-8 eol=lf
.gitignore text encoding=UTF-8 eol=lf
.gitattributes text encoding=UTF-8 eol=lf
.editorconfig text encoding=UTF-8 eol=lf
meson.build text encoding=UTF-8 eol=lf meson.build text encoding=UTF-8 eol=lf
meson_options.txt text encoding=UTF-8 eol=lf meson_options.txt text encoding=UTF-8 eol=lf
Doxyfile text encoding=UTF-8 eol=lf Doxyfile text encoding=UTF-8 eol=lf

View File

@ -8,6 +8,14 @@ body
margin-top: 3rem; margin-top: 3rem;
} }
@media screen and (min-width: 768px) and (max-width: 991px)
{
body
{
margin-top: 6rem;
}
}
a a
{ {
text-decoration: none !important; text-decoration: none !important;
@ -22,6 +30,11 @@ header
z-index: 100; z-index: 100;
} }
header, header *
{
white-space: nowrap;
}
article, article > header, article div > section article, article > header, article div > section
{ {
margin-bottom: 5rem; margin-bottom: 5rem;
@ -65,13 +78,10 @@ pre a.tpp-injected
.tpp-enable-if > a .tpp-enable-if > a
{ {
white-space: nowrap; white-space: nowrap;
font-size: 0.8rem; font-size: 0.7rem;
font-weight: bold; font-weight: bold;
background-color: #858585; background-color: #858585;
color: #050505; color: #050505;
padding-bottom: 0px;
margin: 0px 1px;
margin-bottom: 2px;
} }
.tpp-enable-if > a:hover .tpp-enable-if > a:hover
@ -135,7 +145,7 @@ pre.m-code + pre.m-console span
} }
/* comments */ /* comments */
.m-code .c1 pre.m-code .c1
{ {
color: rgb(87,166,74); color: rgb(87,166,74);
} }
@ -149,25 +159,31 @@ pre.m-code .mh
} }
/* keywords */ /* keywords */
.m-code .k pre.m-code .k
{ {
color: rgb(86,156,214); color: rgb(86,156,214);
} }
pre.m-code .kt,
pre.m-code .k,
pre.m-code .nc
{
font-weight: normal;
}
/* identifier names */ /* identifier names */
.m-code .n pre.m-code .n
{ {
color: rgb(220,220,220); color: rgb(220,220,220);
} }
/* punctuators (brackets etc) */ /* punctuators (brackets etc) */
.m-code .p pre.m-code .p
{ {
color: rgb(120,120,120); color: rgb(120,120,120);
} }
/* preprocessor directives */ /* preprocessor directives */
.m-code .cp pre.m-code .cp
{ {
color: rgb(120,120,120); color: rgb(120,120,120);
} }
@ -209,7 +225,6 @@ pre.m-code .mh
margin-right: -0.9rem; margin-right: -0.9rem;
margin-top: -0.5rem; margin-top: -0.5rem;
} }
@media screen and (min-width: 992px) @media screen and (min-width: 992px)
{ {
.gh-badges .gh-badges
@ -255,6 +270,27 @@ main > article > .m-container.m-container-inflatable > .m-row > div.m-col-l-10.m
max-width: calc(100% + 2rem); max-width: calc(100% + 2rem);
} }
/* include <blah.h> */
.m-doc-details div h3
{
overflow: auto;
}
h1 .m-doc-include,
h3 .m-doc-include
{
float: right;
}
h1 .m-doc-include *,
h3 .m-doc-include *
{
opacity: 1.0 !important;
}
h1 .m-doc-include .cp,
h3 .m-doc-include .cp
{
color: #747474;
}
/* "Try this code on Compiler Explorer" */ /* "Try this code on Compiler Explorer" */
.godbolt .godbolt
{ {

View File

@ -5,47 +5,6 @@
#ifdef _WIN32 #ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#define NOATOM // - Atom Manager routines
#define NOBITMAP
#define NOCLIPBOARD // - Clipboard routines
#define NOCOLOR // - Screen colors
#define NOCOMM // - COMM driver routines
#define NOCTLMGR // - Control and Dialog routines
#define NODEFERWINDOWPOS // - DeferWindowPos routines
#define NODRAWTEXT // - DrawText() and DT_*
#define NOGDI // - All GDI defines and routines
#define NOGDICAPMASKS // - CC_*, LC_*, PC_*, CP_*, TC_*, RC_
#define NOHELP // - Help engine interface.
#define NOICONS // - IDI_*
#define NOKANJI // - Kanji support stuff.
#define NOKEYSTATES // - MK_*
#define NOKERNEL // - All KERNEL defines and routines
#define NOMB // - MB_* and MessageBox()
#define NOMCX // - Modem Configuration Extensions
#define NOMENUS // - MF_*
#define NOMEMMGR // - GMEM_*, LMEM_*, GHND, LHND, associated routines
#define NOMETAFILE // - typedef METAFILEPICT
#define NOMINMAX // - Macros min(a,b) and max(a,b)
#define NOMSG // - typedef MSG and associated routines
//#define NONLS // - All NLS defines and routines
#define NOOPENFILE // - OpenFile(), OemToAnsi, AnsiToOem, and OF_*
#define NOPROFILER // - Profiler interface.
#define NORASTEROPS // - Binary and Tertiary raster ops
#define NOSCROLL // - SB_* and scrolling routines
#define NOSERVICE // - All Service Controller routines, SERVICE_ equates, etc.
#define NOSHOWWINDOW // - SW_*
#define NOSOUND // - Sound driver routines
#define NOSYSCOMMANDS // - SC_*
#define NOSYSMETRICS // - SM_*
#define NOTEXTMETRIC // - typedef TEXTMETRIC and associated routines
#define NOUSER // - All USER defines and routines
#define NOVIRTUALKEYCODES // - VK_*
#define NOWH // - SetWindowsHook and WH_*
#define NOWINOFFSETS // - GWL_*, GCL_*, associated routines
#define NOWINMESSAGES // - WM_*, EM_*, LB_*, CB_*
#define NOWINSTYLES // - WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
#include <Windows.h> #include <Windows.h>
inline void init_utf8_console() noexcept inline void init_utf8_console() noexcept

View File

@ -103,6 +103,10 @@
#undef TOML_EVAL_BOOL_1 #undef TOML_EVAL_BOOL_1
#undef TOML_EVAL_BOOL_0 #undef TOML_EVAL_BOOL_0
#undef TOML_HAS_CUSTOM_OPTIONAL_TYPE #undef TOML_HAS_CUSTOM_OPTIONAL_TYPE
#undef TOML_UNWRAPPED_NODE_TYPE_LIST
#undef TOML_NATIVE_VALUE_TYPE_LIST
#undef TOML_NATIVE_STRING_TYPE_NAME
#undef TOML_NODE_TYPE_LIST
#endif #endif
//# {{ //# {{

View File

@ -174,7 +174,7 @@ namespace toml::impl
TOML_ALWAYS_INLINE TOML_ALWAYS_INLINE
auto* make_node(T&& val) noexcept auto* make_node(T&& val) noexcept
{ {
using type = unwrapped<remove_cvref_t<T>>; using type = unwrap_node<remove_cvref_t<T>>;
if constexpr (is_one_of<type, array, table>) if constexpr (is_one_of<type, array, table>)
{ {
static_assert( static_assert(
@ -190,14 +190,18 @@ namespace toml::impl
"Instantiating values from wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled." "Instantiating values from wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled."
); );
static_assert( static_assert(
is_value_or_promotable<type>, is_native<type> || is_losslessly_convertible_to_native<type>,
"Value initializers must be (or be promotable to) one of the TOML value types" "Value initializers must be (or be promotable to) one of the TOML value types"
); );
#if TOML_WINDOWS_COMPAT
if constexpr (is_wide_string<T>) if constexpr (is_wide_string<T>)
{
#if TOML_WINDOWS_COMPAT
return new value{ narrow(std::forward<T>(val)) }; return new value{ narrow(std::forward<T>(val)) };
else #else
static_assert(dependent_false<T>, "Evaluated unreachable branch!");
#endif #endif
}
else
return new value{ std::forward<T>(val) }; return new value{ std::forward<T>(val) };
} }
} }
@ -613,13 +617,14 @@ namespace toml
template <typename U, typename... V> template <typename U, typename... V>
iterator emplace(const_iterator pos, V&&... args) noexcept iterator emplace(const_iterator pos, V&&... args) noexcept
{ {
using type = impl::unwrapped<U>; using type = impl::unwrap_node<U>;
static_assert( static_assert(
impl::is_value_or_node<type>, impl::is_native<type> || impl::is_one_of<type, table, array>,
"Emplacement type parameter must be one of the basic value types, a toml::table, or a toml::array" "Emplacement type parameter must be one of the following:"
TOML_UNWRAPPED_NODE_TYPE_LIST
); );
return { values.emplace(pos.raw_, new impl::node_of<type>{ std::forward<V>(args)...} ) }; return { values.emplace(pos.raw_, new impl::wrap_node<type>{ std::forward<V>(args)...} ) };
} }
/// \brief Removes the specified node from the array. /// \brief Removes the specified node from the array.
@ -774,13 +779,14 @@ namespace toml
template <typename U, typename... V> template <typename U, typename... V>
decltype(auto) emplace_back(V&&... args) noexcept decltype(auto) emplace_back(V&&... args) noexcept
{ {
using type = impl::unwrapped<U>; using type = impl::unwrap_node<U>;
static_assert( static_assert(
impl::is_value_or_node<type>, impl::is_native<type> || impl::is_one_of<type, table, array>,
"Emplacement type parameter must be one of the basic value types, a toml::table, or a toml::array" "Emplacement type parameter must be one of the following:"
TOML_UNWRAPPED_NODE_TYPE_LIST
); );
auto nde = new impl::node_of<type>{ std::forward<V>(args)... }; auto nde = new impl::wrap_node<type>{ std::forward<V>(args)... };
values.emplace_back(nde); values.emplace_back(nde);
return *nde; return *nde;
} }
@ -837,7 +843,7 @@ namespace toml
/// ///
/// \returns A pointer to the selected node if it existed and was of the specified type, or nullptr. /// \returns A pointer to the selected node if it existed and was of the specified type, or nullptr.
template <typename T> template <typename T>
[[nodiscard]] impl::node_of<T>* get_as(size_t index) noexcept [[nodiscard]] impl::wrap_node<T>* get_as(size_t index) noexcept
{ {
if (auto val = get(index)) if (auto val = get(index))
return val->as<T>(); return val->as<T>();
@ -851,7 +857,7 @@ namespace toml
/// ///
/// \returns A pointer to the selected node if it existed and was of the specified type, or nullptr. /// \returns A pointer to the selected node if it existed and was of the specified type, or nullptr.
template <typename T> template <typename T>
[[nodiscard]] const impl::node_of<T>* get_as(size_t index) const noexcept [[nodiscard]] const impl::wrap_node<T>* get_as(size_t index) const noexcept
{ {
if (auto val = get(index)) if (auto val = get(index))
return val->as<T>(); return val->as<T>();
@ -879,9 +885,9 @@ namespace toml
template <typename T> template <typename T>
[[nodiscard]] static bool container_equality(const array& lhs, const T& rhs) noexcept [[nodiscard]] static bool container_equality(const array& lhs, const T& rhs) noexcept
{ {
using elem_t = std::remove_const_t<typename T::value_type>; using element_type = std::remove_const_t<typename T::value_type>;
static_assert( static_assert(
impl::is_value_or_promotable<elem_t>, impl::is_native<element_type> || impl::is_losslessly_convertible_to_native<element_type>,
"Container element type must be (or be promotable to) one of the TOML value types" "Container element type must be (or be promotable to) one of the TOML value types"
); );
@ -893,7 +899,7 @@ namespace toml
size_t i{}; size_t i{};
for (auto& list_elem : rhs) for (auto& list_elem : rhs)
{ {
const auto elem = lhs.get_as<impl::promoted<elem_t>>(i++); const auto elem = lhs.get_as<impl::native_type_of<element_type>>(i++);
if (!elem || *elem != list_elem) if (!elem || *elem != list_elem)
return false; return false;
} }

View File

@ -16,6 +16,9 @@ TOML_DISABLE_ALL_WARNINGS
#endif #endif
#include <cstdint> #include <cstdint>
#include <cstring> //memcpy, memset #include <cstring> //memcpy, memset
#include <cfloat>
#include <climits>
#include <limits>
#include <memory> #include <memory>
#include <string_view> #include <string_view>
#include <string> #include <string>
@ -38,6 +41,17 @@ TOML_POP_WARNINGS
#define TOML_LAUNDER(x) x #define TOML_LAUNDER(x) x
#endif #endif
////////// ENVIRONMENT GROUND-TRUTHS
static_assert(CHAR_BIT == 8);
static_assert(FLT_RADIX == 2);
static_assert('A' == 65);
static_assert(sizeof(double) == 8);
static_assert(std::numeric_limits<double>::is_iec559);
static_assert(std::numeric_limits<double>::digits == 53);
static_assert(std::numeric_limits<double>::digits10 == 15);
static_assert(std::numeric_limits<double>::max_digits10 == 17);
////////// FORWARD DECLARATIONS & TYPEDEFS ////////// FORWARD DECLARATIONS & TYPEDEFS
TOML_PUSH_WARNINGS TOML_PUSH_WARNINGS
@ -338,6 +352,27 @@ namespace toml::impl
return static_cast<std::underlying_type_t<T>>(val); return static_cast<std::underlying_type_t<T>>(val);
} }
// Q: "why not use the built-in fpclassify?"
// A: Because it gets broken by -ffast-math and friends
enum class fp_class : unsigned { ok, neg_inf, pos_inf, nan };
[[nodiscard]]
inline fp_class fpclassify(const double& val) noexcept
{
constexpr uint64_t sign = 0b1000000000000000000000000000000000000000000000000000000000000000ull;
constexpr uint64_t exponent = 0b0111111111110000000000000000000000000000000000000000000000000000ull;
constexpr uint64_t mantissa = 0b0000000000001111111111111111111111111111111111111111111111111111ull;
uint64_t val_bits;
memcpy(&val_bits, &val, sizeof(val));
if ((val_bits & exponent) != exponent)
return fp_class::ok;
if ((val_bits & mantissa))
return fp_class::nan;
return (val_bits & sign) ? fp_class::neg_inf : fp_class::pos_inf;
}
// Q: "why not use std::find??" // Q: "why not use std::find??"
// A: Because <algorithm> is _huge_ and std::find would be the only thing I used from it. // A: Because <algorithm> is _huge_ and std::find would be the only thing I used from it.
// I don't want to impose such a heavy compile-time burden on users. // I don't want to impose such a heavy compile-time burden on users.
@ -368,15 +403,216 @@ namespace toml::impl
TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS
// general value traits
// (as they relate to their equivalent native TOML type)
template <typename T> template <typename T>
inline constexpr bool is_value = struct value_traits
std::is_same_v<T, string> {
|| std::is_same_v<T, int64_t> using native_type = void;
|| std::is_same_v<T, double> static constexpr bool is_native = false;
|| std::is_same_v<T, bool> static constexpr bool is_losslessly_convertible_to_native = false;
|| std::is_same_v<T, date> static constexpr bool can_represent_native = false;
|| std::is_same_v<T, time> static constexpr bool can_partially_represent_native = false;
|| std::is_same_v<T, date_time>; static constexpr auto node_type = ::toml::node_type::none;
};
template <typename T> struct value_traits<T&> : value_traits<T> {};
template <typename T> struct value_traits<T&&> : value_traits<T> {};
template <typename T> struct value_traits<T*const> : value_traits<T*>{};
template <typename T> struct value_traits<T*volatile> : value_traits<T*>{};
template <typename T> struct value_traits<T*const volatile> : value_traits<T*>{};
// integer value traits
template <typename T>
struct unsigned_integer_value_traits
{
using native_type = int64_t;
static constexpr bool is_native = false;
static constexpr bool is_signed = false;
static constexpr auto min = (std::numeric_limits<T>::min)();
static constexpr auto max = (std::numeric_limits<T>::max)();
static constexpr bool is_losslessly_convertible_to_native
= (std::numeric_limits<T>::max)() <= 9223372036854775807ULL;
static constexpr bool can_represent_native = false;
static constexpr bool can_partially_represent_native = true;
static constexpr auto node_type = ::toml::node_type::integer;
};
template <typename T>
struct signed_integer_value_traits
{
using native_type = int64_t;
static constexpr bool is_native = std::is_same_v<T, native_type>;
static constexpr bool is_signed = true;
static constexpr auto min = (std::numeric_limits<T>::min)();
static constexpr auto max = (std::numeric_limits<T>::max)();
static constexpr bool is_losslessly_convertible_to_native
= (std::numeric_limits<T>::min)() >= (-9223372036854775807LL - 1LL)
&& (std::numeric_limits<T>::max)() <= 9223372036854775807LL;
static constexpr bool can_represent_native
= (std::numeric_limits<T>::min)() <= (-9223372036854775807LL - 1LL)
&& (std::numeric_limits<T>::max)() >= 9223372036854775807LL;
static constexpr bool can_partially_represent_native = true;
static constexpr auto node_type = ::toml::node_type::integer;
};
template <typename T, bool U = std::is_unsigned_v<T>>
struct integer_value_traits : signed_integer_value_traits<T> {};
template <typename T>
struct integer_value_traits<T, true> : unsigned_integer_value_traits<T> {};
template <> struct value_traits<char> : integer_value_traits<char> {};
template <> struct value_traits<signed char> : integer_value_traits<signed char> {};
template <> struct value_traits<unsigned char> : integer_value_traits<unsigned char> {};
template <> struct value_traits<signed short> : integer_value_traits<signed short> {};
template <> struct value_traits<unsigned short> : integer_value_traits<unsigned short> {};
template <> struct value_traits<signed int> : integer_value_traits<signed int> {};
template <> struct value_traits<unsigned int> : integer_value_traits<unsigned int> {};
template <> struct value_traits<signed long> : integer_value_traits<signed long> {};
template <> struct value_traits<unsigned long> : integer_value_traits<unsigned long> {};
template <> struct value_traits<signed long long> : integer_value_traits<signed long long> {};
template <> struct value_traits<unsigned long long> : integer_value_traits<unsigned long long> {};
#ifdef __SIZEOF_INT128__
template <typename T>
struct big_integer_value_traits
{
using native_type = int64_t;
static constexpr bool is_native = false;
static constexpr bool is_losslessly_convertible_to_native = false;
static constexpr bool is_signed = static_cast<T>(-1) < T{}; // for impls not properly specializing <type_traits>
static constexpr bool can_represent_native = is_signed;
static constexpr bool can_partially_represent_native = true;
static constexpr auto node_type = ::toml::node_type::integer;
};
template <>
struct value_traits<__int128_t> : big_integer_value_traits<__int128_t>
{
static constexpr auto max = static_cast<__int128_t>(( __uint128_t{ 1u } << ((__SIZEOF_INT128__ * CHAR_BIT) - 1)) - 1);
static constexpr auto min = -max - __int128_t{ 1 };
};
template <>
struct value_traits<__uint128_t> : big_integer_value_traits<__uint128_t>
{
static constexpr auto min = __uint128_t{};
static constexpr auto max = (2u * static_cast<__uint128_t>(value_traits<__int128_t>::max)) + 1u;
};
#endif
#ifdef TOML_SMALL_INT_TYPE
template <> struct value_traits<TOML_SMALL_INT_TYPE> : signed_integer_value_traits<TOML_SMALL_INT_TYPE> {};
#endif
static_assert(value_traits<int64_t>::is_native);
static_assert(value_traits<int64_t>::is_losslessly_convertible_to_native);
static_assert(value_traits<int64_t>::can_represent_native);
static_assert(value_traits<int64_t>::can_partially_represent_native);
// float value traits
template <typename T>
struct float_value_traits
{
using native_type = double;
static constexpr bool is_native = std::is_same_v<T, native_type>;
static constexpr bool is_signed = true;
static constexpr bool is_losslessly_convertible_to_native
= std::numeric_limits<T>::is_iec559
&& std::numeric_limits<T>::digits <= 53
&& std::numeric_limits<T>::digits10 <= 15
&& std::numeric_limits<T>::max_digits10 <= 17;
static constexpr bool can_represent_native
= std::numeric_limits<T>::is_iec559
&& std::numeric_limits<T>::digits >= 53
&& std::numeric_limits<T>::digits10 >= 15
&& std::numeric_limits<T>::max_digits10 >= 17;
static constexpr bool can_partially_represent_native //32-bit float values
= std::numeric_limits<T>::is_iec559
&& std::numeric_limits<T>::digits >= 24
&& std::numeric_limits<T>::digits10 >= 6
&& std::numeric_limits<T>::max_digits10 >= 9;
static constexpr auto node_type = ::toml::node_type::floating_point;
};
template <> struct value_traits<float> : float_value_traits<float> {};
template <> struct value_traits<double> : float_value_traits<double> {};
template <> struct value_traits<long double> : float_value_traits<long double> {};
#if defined(FLT16_MANT_DIG) && defined(FLT16_DIG) && defined(FLT16_DECIMAL_DIG)
template <> struct value_traits<_Float16> : float_value_traits<_Float16> {};
#endif
//#ifdef __SIZEOF_FLOAT80__
//template <> struct value_traits<__float80> : float_value_traits<__float80> {};
//#endif
#ifdef __SIZEOF_FLOAT128__
template <> struct value_traits<__float128> : float_value_traits<__float128> {};
#endif
#ifdef TOML_SMALL_FLOAT_TYPE
template <> struct value_traits<TOML_SMALL_FLOAT_TYPE> : float_value_traits<TOML_SMALL_FLOAT_TYPE> {};
#endif
static_assert(value_traits<double>::is_native);
static_assert(value_traits<double>::is_losslessly_convertible_to_native);
static_assert(value_traits<double>::can_represent_native);
static_assert(value_traits<double>::can_partially_represent_native);
// string value traits
template <typename T>
struct string_value_traits
{
using native_type = ::toml::string;
static constexpr bool is_native = std::is_same_v<T, native_type>;
static constexpr bool is_losslessly_convertible_to_native = true;
static constexpr bool can_represent_native
= !std::is_array_v<T>
&& (!std::is_pointer_v<T> || std::is_const_v<std::remove_pointer_t<T>>);
static constexpr bool can_partially_represent_native = can_represent_native;
static constexpr auto node_type = ::toml::node_type::string;
};
template <> struct value_traits<std::string> : string_value_traits<std::string> {};
template <> struct value_traits<std::string_view> : string_value_traits<std::string_view> {};
template <> struct value_traits<const char*> : string_value_traits<const char *> {};
template <size_t N> struct value_traits<const char[N]> : string_value_traits<const char[N]> {};
template <> struct value_traits<char*> : string_value_traits<char*> {};
template <size_t N> struct value_traits<char[N]> : string_value_traits<char[N]> {};
#ifdef __cpp_lib_char8_t
template <> struct value_traits<std::u8string> : string_value_traits<std::u8string> {};
template <> struct value_traits<std::u8string_view> : string_value_traits<std::u8string_view> {};
template <> struct value_traits<const char8_t*> : string_value_traits<const char8_t*> {};
template <size_t N> struct value_traits<const char8_t[N]> : string_value_traits<const char8_t[N]> {};
template <> struct value_traits<char8_t*> : string_value_traits<char8_t*> {};
template <size_t N> struct value_traits<char8_t[N]> : string_value_traits<char8_t[N]> {};
#endif
#if TOML_WINDOWS_COMPAT
template <typename T>
struct wstring_value_traits
{
using native_type = ::toml::string;
static constexpr bool is_native = false;
static constexpr bool is_losslessly_convertible_to_native = true; //narrow
static constexpr bool can_represent_native = std::is_same_v<T, std::wstring>; //widen
static constexpr bool can_partially_represent_native = can_represent_native;
static constexpr auto node_type = ::toml::node_type::string;
};
template <> struct value_traits<std::wstring> : wstring_value_traits<std::wstring> {};
template <> struct value_traits<std::wstring_view> : wstring_value_traits<std::wstring_view> {};
template <> struct value_traits<const wchar_t*> : wstring_value_traits<const wchar_t*> {};
template <size_t N> struct value_traits<const wchar_t[N]> : wstring_value_traits<const wchar_t[N]> {};
template <> struct value_traits<wchar_t*> : wstring_value_traits<wchar_t*> {};
template <size_t N> struct value_traits<wchar_t[N]> : wstring_value_traits<wchar_t[N]> {};
#endif
// other native value traits
template <typename T, ::toml::node_type NodeType>
struct native_value_traits
{
using native_type = T;
static constexpr bool is_native = true;
static constexpr bool is_losslessly_convertible_to_native = true;
static constexpr bool can_represent_native = true;
static constexpr bool can_partially_represent_native = true;
static constexpr auto node_type = NodeType;
};
template <> struct value_traits<bool> : native_value_traits<bool, node_type::boolean> {};
template <> struct value_traits<date> : native_value_traits<date, node_type::date> {};
template <> struct value_traits<time> : native_value_traits<time, node_type::time> {};
template <> struct value_traits<date_time> : native_value_traits<date_time, node_type::date_time> {};
template <typename T> template <typename T>
inline constexpr bool is_wide_string = is_one_of< inline constexpr bool is_wide_string = is_one_of<
@ -387,34 +623,20 @@ namespace toml::impl
std::wstring std::wstring
>; >;
template <typename T> // native value category queries
inline constexpr bool is_value_or_promotable =
is_value<T>
|| std::is_same_v<std::decay_t<T>, string_char*>
|| std::is_same_v<T, string_view>
|| std::is_same_v<T, int32_t>
|| std::is_same_v<T, int16_t>
|| std::is_same_v<T, int8_t>
|| 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>
#if TOML_WINDOWS_COMPAT
|| is_wide_string<T>
#endif
#ifdef TOML_SMALL_FLOAT_TYPE
|| std::is_same_v<T, TOML_SMALL_FLOAT_TYPE>
#endif
#ifdef TOML_SMALL_INT_TYPE
|| std::is_same_v<T, TOML_SMALL_INT_TYPE>
#endif
;
template <typename T> template <typename T>
inline constexpr bool is_value_or_node = using native_type_of = typename value_traits<T>::native_type;
is_value<T> template <typename T>
|| std::is_same_v<T, array> inline constexpr bool is_native = value_traits<T>::is_native;
|| std::is_same_v<T, table>; template <typename T>
inline constexpr bool can_represent_native = value_traits<T>::can_represent_native;
template <typename T>
inline constexpr bool can_partially_represent_native = value_traits<T>::can_partially_represent_native;
template <typename T>
inline constexpr bool is_losslessly_convertible_to_native = value_traits<T>::is_losslessly_convertible_to_native;
template <typename T, typename... U>
inline constexpr bool is_natively_one_of = is_one_of<native_type_of<T>, U...>;
template <typename T> struct node_wrapper { using type = T; }; template <typename T> struct node_wrapper { using type = T; };
template <> struct node_wrapper<string> { using type = value<string>; }; template <> struct node_wrapper<string> { using type = value<string>; };
@ -424,65 +646,16 @@ namespace toml::impl
template <> struct node_wrapper<date> { using type = value<date>; }; template <> struct node_wrapper<date> { using type = value<date>; };
template <> struct node_wrapper<time> { using type = value<time>; }; template <> struct node_wrapper<time> { using type = value<time>; };
template <> struct node_wrapper<date_time> { using type = value<date_time>; }; template <> struct node_wrapper<date_time> { using type = value<date_time>; };
template <typename T> using wrap_node = typename node_wrapper<T>::type;
template <typename T>
using node_of = typename impl::node_wrapper<T>::type;
template <typename T> struct node_unwrapper { using type = T; }; template <typename T> struct node_unwrapper { using type = T; };
template <typename T> struct node_unwrapper<value<T>> { using type = T; }; template <typename T> struct node_unwrapper<value<T>> { using type = T; };
template <typename T> using unwrap_node = typename node_unwrapper<T>::type;
template <typename T> using unwrapped = typename impl::node_unwrapper<T>::type; template <typename T> struct node_type_getter { static constexpr auto value = value_traits<T>::node_type; };
template <> struct node_type_getter<table> { static constexpr auto value = node_type::table; };
template <typename T> struct value_promoter { using type = T; }; template <> struct node_type_getter<array> { static constexpr auto value = node_type::array; };
template <size_t N> struct value_promoter<const string_char[N]> { using type = string; }; template <typename T> inline constexpr node_type node_type_of = node_type_getter<unwrap_node<remove_cvref_t<T>>>::value;
template <size_t N> struct value_promoter<const string_char(&)[N]> { using type = string; };
template <size_t N> struct value_promoter<const string_char(&&)[N]> { using type = string; };
template <> struct value_promoter<const string_char*> { using type = string; };
template <size_t N> struct value_promoter<string_char[N]> { using type = string; };
template <size_t N> struct value_promoter<string_char(&)[N]> { using type = string; };
template <size_t N> struct value_promoter<string_char(&&)[N]> { using type = string; };
template <> struct value_promoter<string_char*> { using type = string; };
template <> struct value_promoter<string_view> { using type = string; };
#if TOML_WINDOWS_COMPAT
template <size_t N> struct value_promoter<const wchar_t[N]> { using type = string; };
template <size_t N> struct value_promoter<const wchar_t(&)[N]> { using type = string; };
template <size_t N> struct value_promoter<const wchar_t(&&)[N]> { using type = string; };
template <> struct value_promoter<const wchar_t*> { using type = string; };
template <size_t N> struct value_promoter<wchar_t[N]> { using type = string; };
template <size_t N> struct value_promoter<wchar_t(&)[N]> { using type = string; };
template <size_t N> struct value_promoter<wchar_t(&&)[N]> { using type = string; };
template <> struct value_promoter<wchar_t*> { using type = string; };
template <> struct value_promoter<std::wstring_view> { using type = string; };
template <> struct value_promoter<std::wstring> { using type = string; };
#endif
template <> struct value_promoter<int32_t> { using type = int64_t; };
template <> struct value_promoter<int16_t> { using type = int64_t; };
template <> struct value_promoter<int8_t> { using type = int64_t; };
template <> struct value_promoter<uint32_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<float> { using type = double; };
#ifdef TOML_SMALL_FLOAT_TYPE
template <> struct value_promoter<TOML_SMALL_FLOAT_TYPE> { using type = double; };
#endif
#ifdef TOML_SMALL_INT_TYPE
template <> struct value_promoter<TOML_SMALL_INT_TYPE> { using type = int64_t; };
#endif
template <typename T> using promoted = typename impl::value_promoter<T>::type;
template <typename T> struct node_type_of_;
template <> struct node_type_of_<table> { static constexpr auto value = node_type::table; };
template <> struct node_type_of_<array> { static constexpr auto value = node_type::array; };
template <> struct node_type_of_<string> { static constexpr auto value = node_type::string; };
template <> struct node_type_of_<int64_t> { static constexpr auto value = node_type::integer; };
template <> struct node_type_of_<double> { static constexpr auto value = node_type::floating_point; };
template <> struct node_type_of_<bool> { static constexpr auto value = node_type::boolean; };
template <> struct node_type_of_<date> { static constexpr auto value = node_type::date; };
template <> struct node_type_of_<time> { static constexpr auto value = node_type::time; };
template <> struct node_type_of_<date_time> { static constexpr auto value = node_type::date_time; };
template <typename T>
inline constexpr auto node_type_of = node_type_of_<promoted<typename node_unwrapper<remove_cvref_t<T>>::type>>::value;
inline constexpr std::string_view low_character_escape_table[] = inline constexpr std::string_view low_character_escape_table[] =
{ {
@ -534,26 +707,28 @@ namespace toml::impl
"date-time"sv "date-time"sv
}; };
#define TOML_P2S_DECL(Linkage, Type) \ #define TOML_P2S_DECL(Type) \
template <typename Char> \ template <typename Char> \
Linkage void print_to_stream(Type, std::basic_ostream<Char>&) inline void print_to_stream(Type, std::basic_ostream<Char>&)
TOML_P2S_DECL(TOML_ALWAYS_INLINE, int8_t); TOML_P2S_DECL(int8_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, int16_t); TOML_P2S_DECL(int16_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, int32_t); TOML_P2S_DECL(int32_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, int64_t); TOML_P2S_DECL(int64_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, uint8_t); TOML_P2S_DECL(uint8_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, uint16_t); TOML_P2S_DECL(uint16_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, uint32_t); TOML_P2S_DECL(uint32_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, uint64_t); TOML_P2S_DECL(uint64_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, float); TOML_P2S_DECL(float);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, double); TOML_P2S_DECL(const date&);
TOML_P2S_DECL(inline, const date&); TOML_P2S_DECL(const time&);
TOML_P2S_DECL(inline, const time&); TOML_P2S_DECL(time_offset);
TOML_P2S_DECL(inline, time_offset); TOML_P2S_DECL(const date_time&);
TOML_P2S_DECL(inline, const date_time&);
#undef TOML_P2S_DECL #undef TOML_P2S_DECL
template <typename T>
inline constexpr bool dependent_false = false;
} }
namespace toml namespace toml
@ -566,28 +741,28 @@ namespace toml
inline constexpr bool is_array = std::is_same_v<impl::remove_cvref_t<T>, array>; 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::string or toml::value<toml::string>. /// \brief Metafunction for determining if a type is a toml::string or toml::value<toml::string>.
template <typename T> template <typename T>
inline constexpr bool is_string = std::is_same_v<impl::node_of<impl::remove_cvref_t<T>>, value<string>>; inline constexpr bool is_string = std::is_same_v<impl::wrap_node<impl::remove_cvref_t<T>>, value<string>>;
/// \brief Metafunction for determining if a type is an int64_t or toml::value<int64_t>. /// \brief Metafunction for determining if a type is an int64_t or toml::value<int64_t>.
template <typename T> template <typename T>
inline constexpr bool is_integer = std::is_same_v<impl::node_of<impl::remove_cvref_t<T>>, value<int64_t>>; inline constexpr bool is_integer = std::is_same_v<impl::wrap_node<impl::remove_cvref_t<T>>, value<int64_t>>;
/// \brief Metafunction for determining if a type is a double or toml::value<double>. /// \brief Metafunction for determining if a type is a double or toml::value<double>.
template <typename T> template <typename T>
inline constexpr bool is_floating_point = std::is_same_v<impl::node_of<impl::remove_cvref_t<T>>, value<double>>; inline constexpr bool is_floating_point = std::is_same_v<impl::wrap_node<impl::remove_cvref_t<T>>, value<double>>;
/// \brief Metafunction for determining if a type satisfies `toml::is_integer || toml::is_floating_point`. /// \brief Metafunction for determining if a type satisfies `toml::is_integer || toml::is_floating_point`.
template <typename T> template <typename T>
inline constexpr bool is_number = is_integer<T> || is_floating_point<T>; inline constexpr bool is_number = is_integer<T> || is_floating_point<T>;
/// \brief Metafunction for determining if a type is a bool toml::value<bool>. /// \brief Metafunction for determining if a type is a bool toml::value<bool>.
template <typename T> template <typename T>
inline constexpr bool is_boolean = std::is_same_v<impl::node_of<impl::remove_cvref_t<T>>, value<bool>>; inline constexpr bool is_boolean = std::is_same_v<impl::wrap_node<impl::remove_cvref_t<T>>, value<bool>>;
/// \brief Metafunction for determining if a type is a toml::date or toml::value<date>. /// \brief Metafunction for determining if a type is a toml::date or toml::value<date>.
template <typename T> template <typename T>
inline constexpr bool is_date = std::is_same_v<impl::node_of<impl::remove_cvref_t<T>>, value<date>>; inline constexpr bool is_date = std::is_same_v<impl::wrap_node<impl::remove_cvref_t<T>>, value<date>>;
/// \brief Metafunction for determining if a type is a toml::time or toml::value<time>. /// \brief Metafunction for determining if a type is a toml::time or toml::value<time>.
template <typename T> template <typename T>
inline constexpr bool is_time = std::is_same_v<impl::node_of<impl::remove_cvref_t<T>>, value<time>>; inline constexpr bool is_time = std::is_same_v<impl::wrap_node<impl::remove_cvref_t<T>>, value<time>>;
/// \brief Metafunction for determining if a type is a toml::date_time or toml::value<date_time>. /// \brief Metafunction for determining if a type is a toml::date_time or toml::value<date_time>.
template <typename T> template <typename T>
inline constexpr bool is_date_time = std::is_same_v<impl::node_of<impl::remove_cvref_t<T>>, value<date_time>>; inline constexpr bool is_date_time = std::is_same_v<impl::wrap_node<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.
/// ///

View File

@ -12,6 +12,10 @@
//# }} //# }}
#include "toml_default_formatter.h" #include "toml_default_formatter.h"
TOML_PUSH_WARNINGS
TOML_DISABLE_ALL_WARNINGS
#include <cmath>
TOML_POP_WARNINGS
TOML_PUSH_WARNINGS TOML_PUSH_WARNINGS
TOML_DISABLE_SWITCH_WARNINGS TOML_DISABLE_SWITCH_WARNINGS

View File

@ -29,16 +29,18 @@ namespace toml
}; };
[[nodiscard]] [[nodiscard]]
TOML_ATTR(const)
TOML_ALWAYS_INLINE TOML_ALWAYS_INLINE
TOML_ATTR(const)
TOML_ATTR(flatten)
constexpr format_flags operator & (format_flags lhs, format_flags rhs) noexcept constexpr format_flags operator & (format_flags lhs, format_flags rhs) noexcept
{ {
return static_cast<format_flags>(impl::unbox_enum(lhs) & impl::unbox_enum(rhs)); return static_cast<format_flags>(impl::unbox_enum(lhs) & impl::unbox_enum(rhs));
} }
[[nodiscard]] [[nodiscard]]
TOML_ATTR(const)
TOML_ALWAYS_INLINE TOML_ALWAYS_INLINE
TOML_ATTR(const)
TOML_ATTR(flatten)
constexpr format_flags operator | (format_flags lhs, format_flags rhs) noexcept constexpr format_flags operator | (format_flags lhs, format_flags rhs) noexcept
{ {
return static_cast<format_flags>( impl::unbox_enum(lhs) | impl::unbox_enum(rhs) ); return static_cast<format_flags>( impl::unbox_enum(lhs) | impl::unbox_enum(rhs) );

View File

@ -75,7 +75,6 @@ namespace toml
// print_to_stream() machinery // print_to_stream() machinery
namespace impl namespace impl
{ {
template TOML_API void print_floating_point_to_stream(float, std::ostream&, bool);
template TOML_API void print_floating_point_to_stream(double, std::ostream&, bool); template TOML_API void print_floating_point_to_stream(double, std::ostream&, bool);
} }
} }

View File

@ -9,6 +9,41 @@
TOML_PUSH_WARNINGS TOML_PUSH_WARNINGS
TOML_DISABLE_VTABLE_WARNINGS TOML_DISABLE_VTABLE_WARNINGS
#if TOML_CHAR_8_STRINGS
#define TOML_NATIVE_STRING_TYPE_NAME "std::u8string"
#else
#define TOML_NATIVE_STRING_TYPE_NAME "std::string"
#endif
#define TOML_NATIVE_VALUE_TYPE_LIST \
"\n| - " TOML_NATIVE_STRING_TYPE_NAME \
"\n| - int64_t" \
"\n| - double" \
"\n| - bool" \
"\n| - toml::date" \
"\n| - toml::time" \
"\n| - toml::date_time"
#define TOML_NODE_TYPE_LIST \
"\n| - toml::table" \
"\n| - toml::array" \
"\n| - toml::value<" TOML_NATIVE_STRING_TYPE_NAME ">" \
"\n| - toml::value<int64_t>" \
"\n| - toml::value<double>" \
"\n| - toml::value<bool>" \
"\n| - toml::value<toml::date>" \
"\n| - toml::value<toml::time>" \
"\n| - toml::value<toml::date_time>"
#define TOML_UNWRAPPED_NODE_TYPE_LIST \
"\n|" \
"\n| A native TOML value type" \
TOML_NATIVE_VALUE_TYPE_LIST \
"\n|" \
"\n| A TOML node type" \
TOML_NODE_TYPE_LIST
namespace toml namespace toml
{ {
/// \brief A TOML node. /// \brief A TOML node.
@ -27,24 +62,27 @@ namespace toml
node& operator= (node&& rhs) noexcept; node& operator= (node&& rhs) noexcept;
template <typename T> template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE [[nodiscard]]
impl::node_of<T>& ref_cast() & noexcept TOML_ALWAYS_INLINE
impl::wrap_node<T>& ref_cast() & noexcept
{ {
return *reinterpret_cast<impl::node_of<T>*>(this); return *reinterpret_cast<impl::wrap_node<T>*>(this);
} }
template <typename T> template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE [[nodiscard]]
impl::node_of<T>&& ref_cast() && noexcept TOML_ALWAYS_INLINE
impl::wrap_node<T>&& ref_cast() && noexcept
{ {
return std::move(*reinterpret_cast<impl::node_of<T>*>(this)); return std::move(*reinterpret_cast<impl::wrap_node<T>*>(this));
} }
template <typename T> template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE [[nodiscard]]
const impl::node_of<T>& ref_cast() const & noexcept TOML_ALWAYS_INLINE
const impl::wrap_node<T>& ref_cast() const & noexcept
{ {
return *reinterpret_cast<const impl::node_of<T>*>(this); return *reinterpret_cast<const impl::wrap_node<T>*>(this);
} }
template <typename N, typename T> template <typename N, typename T>
@ -93,13 +131,14 @@ namespace toml
/// ///
/// \returns Returns true if this node is an instance of the specified type. /// \returns Returns true if this node is an instance of the specified type.
template <typename T> template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE [[nodiscard]]
bool is() const noexcept bool is() const noexcept
{ {
using type = impl::unwrapped<impl::remove_cvref_t<T>>; using type = impl::unwrap_node<T>;
static_assert( static_assert(
impl::is_value_or_node<type>, impl::is_native<type> || impl::is_one_of<type, table, array>,
"Template type parameter must be one of the TOML value types, a toml::table, or a toml::array" "The template type argument of node::is() must be one of the following:"
TOML_UNWRAPPED_NODE_TYPE_LIST
); );
if constexpr (std::is_same_v<type, table>) return is_table(); if constexpr (std::is_same_v<type, table>) return is_table();
@ -142,6 +181,19 @@ namespace toml
[[nodiscard]] virtual const toml::value<time>* as_time() const noexcept; [[nodiscard]] virtual const toml::value<time>* as_time() const noexcept;
[[nodiscard]] virtual const toml::value<date_time>* as_date_time() const noexcept; [[nodiscard]] virtual const toml::value<date_time>* as_date_time() const noexcept;
private:
template <typename T>
[[nodiscard]]
decltype(auto) get_value_exact() const noexcept;
public:
template <typename T>
[[nodiscard]]
optional<T> value_exact() const noexcept;
/// \brief Gets the raw value contained by this node. /// \brief Gets the raw value contained by this node.
/// ///
/// \detail The optional returned by this function will only contain a value if the node was an instance of /// \detail The optional returned by this function will only contain a value if the node was an instance of
@ -189,7 +241,8 @@ namespace toml
/// ///
/// \returns The underlying value if the node was a value of the matching type (or convertible to it), or an empty optional. /// \returns The underlying value if the node was a value of the matching type (or convertible to it), or an empty optional.
template <typename T> template <typename T>
[[nodiscard]] optional<T> value() const noexcept; [[nodiscard]]
optional<T> value() const noexcept;
/// \brief Gets the raw value contained by this node, or a default. /// \brief Gets the raw value contained by this node, or a default.
/// ///
@ -202,7 +255,8 @@ namespace toml
/// ///
/// \see node::value() /// \see node::value()
template <typename T> template <typename T>
[[nodiscard]] auto value_or(T&& default_value) const noexcept; [[nodiscard]]
auto value_or(T&& default_value) const noexcept;
/// \brief Gets a pointer to the node as a more specific node type. /// \brief Gets a pointer to the node as a more specific node type.
/// ///
@ -226,13 +280,14 @@ namespace toml
/// ///
/// \returns A pointer to the node as the given type, or nullptr if it was a different type. /// \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]]
impl::node_of<T>* as() noexcept impl::wrap_node<T>* as() noexcept
{ {
using type = impl::unwrapped<T>; using type = impl::unwrap_node<T>;
static_assert( static_assert(
impl::is_value_or_node<type>, impl::is_native<type> || impl::is_one_of<type, table, array>,
"Template type parameter must be one of the TOML value types, a toml::table, or a toml::array" "The template type argument of node::as() must be one of the following:"
TOML_UNWRAPPED_NODE_TYPE_LIST
); );
if constexpr (std::is_same_v<type, table>) return as_table(); if constexpr (std::is_same_v<type, table>) return as_table();
@ -248,13 +303,14 @@ namespace toml
/// \brief Gets a pointer to the node as a more specific node type (const overload). /// \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]]
const impl::node_of<T>* as() const noexcept const impl::wrap_node<T>* as() const noexcept
{ {
using type = impl::unwrapped<T>; using type = impl::unwrap_node<T>;
static_assert( static_assert(
impl::is_value_or_node<type>, impl::is_native<type> || impl::is_one_of<type, table, array>,
"Template type parameter must be one of the TOML value types, a toml::table, or a toml::array" "The template type argument of node::as() must be one of the following:"
TOML_UNWRAPPED_NODE_TYPE_LIST
); );
if constexpr (std::is_same_v<type, table>) return as_table(); if constexpr (std::is_same_v<type, table>) return as_table();
@ -424,18 +480,20 @@ namespace toml
template <typename T, typename N> template <typename T, typename N>
[[nodiscard]] static decltype(auto) do_ref(N&& n) noexcept [[nodiscard]]
static decltype(auto) do_ref(N&& n) noexcept
{ {
using type = impl::unwrapped<T>; using type = impl::unwrap_node<T>;
static_assert( static_assert(
impl::is_value_or_node<type>, impl::is_native<type> || impl::is_one_of<type, table, array>,
"Template type parameter must be one of the TOML value types, a toml::table, or a toml::array" "The template type argument of node::ref() must be one of the following:"
TOML_UNWRAPPED_NODE_TYPE_LIST
); );
TOML_ASSERT( TOML_ASSERT(
n.template is<T>() n.template is<T>()
&& "template type argument T provided to toml::node::ref() didn't match the node's actual type" && "template type argument T provided to toml::node::ref() didn't match the node's actual type"
); );
if constexpr (impl::is_value<type>) if constexpr (impl::is_native<type>)
return std::forward<N>(n).template ref_cast<type>().get(); return std::forward<N>(n).template ref_cast<type>().get();
else else
return std::forward<N>(n).template ref_cast<type>(); return std::forward<N>(n).template ref_cast<type>();
@ -511,21 +569,24 @@ namespace toml
/// ///
/// \returns A reference to the underlying data. /// \returns A reference to the underlying data.
template <typename T> template <typename T>
[[nodiscard]] impl::unwrapped<T>& ref() & noexcept [[nodiscard]]
impl::unwrap_node<T>& ref() & noexcept
{ {
return do_ref<T>(*this); return do_ref<T>(*this);
} }
/// \brief Gets a raw reference to a value node's underlying data (rvalue overload). /// \brief Gets a raw reference to a value node's underlying data (rvalue overload).
template <typename T> template <typename T>
[[nodiscard]] impl::unwrapped<T>&& ref() && noexcept [[nodiscard]]
impl::unwrap_node<T>&& ref() && noexcept
{ {
return do_ref<T>(std::move(*this)); return do_ref<T>(std::move(*this));
} }
/// \brief Gets a raw reference to a value node's underlying data (const lvalue overload). /// \brief Gets a raw reference to a value node's underlying data (const lvalue overload).
template <typename T> template <typename T>
[[nodiscard]] const impl::unwrapped<T>& ref() const& noexcept [[nodiscard]]
const impl::unwrap_node<T>& ref() const& noexcept
{ {
return do_ref<T>(*this); return do_ref<T>(*this);
} }

View File

@ -147,12 +147,14 @@ namespace toml
[[nodiscard]] [[nodiscard]]
auto as() const noexcept auto as() const noexcept
{ {
using type = impl::unwrap_node<U>;
static_assert( static_assert(
impl::is_value_or_node<impl::unwrapped<U>>, impl::is_native<type> || impl::is_one_of<type, table, array>,
"Template type parameter must be one of the basic value types, a toml::table, or a toml::array" "Template type parameter must be one of the following:"
TOML_UNWRAPPED_NODE_TYPE_LIST
); );
return node_ ? node_->template as<U>() : nullptr; return node_ ? node_->template as<type>() : nullptr;
} }
/// \brief Returns a pointer to the viewed node as a toml::table, if it is one. /// \brief Returns a pointer to the viewed node as a toml::table, if it is one.
@ -174,6 +176,15 @@ namespace toml
/// \brief Returns a pointer to the viewed node as a toml::value<date_time>, if it is one. /// \brief Returns a pointer to the viewed node as a toml::value<date_time>, if it is one.
[[nodiscard]] auto as_date_time() const noexcept { return as<date_time>(); } [[nodiscard]] auto as_date_time() const noexcept { return as<date_time>(); }
template <typename U>
[[nodiscard]]
optional<U> value_exact() const noexcept
{
if (node_)
return node_->template value_exact<U>();
return {};
}
TOML_PUSH_WARNINGS TOML_PUSH_WARNINGS
TOML_DISABLE_INIT_WARNINGS TOML_DISABLE_INIT_WARNINGS
@ -185,7 +196,8 @@ namespace toml
/// ///
/// \see node::value() /// \see node::value()
template <typename U> template <typename U>
[[nodiscard]] optional<U> value() const noexcept [[nodiscard]]
optional<U> value() const noexcept
{ {
if (node_) if (node_)
return node_->template value<U>(); return node_->template value<U>();
@ -205,12 +217,45 @@ namespace toml
/// ///
/// \see node::value_or() /// \see node::value_or()
template <typename U> template <typename U>
[[nodiscard]] auto value_or(U&& default_value) const noexcept [[nodiscard]]
auto value_or(U&& default_value) const noexcept
{ {
using return_type = decltype(node_->value_or(std::forward<U>(default_value))); using namespace ::toml::impl;
static_assert(
!is_wide_string<U> || TOML_WINDOWS_COMPAT,
"Retrieving values as wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled."
);
if constexpr (is_wide_string<U>)
{
#if TOML_WINDOWS_COMPAT
if (node_) if (node_)
return node_->value_or(std::forward<U>(default_value)); return node_->value_or(std::forward<U>(default_value));
return return_type{ std::forward<U>(default_value) }; return std::wstring{ std::forward<U>(default_value) };
#else
static_assert(impl::dependent_false<U>, "Evaluated unreachable branch!");
#endif
}
else
{
using value_type = std::conditional_t<
std::is_pointer_v<std::decay_t<U>>,
std::add_pointer_t<std::add_const_t<std::remove_pointer_t<std::decay_t<U>>>>,
std::decay_t<U>
>;
if (node_)
return node_->value_or(std::forward<U>(default_value));
if constexpr (std::is_pointer_v<value_type>)
return value_type{ default_value };
else
return std::forward<U>(default_value);
}
} }
/// \brief Invokes a visitor on the viewed node based on its concrete type. /// \brief Invokes a visitor on the viewed node based on its concrete type.
@ -252,10 +297,11 @@ namespace toml
template <typename U> template <typename U>
[[nodiscard]] decltype(auto) ref() const noexcept [[nodiscard]] decltype(auto) ref() const noexcept
{ {
using type = impl::unwrapped<U>; using type = impl::unwrap_node<U>;
static_assert( static_assert(
impl::is_value_or_node<type>, impl::is_native<type> || impl::is_one_of<type, table, array>,
"Template type parameter must be one of the TOML value types, a toml::table, or a toml::array" "Template type parameter must be one of the following:"
TOML_UNWRAPPED_NODE_TYPE_LIST
); );
TOML_ASSERT( TOML_ASSERT(
node_ node_
@ -296,7 +342,10 @@ namespace toml
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const toml::value<U>&, template <typename U>) TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const toml::value<U>&, template <typename U>)
/// \brief Returns true if the viewed node is a value with the same value as RHS. /// \brief Returns true if the viewed node is a value with the same value as RHS.
template <typename U, typename = std::enable_if_t<impl::is_value_or_promotable<U>>> template <typename U, typename = std::enable_if_t<
impl::is_native<U>
|| impl::is_losslessly_convertible_to_native<U>
>>
[[nodiscard]] friend bool operator == (const node_view& lhs, const U& rhs) noexcept [[nodiscard]] friend bool operator == (const node_view& lhs, const U& rhs) noexcept
{ {
static_assert( static_assert(
@ -304,20 +353,27 @@ namespace toml
"Comparison with wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled." "Comparison with wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled."
); );
#if TOML_WINDOWS_COMPAT
if constexpr (impl::is_wide_string<U>) if constexpr (impl::is_wide_string<U>)
return lhs == impl::narrow(rhs);
else
#endif
{ {
const auto val = lhs.as<impl::promoted<U>>(); #if TOML_WINDOWS_COMPAT
return lhs == impl::narrow(rhs);
#else
static_assert(impl::dependent_false<U>, "Evaluated unreachable branch!");
#endif
}
else
{
const auto val = lhs.as<impl::native_type_of<U>>();
return val && *val == rhs; return val && *val == rhs;
} }
} }
TOML_ASYMMETRICAL_EQUALITY_OPS( TOML_ASYMMETRICAL_EQUALITY_OPS(
const node_view&, const node_view&,
const U&, const U&,
template <typename U, typename = std::enable_if_t<impl::is_value_or_promotable<U>>> template <typename U, typename = std::enable_if_t<
impl::is_native<U>
|| impl::is_losslessly_convertible_to_native<U>
>>
) )
/// \brief Returns true if the viewed node is an array with the same contents as the RHS initializer list. /// \brief Returns true if the viewed node is an array with the same contents as the RHS initializer list.

View File

@ -1023,8 +1023,8 @@ namespace toml::impl
push_parse_scope("floating-point"sv); push_parse_scope("floating-point"sv);
start_recording(true); start_recording(true);
const int sign = *cp == U'-' ? -1 : 1; const bool negative = *cp == U'-';
if (is_match(*cp, U'+', U'-')) if (negative || *cp == U'+')
advance_and_return_if_error_or_eof({}); advance_and_return_if_error_or_eof({});
const bool inf = is_match(*cp, U'i', U'I'); const bool inf = is_match(*cp, U'i', U'I');
@ -1037,9 +1037,14 @@ namespace toml::impl
if (cp && !is_value_terminator(*cp)) if (cp && !is_value_terminator(*cp))
set_error_and_return_default("expected value-terminator, saw '"sv, to_sv(*cp), "'"sv); set_error_and_return_default("expected value-terminator, saw '"sv, to_sv(*cp), "'"sv);
return inf // control for implementations that don't properly implement std::numeric_limits<double>::quiet_NaN()
? sign * std::numeric_limits<double>::infinity() // and/or std::numeric_limits<double>::infinity() (e.g. due to -ffast-math and friends)
: std::numeric_limits<double>::quiet_NaN(); constexpr uint64_t neg_inf = 0b1111111111110000000000000000000000000000000000000000000000000000ull;
constexpr uint64_t pos_inf = 0b0111111111110000000000000000000000000000000000000000000000000000ull;
constexpr uint64_t qnan = 0b0111111111111000000000000000000000000000000000000000000000000000ull;
double rval;
std::memcpy(&rval, inf ? (negative ? &neg_inf : &pos_inf) : &qnan, sizeof(double));
return rval;
} }
TOML_PUSH_WARNINGS TOML_PUSH_WARNINGS

View File

@ -34,8 +34,7 @@ namespace toml::impl
// - Strings in C++. Honestly. // - Strings in C++. Honestly.
template <typename Char1, typename Char2> template <typename Char1, typename Char2>
TOML_ALWAYS_INLINE inline void print_to_stream(std::basic_string_view<Char1> str, std::basic_ostream<Char2>& stream)
void print_to_stream(std::basic_string_view<Char1> str, std::basic_ostream<Char2>& stream)
{ {
static_assert(sizeof(Char1) == 1); static_assert(sizeof(Char1) == 1);
static_assert(sizeof(Char2) == 1); static_assert(sizeof(Char2) == 1);
@ -43,8 +42,7 @@ namespace toml::impl
} }
template <typename Char1, typename Char2> template <typename Char1, typename Char2>
TOML_ALWAYS_INLINE inline void print_to_stream(const std::basic_string<Char1>& str, std::basic_ostream<Char2>& stream)
void print_to_stream(const std::basic_string<Char1>& str, std::basic_ostream<Char2>& stream)
{ {
static_assert(sizeof(Char1) == 1); static_assert(sizeof(Char1) == 1);
static_assert(sizeof(Char2) == 1); static_assert(sizeof(Char2) == 1);
@ -52,8 +50,7 @@ namespace toml::impl
} }
template <typename Char> template <typename Char>
TOML_ALWAYS_INLINE inline void print_to_stream(char character, std::basic_ostream<Char>& stream)
void print_to_stream(char character, std::basic_ostream<Char>& stream)
{ {
static_assert(sizeof(Char) == 1); static_assert(sizeof(Char) == 1);
stream.put(static_cast<Char>(character)); stream.put(static_cast<Char>(character));
@ -61,8 +58,7 @@ namespace toml::impl
template <typename Char> template <typename Char>
TOML_ATTR(nonnull) TOML_ATTR(nonnull)
TOML_ALWAYS_INLINE inline void print_to_stream(const char* str, size_t len, std::basic_ostream<Char>& stream)
void print_to_stream(const char* str, size_t len, std::basic_ostream<Char>& stream)
{ {
static_assert(sizeof(Char) == 1); static_assert(sizeof(Char) == 1);
stream.write(reinterpret_cast<const Char*>(str), static_cast<std::streamsize>(len)); stream.write(reinterpret_cast<const Char*>(str), static_cast<std::streamsize>(len));
@ -71,8 +67,7 @@ namespace toml::impl
#ifdef __cpp_lib_char8_t #ifdef __cpp_lib_char8_t
template <typename Char> template <typename Char>
TOML_ALWAYS_INLINE inline void print_to_stream(char8_t character, std::basic_ostream<Char>& stream)
void print_to_stream(char8_t character, std::basic_ostream<Char>& stream)
{ {
static_assert(sizeof(Char) == 1); static_assert(sizeof(Char) == 1);
stream.put(static_cast<Char>(character)); stream.put(static_cast<Char>(character));
@ -80,8 +75,7 @@ namespace toml::impl
template <typename Char> template <typename Char>
TOML_ATTR(nonnull) TOML_ATTR(nonnull)
TOML_ALWAYS_INLINE inline void print_to_stream(const char8_t* str, size_t len, std::basic_ostream<Char>& stream)
void print_to_stream(const char8_t* str, size_t len, std::basic_ostream<Char>& stream)
{ {
static_assert(sizeof(Char) == 1); static_assert(sizeof(Char) == 1);
stream.write(reinterpret_cast<const Char*>(str), static_cast<std::streamsize>(len)); stream.write(reinterpret_cast<const Char*>(str), static_cast<std::streamsize>(len));
@ -130,8 +124,7 @@ namespace toml::impl
#define TOML_P2S_OVERLOAD(Type) \ #define TOML_P2S_OVERLOAD(Type) \
template <typename Char> \ template <typename Char> \
TOML_ALWAYS_INLINE \ inline void print_to_stream(Type val, std::basic_ostream<Char>& stream) \
void print_to_stream(Type val, std::basic_ostream<Char>& stream) \
{ \ { \
static_assert(sizeof(Char) == 1); \ static_assert(sizeof(Char) == 1); \
print_integer_to_stream(val, stream); \ print_integer_to_stream(val, stream); \
@ -157,19 +150,21 @@ namespace toml::impl
"The stream's underlying character type must be 1 byte in size." "The stream's underlying character type must be 1 byte in size."
); );
switch (std::fpclassify(val)) switch (impl::fpclassify(val))
{ {
case FP_INFINITE: case fp_class::neg_inf:
if (val < T{}) print_to_stream("-inf"sv, stream);
print_to_stream('-', stream); break;
case fp_class::pos_inf:
print_to_stream("inf"sv, stream); print_to_stream("inf"sv, stream);
return; break;
case FP_NAN: case fp_class::nan:
print_to_stream("nan"sv, stream); print_to_stream("nan"sv, stream);
return; break;
default: case fp_class::ok:
{ {
static constexpr auto needs_decimal_point = [](auto&& s) noexcept static constexpr auto needs_decimal_point = [](auto&& s) noexcept
{ {
@ -204,32 +199,32 @@ namespace toml::impl
print_to_stream(".0"sv, stream); print_to_stream(".0"sv, stream);
} }
#endif #endif
break;
} }
TOML_NO_DEFAULT_CASE;
} }
} }
#if !TOML_ALL_INLINE #if !TOML_ALL_INLINE
extern template TOML_API void print_floating_point_to_stream(float, std::ostream&, bool);
extern template TOML_API void print_floating_point_to_stream(double, std::ostream&, bool); extern template TOML_API void print_floating_point_to_stream(double, std::ostream&, bool);
#endif #endif
#define TOML_P2S_OVERLOAD(Type) \ #define TOML_P2S_OVERLOAD(Type) \
template <typename Char> \ template <typename Char> \
TOML_ALWAYS_INLINE \ inline void print_to_stream(Type val, std::basic_ostream<Char>& stream) \
void print_to_stream(Type val, std::basic_ostream<Char>& stream) \
{ \ { \
static_assert(sizeof(Char) == 1); \ static_assert(sizeof(Char) == 1); \
print_floating_point_to_stream(val, stream); \ print_floating_point_to_stream(val, stream); \
} }
TOML_P2S_OVERLOAD(float)
TOML_P2S_OVERLOAD(double) TOML_P2S_OVERLOAD(double)
#undef TOML_P2S_OVERLOAD #undef TOML_P2S_OVERLOAD
template <typename Char> template <typename Char>
TOML_ALWAYS_INLINE inline void print_to_stream(bool val, std::basic_ostream<Char>& stream)
void print_to_stream(bool val, std::basic_ostream<Char>& stream)
{ {
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

@ -453,11 +453,15 @@ namespace toml
"Insertion using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled." "Insertion using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled."
); );
#if TOML_WINDOWS_COMPAT
if constexpr (impl::is_wide_string<K>) if constexpr (impl::is_wide_string<K>)
{
#if TOML_WINDOWS_COMPAT
return insert(impl::narrow(std::forward<K>(key)), std::forward<V>(val)); return insert(impl::narrow(std::forward<K>(key)), std::forward<V>(val));
else #else
static_assert(impl::dependent_false<K>, "Evaluated unreachable branch!");
#endif #endif
}
else
{ {
auto ipos = values.lower_bound(key); auto ipos = values.lower_bound(key);
if (ipos == values.end() || ipos->first != key) if (ipos == values.end() || ipos->first != key)
@ -560,11 +564,15 @@ namespace toml
"Insertion using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled." "Insertion using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled."
); );
#if TOML_WINDOWS_COMPAT
if constexpr (impl::is_wide_string<K>) if constexpr (impl::is_wide_string<K>)
{
#if TOML_WINDOWS_COMPAT
return insert_or_assign(impl::narrow(std::forward<K>(key)), std::forward<V>(val)); return insert_or_assign(impl::narrow(std::forward<K>(key)), std::forward<V>(val));
else #else
static_assert(impl::dependent_false<K>, "Evaluated unreachable branch!");
#endif #endif
}
else
{ {
auto ipos = values.lower_bound(key); auto ipos = values.lower_bound(key);
if (ipos == values.end() || ipos->first != key) if (ipos == values.end() || ipos->first != key)
@ -626,17 +634,22 @@ namespace toml
"Emplacement using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled." "Emplacement using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled."
); );
#if TOML_WINDOWS_COMPAT
if constexpr (impl::is_wide_string<K>) if constexpr (impl::is_wide_string<K>)
{
#if TOML_WINDOWS_COMPAT
return emplace<U>(impl::narrow(std::forward<K>(key)), std::forward<V>(args)...); return emplace<U>(impl::narrow(std::forward<K>(key)), std::forward<V>(args)...);
else #else
static_assert(impl::dependent_false<U>, "Evaluated unreachable branch!");
#endif #endif
}
else
{ {
using type = impl::unwrapped<U>; using type = impl::unwrap_node<U>;
static_assert( static_assert(
impl::is_value_or_node<type>, impl::is_native<type> || impl::is_one_of<type, table, array>,
"Emplacement type parameter must be one of the basic value types, a toml::table, or a toml::array" "The emplacement type argument of table::emplace() must be one of the following:"
TOML_UNWRAPPED_NODE_TYPE_LIST
); );
auto ipos = values.lower_bound(key); auto ipos = values.lower_bound(key);
@ -645,7 +658,7 @@ namespace toml
ipos = values.emplace_hint( ipos = values.emplace_hint(
ipos, ipos,
std::forward<K>(key), std::forward<K>(key),
new impl::node_of<type>{ std::forward<V>(args)... } new impl::wrap_node<type>{ std::forward<V>(args)... }
); );
return { ipos, true }; return { ipos, true };
} }
@ -782,11 +795,15 @@ namespace toml
"Retrieval using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled." "Retrieval using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled."
); );
#if TOML_WINDOWS_COMPAT
if constexpr (impl::is_wide_string<Key>) if constexpr (impl::is_wide_string<Key>)
{
#if TOML_WINDOWS_COMPAT
return do_get(vals, impl::narrow(key)); return do_get(vals, impl::narrow(key));
else #else
static_assert(impl::dependent_false<Key>, "Evaluated unreachable branch!");
#endif #endif
}
else
{ {
if (auto it = vals.find(key); it != vals.end()) if (auto it = vals.find(key); it != vals.end())
return { it->second.get() }; return { it->second.get() };
@ -927,7 +944,7 @@ namespace toml
/// ///
/// \returns A pointer to the node at the specified key if it was of the given type, or nullptr. /// \returns A pointer to the node at the specified key if it was of the given type, or nullptr.
template <typename T> template <typename T>
[[nodiscard]] impl::node_of<T>* get_as(string_view key) noexcept [[nodiscard]] impl::wrap_node<T>* get_as(string_view key) noexcept
{ {
return do_get_as<T>(values, key); return do_get_as<T>(values, key);
} }
@ -939,7 +956,7 @@ namespace toml
/// ///
/// \returns A pointer to the node at the specified key if it was of the given type, or nullptr. /// \returns A pointer to the node at the specified key if it was of the given type, or nullptr.
template <typename T> template <typename T>
[[nodiscard]] const impl::node_of<T>* get_as(string_view key) const noexcept [[nodiscard]] const impl::wrap_node<T>* get_as(string_view key) const noexcept
{ {
return do_get_as<T>(values, key); return do_get_as<T>(values, key);
} }
@ -955,7 +972,7 @@ namespace toml
/// ///
/// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled.
template <typename T> template <typename T>
[[nodiscard]] impl::node_of<T>* get_as(std::wstring_view key) noexcept [[nodiscard]] impl::wrap_node<T>* get_as(std::wstring_view key) noexcept
{ {
return get_as<T>(impl::narrow(key)); return get_as<T>(impl::narrow(key));
} }
@ -969,7 +986,7 @@ namespace toml
/// ///
/// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled.
template <typename T> template <typename T>
[[nodiscard]] const impl::node_of<T>* get_as(std::wstring_view key) const noexcept [[nodiscard]] const impl::wrap_node<T>* get_as(std::wstring_view key) const noexcept
{ {
return get_as<T>(impl::narrow(key)); return get_as<T>(impl::narrow(key));
} }

View File

@ -20,25 +20,27 @@ namespace toml
/// ///
/// \tparam T The value's data type. Can be one of: /// \tparam T The value's data type. Can be one of:
/// - toml::string /// - toml::string
/// - int64_t
/// - double
/// - bool
/// - toml::date /// - toml::date
/// - toml::time /// - toml::time
/// - toml::date_time /// - toml::date_time
/// - int64_t
/// - double
/// - bool
template <typename T> template <typename T>
class TOML_API value final : public node class TOML_API value final : public node
{ {
static_assert( static_assert(
impl::is_value<T>, impl::is_native<T>,
"Template type parameter must be one of the TOML value types" "A toml::value<> must model one of the native TOML value types:"
TOML_NATIVE_VALUE_TYPE_LIST
); );
private: private:
friend class TOML_PARSER_TYPENAME; friend class TOML_PARSER_TYPENAME;
template <typename U, typename V> template <typename U, typename V>
[[nodiscard]] TOML_ALWAYS_INLINE [[nodiscard]]
TOML_ALWAYS_INLINE
static auto as_value([[maybe_unused]] V* ptr) noexcept static auto as_value([[maybe_unused]] V* ptr) noexcept
{ {
if constexpr (std::is_same_v<T, U>) if constexpr (std::is_same_v<T, U>)
@ -194,21 +196,40 @@ namespace toml
} }
/// \brief Value equality operator. /// \brief Value equality operator.
[[nodiscard]] friend bool operator == (const value& lhs, value_arg rhs) noexcept [[nodiscard]]
friend bool operator == (const value& lhs, value_arg rhs) noexcept
{ {
if constexpr (std::is_same_v<value_type, double>) if constexpr (std::is_same_v<value_type, double>)
{ {
using namespace ::toml::impl;
static constexpr auto pack = [](auto l, auto r) constexpr noexcept static constexpr auto pack = [](auto l, auto r) constexpr noexcept
{ {
return ((static_cast<uint64_t>(l) << 32) | static_cast<uint64_t>(r)); return (static_cast<uint64_t>(unbox_enum(l)) << 32)
| static_cast<uint64_t>(unbox_enum(r));
}; };
switch (pack(std::fpclassify(lhs.val_), std::fpclassify(rhs))) switch (pack(impl::fpclassify(lhs.val_), impl::fpclassify(rhs)))
{ {
case pack(FP_INFINITE, FP_INFINITE): return (lhs.val_ < 0.0) == (rhs < 0.0); case pack(fp_class::pos_inf, fp_class::neg_inf): [[fallthrough]];
case pack(FP_NAN, FP_NAN): return true; case pack(fp_class::pos_inf, fp_class::nan): [[fallthrough]];
default: return lhs.val_ == rhs; case pack(fp_class::neg_inf, fp_class::pos_inf): [[fallthrough]];
case pack(fp_class::neg_inf, fp_class::nan): [[fallthrough]];
case pack(fp_class::nan, fp_class::pos_inf): [[fallthrough]];
case pack(fp_class::nan, fp_class::neg_inf):
return false;
case pack(fp_class::pos_inf, fp_class::pos_inf): [[fallthrough]];
case pack(fp_class::neg_inf, fp_class::neg_inf): [[fallthrough]];
case pack(fp_class::nan, fp_class::nan):
return true;
case pack(fp_class::ok, fp_class::ok):
return lhs.val_ == rhs;
TOML_NO_DEFAULT_CASE;
} }
TOML_UNREACHABLE;
} }
else else
return lhs.val_ == rhs; return lhs.val_ == rhs;
@ -241,7 +262,8 @@ namespace toml
/// ///
/// \returns True if the values were of the same type and contained the same value. /// \returns True if the values were of the same type and contained the same value.
template <typename U> template <typename U>
[[nodiscard]] friend bool operator == (const value& lhs, const value<U>& rhs) noexcept [[nodiscard]]
friend bool operator == (const value& lhs, const value<U>& rhs) noexcept
{ {
if constexpr (std::is_same_v<T, U>) if constexpr (std::is_same_v<T, U>)
return lhs == rhs.val_; //calls asymmetrical value-equality operator defined above return lhs == rhs.val_; //calls asymmetrical value-equality operator defined above
@ -256,7 +278,8 @@ namespace toml
/// ///
/// \returns True if the values were not of the same type, or did not contain the same value. /// \returns True if the values were not of the same type, or did not contain the same value.
template <typename U> template <typename U>
[[nodiscard]] friend bool operator != (const value& lhs, const value<U>& rhs) noexcept [[nodiscard]]
friend bool operator != (const value& lhs, const value<U>& rhs) noexcept
{ {
return !(lhs == rhs); return !(lhs == rhs);
} }
@ -269,7 +292,8 @@ namespace toml
/// \returns <strong><em>Same value types:</em></strong> `lhs.get() < rhs.get()` <br> /// \returns <strong><em>Same value types:</em></strong> `lhs.get() < rhs.get()` <br>
/// <strong><em>Different value types:</em></strong> `lhs.type() < rhs.type()` /// <strong><em>Different value types:</em></strong> `lhs.type() < rhs.type()`
template <typename U> template <typename U>
[[nodiscard]] friend bool operator < (const value& lhs, const value<U>& rhs) noexcept [[nodiscard]]
friend bool operator < (const value& lhs, const value<U>& rhs) noexcept
{ {
if constexpr (std::is_same_v<T, U>) if constexpr (std::is_same_v<T, U>)
return lhs.val_ < rhs.val_; return lhs.val_ < rhs.val_;
@ -285,7 +309,8 @@ namespace toml
/// \returns <strong><em>Same value types:</em></strong> `lhs.get() <= rhs.get()` <br> /// \returns <strong><em>Same value types:</em></strong> `lhs.get() <= rhs.get()` <br>
/// <strong><em>Different value types:</em></strong> `lhs.type() <= rhs.type()` /// <strong><em>Different value types:</em></strong> `lhs.type() <= rhs.type()`
template <typename U> template <typename U>
[[nodiscard]] friend bool operator <= (const value& lhs, const value<U>& rhs) noexcept [[nodiscard]]
friend bool operator <= (const value& lhs, const value<U>& rhs) noexcept
{ {
if constexpr (std::is_same_v<T, U>) if constexpr (std::is_same_v<T, U>)
return lhs.val_ <= rhs.val_; return lhs.val_ <= rhs.val_;
@ -301,7 +326,8 @@ namespace toml
/// \returns <strong><em>Same value types:</em></strong> `lhs.get() > rhs.get()` <br> /// \returns <strong><em>Same value types:</em></strong> `lhs.get() > rhs.get()` <br>
/// <strong><em>Different value types:</em></strong> `lhs.type() > rhs.type()` /// <strong><em>Different value types:</em></strong> `lhs.type() > rhs.type()`
template <typename U> template <typename U>
[[nodiscard]] friend bool operator > (const value& lhs, const value<U>& rhs) noexcept [[nodiscard]]
friend bool operator > (const value& lhs, const value<U>& rhs) noexcept
{ {
if constexpr (std::is_same_v<T, U>) if constexpr (std::is_same_v<T, U>)
return lhs.val_ > rhs.val_; return lhs.val_ > rhs.val_;
@ -317,7 +343,8 @@ namespace toml
/// \returns <strong><em>Same value types:</em></strong> `lhs.get() >= rhs.get()` <br> /// \returns <strong><em>Same value types:</em></strong> `lhs.get() >= rhs.get()` <br>
/// <strong><em>Different value types:</em></strong> `lhs.type() >= rhs.type()` /// <strong><em>Different value types:</em></strong> `lhs.type() >= rhs.type()`
template <typename U> template <typename U>
[[nodiscard]] friend bool operator >= (const value& lhs, const value<U>& rhs) noexcept [[nodiscard]]
friend bool operator >= (const value& lhs, const value<U>& rhs) noexcept
{ {
if constexpr (std::is_same_v<T, U>) if constexpr (std::is_same_v<T, U>)
return lhs.val_ >= rhs.val_; return lhs.val_ >= rhs.val_;
@ -339,128 +366,265 @@ namespace toml
TOML_POP_WARNINGS TOML_POP_WARNINGS
#endif #endif
template <size_t N> value(const string_char(&)[N]) -> value<string>; template <typename T>
template <size_t N> value(const string_char(&&)[N]) -> value<string>; value(T) -> value<impl::native_type_of<impl::remove_cvref_t<T>>>;
value(const string_char*) -> value<string>;
template <size_t N> value(string_char(&)[N]) -> value<string>;
template <size_t N> value(string_char(&&)[N]) -> value<string>;
value(string_char*) -> value<string>;
value(string_view) -> value<string>;
value(string) -> value<string>;
value(bool) -> value<bool>;
value(float) -> value<double>;
value(double) -> value<double>;
value(int8_t) -> value<int64_t>;
value(int16_t) -> value<int64_t>;
value(int32_t) -> value<int64_t>;
value(int64_t) -> value<int64_t>;
value(uint8_t) -> value<int64_t>;
value(uint16_t) -> value<int64_t>;
value(uint32_t) -> value<int64_t>;
value(date) -> value<date>;
value(time) -> value<time>;
value(date_time) -> value<date_time>;
#ifdef TOML_SMALL_FLOAT_TYPE
value(TOML_SMALL_FLOAT_TYPE) -> value<double>;
#endif
#ifdef TOML_SMALL_INT_TYPE
value(TOML_SMALL_INT_TYPE) -> value<int64_t>;
#endif
TOML_PUSH_WARNINGS TOML_PUSH_WARNINGS
TOML_DISABLE_INIT_WARNINGS TOML_DISABLE_INIT_WARNINGS
TOML_DISABLE_SWITCH_WARNINGS
template <typename T>
[[nodiscard]]
inline decltype(auto) node::get_value_exact() const noexcept
{
using namespace ::toml::impl;
static_assert(node_type_of<T> != node_type::none);
static_assert(node_type_of<T> != node_type::table);
static_assert(node_type_of<T> != node_type::array);
static_assert(is_native<T> || can_represent_native<T>);
TOML_ASSERT(this->type() == node_type_of<T>);
if constexpr (node_type_of<T> == node_type::string)
{
const auto& str = *ref_cast<string>();
if constexpr (std::is_same_v<T, string>)
return str;
else if constexpr (std::is_same_v<T, string_view>)
return T{ str };
else if constexpr (std::is_same_v<T, const string_char*>)
return str.c_str();
else if constexpr (std::is_same_v<T, std::wstring>)
{
#if TOML_WINDOWS_COMPAT
return widen(str);
#else
static_assert(dependent_false<T>, "Evaluated unreachable branch!");
#endif
}
// char8_t -> char (safe)
else if constexpr (is_one_of<T, std::string, std::string_view>)
return T(reinterpret_cast<const char*>(str.c_str()), str.length());
else if constexpr (std::is_same_v<T, const char*>)
return reinterpret_cast<const char*>(str.c_str());
#ifdef __cpp_lib_char8_t
// char -> char8_t (potentially unsafe - the feature is 'experimental'!)
else if constexpr (is_one_of<T, std::u8string, std::u8string_view>)
return T(reinterpret_cast<const char8_t*>(str.c_str()), str.length());
else if constexpr (std::is_same_v<T, const char8_t*>)
return reinterpret_cast<const char8_t*>(str.c_str());
else
static_assert(dependent_false<T>, "Evaluated unreachable branch!");
#endif
}
else
return static_cast<T>(*ref_cast<native_type_of<T>>());
}
template <typename T>
inline optional<T> node::value_exact() const noexcept
{
using namespace ::toml::impl;
static_assert(
!is_wide_string<T> || TOML_WINDOWS_COMPAT,
"Retrieving values as wide-character strings with node::value_exact() is only "
"supported on Windows with TOML_WINDOWS_COMPAT enabled."
);
static_assert(
is_native<T> || can_represent_native<T>,
"The return type of node::value_exact() must be one of the following:"
"\n|"
"\n| A native TOML value type"
TOML_NATIVE_VALUE_TYPE_LIST
"\n|"
"\n| A non-view type capable of losslessly representing a native TOML value type"
#if TOML_CHAR_8_STRINGS
"\n| - std::string"
#elif defined __cpp_lib_char8_t
"\n| - std::u8string"
#endif
#if TOML_WINDOWS_COMPAT
"\n| - std::wstring"
#endif
"\n| - any signed integer type >= 64 bits"
"\n| - any floating-point type >= 64 bits of precision"
"\n|"
"\n| An immutable view type not requiring additional temporary storage"
"\n| - std::string_view"
"\n| - const char*"
#ifdef __cpp_lib_char8_t
"\n| - std::u8string_view"
"\n| - const char8_t*"
#endif
);
if (type() == node_type_of<T>)
return { this->get_value_exact<T>() };
else
return {};
}
template <typename T> template <typename T>
inline optional<T> node::value() const noexcept inline optional<T> node::value() const noexcept
{ {
using namespace ::toml::impl;
static_assert( static_assert(
!impl::is_wide_string<T> || TOML_WINDOWS_COMPAT, !is_wide_string<T> || TOML_WINDOWS_COMPAT,
"Retrieving values as wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled." "Retrieving values as wide-character strings with node::value() is only "
"supported on Windows with TOML_WINDOWS_COMPAT enabled."
); );
static_assert( static_assert(
impl::is_value<T> is_native<T> || can_represent_native<T> || can_partially_represent_native<T>,
|| std::is_same_v<T, string_view> "The return type of node::value() must be one of the following:"
|| (TOML_WINDOWS_COMPAT && std::is_same_v<T, std::wstring>), "\n|"
"Value type must be one of the TOML value types (or string_view)" "\n| A native TOML value type"
TOML_NATIVE_VALUE_TYPE_LIST
"\n|"
"\n| A non-view type capable of losslessly representing a native TOML value type"
#if TOML_CHAR_8_STRINGS
"\n| - std::string"
#elif defined __cpp_lib_char8_t
"\n| - std::u8string"
#endif
#if TOML_WINDOWS_COMPAT
"\n| - std::wstring"
#endif
"\n| - any signed integer type >= 64 bits"
"\n| - any floating-point type >= 64 bits of precision"
"\n|"
"\n| A non-view type capable of (reasonably) representing a native TOML value type"
"\n| - any other integer type"
"\n| - any floating-point type >= 32 bits of precision"
"\n|"
"\n| An immutable view type not requiring additional temporary storage"
"\n| - std::string_view"
"\n| - const char*"
#ifdef __cpp_lib_char8_t
"\n| - std::u8string_view"
"\n| - const char8_t*"
#endif
); );
// when asking for strings, dates, times and date_times there's no 'fuzzy' conversion
// semantics to be mindful of so the exact retrieval is enough.
if constexpr (is_natively_one_of<T, string, time, date, date_time>)
{
if (type() == node_type_of<T>)
return { this->get_value_exact<T>() };
else
return {};
}
// everything else requires a bit of logicking.
else
{
switch (type()) switch (type())
{ {
case node_type::none: [[fallthrough]]; // int -> *
case node_type::table: [[fallthrough]];
case node_type::array:
return {};
case node_type::string:
{
if constexpr (std::is_same_v<T, string> || std::is_same_v<T, string_view>)
return { T{ ref_cast<string>().get() } };
#if TOML_WINDOWS_COMPAT
else if constexpr (std::is_same_v<T, std::wstring>)
return { impl::widen(ref_cast<string>().get()) };
#endif
else
return {};
}
case node_type::integer: case node_type::integer:
{ {
if constexpr (std::is_same_v<T, int64_t>) // int -> int
return ref_cast<int64_t>().get(); if constexpr (is_natively_one_of<T, int64_t>)
else if constexpr (std::is_same_v<T, double>) {
return static_cast<double>(ref_cast<int64_t>().get()); if constexpr (is_native<T> || can_represent_native<T>)
return static_cast<T>(*ref_cast<int64_t>());
else
{
using traits = value_traits<T>;
const int64_t val = *ref_cast<int64_t>();
if constexpr (!traits::is_signed)
{
if constexpr ((sizeof(T) * CHAR_BIT) <= 53) // 53 bits < int64_max < 54 bits
{
using common_t = decltype(int64_t{} + T{});
if (val < int64_t{} || static_cast<common_t>(val) > static_cast<common_t>(traits::max))
return {};
}
else
{
if (val < int64_t{})
return {};
}
}
else
{
if (val < traits::min || val > traits::max)
return {};
}
return { static_cast<T>(val) };
}
}
// int -> float
else if constexpr (is_natively_one_of<T, double>)
{
const int64_t val = *ref_cast<int64_t>();
if constexpr (std::numeric_limits<T>::digits < 64)
{
constexpr auto largest_whole_float = (int64_t{ 1 } << std::numeric_limits<T>::digits);
if (val < -largest_whole_float || val > largest_whole_float)
return {};
}
return static_cast<T>(val);
}
// int -> bool
else if constexpr (is_natively_one_of<T, bool>)
return static_cast<bool>(*ref_cast<int64_t>());
// int -> anything else
else else
return {}; return {};
} }
// float -> *
case node_type::floating_point: case node_type::floating_point:
{ {
if constexpr (std::is_same_v<T, double>) // float -> float
return ref_cast<double>().get(); if constexpr (is_natively_one_of<T, double>)
else if constexpr (std::is_same_v<T, int64_t>) {
return static_cast<int64_t>(ref_cast<double>().get()); if constexpr (is_native<T> || can_represent_native<T>)
return { static_cast<T>(*ref_cast<double>()) };
else
{
const double val = *ref_cast<double>();
if (val < (std::numeric_limits<T>::lowest)() || val > (std::numeric_limits<T>::max)())
return {};
return { static_cast<T>(val) };
}
}
// float -> anything else
else else
return {}; return {};
} }
// bool -> *
case node_type::boolean: case node_type::boolean:
{ {
if constexpr (std::is_same_v<T, bool>) // bool -> bool
return ref_cast<bool>().get(); if constexpr (is_natively_one_of<T, bool>)
return { *ref_cast<bool>() };
// bool -> int
else if constexpr (is_natively_one_of<T, int64_t>)
return { static_cast<T>(*ref_cast<bool>()) };
// bool -> anything else
else else
return {}; return {};
} }
}
case node_type::date: // non-values, or 'exact' types covered above
{
if constexpr (std::is_same_v<T, date>)
return ref_cast<date>().get();
else
return {}; return {};
} }
case node_type::time:
{
if constexpr (std::is_same_v<T, time>)
return ref_cast<time>().get();
else
return {};
}
case node_type::date_time:
{
if constexpr (std::is_same_v<T, date_time>)
return ref_cast<date_time>().get();
else
return {};
}
TOML_NO_DEFAULT_CASE;
}
TOML_UNREACHABLE;
} }
TOML_POP_WARNINGS TOML_POP_WARNINGS
@ -468,35 +632,95 @@ namespace toml
template <typename T> template <typename T>
inline auto node::value_or(T&& default_value) const noexcept inline auto node::value_or(T&& default_value) const noexcept
{ {
using namespace ::toml::impl;
static_assert( static_assert(
!impl::is_wide_string<T> || TOML_WINDOWS_COMPAT, !is_wide_string<T> || TOML_WINDOWS_COMPAT,
"Retrieving values as wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled." "Retrieving values as wide-character strings with node::value_or() is only "
); "supported on Windows with TOML_WINDOWS_COMPAT enabled."
static_assert(
impl::is_value_or_promotable<impl::remove_cvref_t<T>>,
"Default value type must be (or be promotable to) one of the TOML value types"
); );
#if TOML_WINDOWS_COMPAT
if constexpr (impl::is_wide_string<T>) if constexpr (is_wide_string<T>)
{ {
if (this->type() == node_type::string) #if TOML_WINDOWS_COMPAT
return impl::widen(ref_cast<string>().get());
if (type() == node_type::string)
return widen(*ref_cast<string>());
return std::wstring{ std::forward<T>(default_value) }; return std::wstring{ std::forward<T>(default_value) };
#else
static_assert(dependent_false<T>, "Evaluated unreachable branch!");
#endif
} }
else else
#endif
{ {
using value_type = impl::promoted<impl::remove_cvref_t<T>>; using value_type = std::conditional_t<
using return_type = std::conditional_t< std::is_pointer_v<std::decay_t<T>>,
std::is_same_v<value_type, string>, std::add_pointer_t<std::add_const_t<std::remove_pointer_t<std::decay_t<T>>>>,
std::conditional_t<std::is_same_v<impl::remove_cvref_t<T>, string>, string, string_view>, std::decay_t<T>
value_type
>; >;
using traits = value_traits<value_type>;
if (auto val = this->value<return_type>()) static_assert(
traits::is_native || traits::can_represent_native || traits::can_partially_represent_native,
"The default return value type of node::value_or() must be one of the following:"
"\n|"
"\n| A native TOML value type"
TOML_NATIVE_VALUE_TYPE_LIST
"\n| "
"\n| A non-view type capable of losslessly representing a native TOML value type"
#if TOML_CHAR_8_STRINGS
"\n| - std::string"
#elif defined __cpp_lib_char8_t
"\n| - std::u8string"
#endif
#if TOML_WINDOWS_COMPAT
"\n| - std::wstring"
#endif
"\n| - any signed integer type >= 64 bits"
"\n| - any floating-point type >= 64 bits of precision"
"\n|"
"\n| A non-view type capable of (reasonably) representing a native TOML value type"
"\n| - any other integer type"
"\n| - any floating-point type >= 32 bits of precision"
"\n|"
"\n| A compatible view type"
"\n| - std::string_view"
"\n| - const char*"
"\n| - const char[] (deduced as const char*)"
"\n| - char* (deduced as const char*)"
#ifdef __cpp_lib_char8_t
"\n| - std::u8string_view"
"\n| - const char8_t*"
"\n| - const char8_t[] (deduced as const char8_t*)"
"\n| - char8_t* (deduced as const char8_t*)"
#endif
#if TOML_WINDOWS_COMPAT
"\n| - std::wstring_view (promoted to std::wstring)"
"\n| - const wchar_t* (promoted to std::wstring)"
"\n| - const wchar_t[] (promoted to std::wstring)"
"\n| - wchar_t* (promoted to std::wstring)"
#endif
);
if constexpr (traits::is_native)
{
if (type() == node_type_of<value_type>)
return *ref_cast<typename traits::native_type>();
return std::forward<T>(default_value);
}
else
{
if (auto val = this->value<value_type>())
return *val; return *val;
return return_type{ std::forward<T>(default_value) }; if constexpr (std::is_pointer_v<value_type>)
return value_type{ default_value };
else
return std::forward<T>(default_value);
}
} }
} }
} }

View File

@ -6,8 +6,8 @@
#pragma once #pragma once
#define TOML_LIB_MAJOR 1 #define TOML_LIB_MAJOR 1
#define TOML_LIB_MINOR 3 #define TOML_LIB_MINOR 4
#define TOML_LIB_PATCH 4 #define TOML_LIB_PATCH 0
#define TOML_LANG_MAJOR 1 #define TOML_LANG_MAJOR 1
#define TOML_LANG_MINOR 0 #define TOML_LANG_MINOR 0

View File

@ -1,7 +1,7 @@
project( project(
'tomlplusplus', 'tomlplusplus',
'cpp', 'cpp',
version : '1.3.4', version : '1.4.0',
license : 'MIT', license : 'MIT',
default_options : [ default_options : [
'cpp_std=c++17', 'cpp_std=c++17',
@ -104,7 +104,6 @@ if build_tests or build_examples
endif endif
add_project_arguments([ add_project_arguments([
'-ferror-limit=5', '-ferror-limit=5',
'-fchar8_t',
'-Wno-unused-command-line-argument', '-Wno-unused-command-line-argument',
# flags from here down are disabling stupidly pedantic warnings that only appear with -Weverything # flags from here down are disabling stupidly pedantic warnings that only appear with -Weverything

View File

@ -35,9 +35,9 @@ def python_value_to_tomlpp(val):
return 'true' if val else 'false' return 'true' if val else 'false'
elif isinstance(val, float): elif isinstance(val, float):
if math.isinf(val): if math.isinf(val):
return ('-' if val < 0.0 else '') + 'std::numeric_limits<double>::infinity()' return 'make_infinity({})'.format('' if val >= 0.0 else '-1')
elif math.isnan(val): elif math.isnan(val):
return 'std::numeric_limits<double>::quiet_NaN()' return 'make_nan()'
else: else:
return str(val) return str(val)
elif isinstance(val, int): elif isinstance(val, int):

View File

@ -155,7 +155,6 @@ class HTMLDocument(object):
def html_find_parent(tag, names, cutoff=None): def html_find_parent(tag, names, cutoff=None):
if not utils.is_collection(names): if not utils.is_collection(names):
names = [ names ] names = [ names ]
@ -861,13 +860,10 @@ class ExtDocLinksFix(object):
def __call__(self, file, doc): def __call__(self, file, doc):
changed = False changed = False
root = doc.body.main.article.div.div root = doc.body.main.article.div.div
tags = tags = html_shallow_search(root, self.__allowedNames, lambda t: html_find_parent(t, 'a', root) is None) tags = tags = html_shallow_search(root, self.__allowedNames, lambda t: html_find_parent(t, 'a', root) is None)
strings = [] strings = []
for tag in tags: for tag in tags:
strings = strings + html_string_descendants(tag, lambda t: html_find_parent(t, 'a', tag) is None) strings = strings + html_string_descendants(tag, lambda t: html_find_parent(t, 'a', tag) is None)
for expr, uri in self.__expressions: for expr, uri in self.__expressions:
for string in strings: for string in strings:
if string.parent is None: if string.parent is None:
@ -880,7 +876,6 @@ class ExtDocLinksFix(object):
if (begins_with_ws and new_tag.string is not None and not new_tag.string[:1].isspace()): if (begins_with_ws and new_tag.string is not None and not new_tag.string[:1].isspace()):
new_tag.insert_before(' ') new_tag.insert_before(' ')
changed = True changed = True
return changed return changed
@ -1009,11 +1004,6 @@ def postprocess_file(dir, file, fixes):
def preprocess_xml(xml_dir): def preprocess_xml(xml_dir):
pass pass

View File

@ -79,6 +79,22 @@ def is_collection(val):
def is_pow2(v):
return v & (v-1) == 0
def next_power_of_2(n):
if n == 0:
return 1
if n & (n - 1) == 0:
return n
while n & (n - 1) > 0:
n &= (n - 1)
return n << 1
def get_all_files(dir, all=None, any=None): def get_all_files(dir, all=None, any=None):
files = [f for f in [path.join(dir, f) for f in os.listdir(dir)] if path.isfile(f)] files = [f for f in [path.join(dir, f) for f in os.listdir(dir)] if path.isfile(f)]
if (files and all is not None): if (files and all is not None):

View File

@ -6,8 +6,17 @@
#pragma once #pragma once
#include "settings.h" #include "settings.h"
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#endif
#if __has_include(<Catch2/single_include/catch2/catch.hpp>) #if __has_include(<Catch2/single_include/catch2/catch.hpp>)
#include <Catch2/single_include/catch2/catch.hpp> #include <Catch2/single_include/catch2/catch.hpp>
#else #else
#error Catch2 is missing! You probably need to fetch submodules ("git submodule update --init extern/Catch2") #error Catch2 is missing! You probably need to fetch submodules ("git submodule update --init extern/Catch2")
#endif #endif
#ifdef __clang__
#pragma clang diagnostic pop
#endif

View File

@ -744,7 +744,7 @@ TEST_CASE("conformance - iarna/valid")
parsing_should_succeed(FILE_LINE_ARGS, spec_float_10, [](toml::table&& tbl) parsing_should_succeed(FILE_LINE_ARGS, spec_float_10, [](toml::table&& tbl)
{ {
auto expected = toml::table{{ auto expected = toml::table{{
{ S(R"(sf1)"sv), std::numeric_limits<double>::infinity() }, { S(R"(sf1)"sv), make_infinity() },
}}; }};
REQUIRE(tbl == expected); REQUIRE(tbl == expected);
}); });
@ -752,7 +752,7 @@ TEST_CASE("conformance - iarna/valid")
parsing_should_succeed(FILE_LINE_ARGS, spec_float_11, [](toml::table&& tbl) parsing_should_succeed(FILE_LINE_ARGS, spec_float_11, [](toml::table&& tbl)
{ {
auto expected = toml::table{{ auto expected = toml::table{{
{ S(R"(sf2)"sv), std::numeric_limits<double>::infinity() }, { S(R"(sf2)"sv), make_infinity() },
}}; }};
REQUIRE(tbl == expected); REQUIRE(tbl == expected);
}); });
@ -760,7 +760,7 @@ TEST_CASE("conformance - iarna/valid")
parsing_should_succeed(FILE_LINE_ARGS, spec_float_12, [](toml::table&& tbl) parsing_should_succeed(FILE_LINE_ARGS, spec_float_12, [](toml::table&& tbl)
{ {
auto expected = toml::table{{ auto expected = toml::table{{
{ S(R"(sf2)"sv), -std::numeric_limits<double>::infinity() }, { S(R"(sf2)"sv), make_infinity(-1) },
}}; }};
REQUIRE(tbl == expected); REQUIRE(tbl == expected);
}); });
@ -768,7 +768,7 @@ TEST_CASE("conformance - iarna/valid")
parsing_should_succeed(FILE_LINE_ARGS, spec_float_13, [](toml::table&& tbl) parsing_should_succeed(FILE_LINE_ARGS, spec_float_13, [](toml::table&& tbl)
{ {
auto expected = toml::table{{ auto expected = toml::table{{
{ S(R"(sf4)"sv), std::numeric_limits<double>::quiet_NaN() }, { S(R"(sf4)"sv), make_nan() },
}}; }};
REQUIRE(tbl == expected); REQUIRE(tbl == expected);
}); });
@ -776,7 +776,7 @@ TEST_CASE("conformance - iarna/valid")
parsing_should_succeed(FILE_LINE_ARGS, spec_float_14, [](toml::table&& tbl) parsing_should_succeed(FILE_LINE_ARGS, spec_float_14, [](toml::table&& tbl)
{ {
auto expected = toml::table{{ auto expected = toml::table{{
{ S(R"(sf5)"sv), std::numeric_limits<double>::quiet_NaN() }, { S(R"(sf5)"sv), make_nan() },
}}; }};
REQUIRE(tbl == expected); REQUIRE(tbl == expected);
}); });
@ -784,7 +784,7 @@ TEST_CASE("conformance - iarna/valid")
parsing_should_succeed(FILE_LINE_ARGS, spec_float_15, [](toml::table&& tbl) parsing_should_succeed(FILE_LINE_ARGS, spec_float_15, [](toml::table&& tbl)
{ {
auto expected = toml::table{{ auto expected = toml::table{{
{ S(R"(sf6)"sv), std::numeric_limits<double>::quiet_NaN() }, { S(R"(sf6)"sv), make_nan() },
}}; }};
REQUIRE(tbl == expected); REQUIRE(tbl == expected);
}); });

27
tests/evil_macros.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
// simulate some 'evil' macros to ensure the library is robust against them if they appear in the wild.
#ifdef _WIN32
#ifndef min
#define min(...) THIS_IS_AN_EVIL_MACRO
#endif
#ifndef max
#define max(...) THIS_IS_AN_EVIL_MACRO
#endif
#ifndef near
#define near THIS_IS_AN_EVIL_MACRO
#endif
#ifndef far
#define far THIS_IS_AN_EVIL_MACRO
#endif
#else
// ...
#endif

View File

@ -9,11 +9,7 @@
#endif #endif
#if USE_TARTANLLAMA_OPTIONAL #if USE_TARTANLLAMA_OPTIONAL
#if __has_include(<tloptional/include/tl/optional.hpp>) #include "tloptional.h"
#include <tloptional/include/tl/optional.hpp>
#else
#error TartanLlama/optional is missing! You probably need to fetch submodules ("git submodule update --init extern/tloptional")
#endif
#endif #endif
#if USE_SINGLE_HEADER #if USE_SINGLE_HEADER
@ -30,11 +26,234 @@
#error TOML_WINDOWS_COMPAT does not match _WIN32 (default behaviour should be to match) #error TOML_WINDOWS_COMPAT does not match _WIN32 (default behaviour should be to match)
#endif #endif
#ifdef __SIZEOF_INT128__
#define int128 __int128_t
#define uint128 __uint128_t
#endif
#if defined(FLT16_MANT_DIG) && defined(FLT16_DIG) && defined(FLT16_DECIMAL_DIG)
#define float16 _Float16
#endif
namespace toml namespace toml
{ {
using std::declval; using std::declval;
using std::is_same_v; using std::is_same_v;
#define CHECK_CAN_REPRESENT_NATIVE(T, expected) \
static_assert((impl::value_traits<T>::is_native || impl::value_traits<T>::can_represent_native) == expected)
CHECK_CAN_REPRESENT_NATIVE(time, true);
CHECK_CAN_REPRESENT_NATIVE(date, true);
CHECK_CAN_REPRESENT_NATIVE(date_time, true);
CHECK_CAN_REPRESENT_NATIVE(bool, true);
CHECK_CAN_REPRESENT_NATIVE(int8_t, false);
CHECK_CAN_REPRESENT_NATIVE(int16_t, false);
CHECK_CAN_REPRESENT_NATIVE(int32_t, false);
CHECK_CAN_REPRESENT_NATIVE(int64_t, true);
CHECK_CAN_REPRESENT_NATIVE(uint8_t, false);
CHECK_CAN_REPRESENT_NATIVE(uint16_t, false);
CHECK_CAN_REPRESENT_NATIVE(uint32_t, false);
CHECK_CAN_REPRESENT_NATIVE(uint64_t, false);
CHECK_CAN_REPRESENT_NATIVE(float, false);
CHECK_CAN_REPRESENT_NATIVE(double, true);
#ifdef int128
CHECK_CAN_REPRESENT_NATIVE(int128, true);
CHECK_CAN_REPRESENT_NATIVE(uint128, false);
#endif
#ifdef float16
CHECK_CAN_REPRESENT_NATIVE(float16, false);
#endif
CHECK_CAN_REPRESENT_NATIVE(char*, false);
CHECK_CAN_REPRESENT_NATIVE(char*const, false);
CHECK_CAN_REPRESENT_NATIVE(char[2], false);
CHECK_CAN_REPRESENT_NATIVE(const char[2], false);
CHECK_CAN_REPRESENT_NATIVE(char(&)[2], false);
CHECK_CAN_REPRESENT_NATIVE(const char(&)[2], false);
CHECK_CAN_REPRESENT_NATIVE(char(&&)[2], false);
CHECK_CAN_REPRESENT_NATIVE(const char(&&)[2], false);
CHECK_CAN_REPRESENT_NATIVE(const char*, true);
CHECK_CAN_REPRESENT_NATIVE(const char*const, true);
CHECK_CAN_REPRESENT_NATIVE(std::string, true);
CHECK_CAN_REPRESENT_NATIVE(std::string_view, true);
#ifdef __cpp_lib_char8_t
CHECK_CAN_REPRESENT_NATIVE(char8_t*, false);
CHECK_CAN_REPRESENT_NATIVE(char8_t*const, false);
CHECK_CAN_REPRESENT_NATIVE(char8_t[2], false);
CHECK_CAN_REPRESENT_NATIVE(const char8_t[2], false);
CHECK_CAN_REPRESENT_NATIVE(char8_t(&)[2], false);
CHECK_CAN_REPRESENT_NATIVE(const char8_t(&)[2], false);
CHECK_CAN_REPRESENT_NATIVE(char(&&)[2], false);
CHECK_CAN_REPRESENT_NATIVE(const char8_t(&&)[2], false);
CHECK_CAN_REPRESENT_NATIVE(const char8_t*, true);
CHECK_CAN_REPRESENT_NATIVE(const char8_t*const, true);
CHECK_CAN_REPRESENT_NATIVE(std::u8string, true);
CHECK_CAN_REPRESENT_NATIVE(std::u8string_view, true);
#endif
CHECK_CAN_REPRESENT_NATIVE(wchar_t*, false);
CHECK_CAN_REPRESENT_NATIVE(wchar_t*const, false);
CHECK_CAN_REPRESENT_NATIVE(wchar_t[2], false);
CHECK_CAN_REPRESENT_NATIVE(const wchar_t[2], false);
CHECK_CAN_REPRESENT_NATIVE(wchar_t(&)[2], false);
CHECK_CAN_REPRESENT_NATIVE(const wchar_t(&)[2], false);
CHECK_CAN_REPRESENT_NATIVE(wchar_t(&&)[2], false);
CHECK_CAN_REPRESENT_NATIVE(const wchar_t(&&)[2], false);
CHECK_CAN_REPRESENT_NATIVE(const wchar_t*, false);
CHECK_CAN_REPRESENT_NATIVE(const wchar_t*const, false);
CHECK_CAN_REPRESENT_NATIVE(std::wstring, !!TOML_WINDOWS_COMPAT);
CHECK_CAN_REPRESENT_NATIVE(std::wstring_view, false);
#define CHECK_VALUE_EXACT(T, expected) \
static_assert(is_same_v<decltype(declval<node>().value_exact<T>()), optional<expected>>); \
static_assert(is_same_v<decltype(declval<node_view<node>>().value_exact<T>()), optional<expected>>); \
static_assert(is_same_v<decltype(declval<node_view<const node>>().value_exact<T>()), optional<expected>>)
#define CHECK_VALUE_OR(T, expected) \
static_assert(is_same_v<decltype(declval<node>().value_or(declval<T>())), expected>); \
static_assert(is_same_v<decltype(declval<node_view<node>>().value_or(declval<T>())), expected>); \
static_assert(is_same_v<decltype(declval<node_view<const node>>().value_or(declval<T>())), expected>)
CHECK_VALUE_EXACT( time, time);
CHECK_VALUE_EXACT( date, date);
CHECK_VALUE_EXACT( date_time, date_time);
CHECK_VALUE_EXACT( bool, bool);
CHECK_VALUE_EXACT( double, double);
CHECK_VALUE_EXACT( int64_t, int64_t);
CHECK_VALUE_EXACT( const char*, const char*);
CHECK_VALUE_EXACT( std::string_view, std::string_view);
CHECK_VALUE_EXACT( std::string, std::string);
#ifdef __cpp_lib_char8_t
CHECK_VALUE_EXACT( const char8_t*, const char8_t*);
CHECK_VALUE_EXACT( std::u8string_view, std::u8string_view);
CHECK_VALUE_EXACT( std::u8string, std::u8string);
#endif
CHECK_VALUE_OR( time, time);
CHECK_VALUE_OR( time&, time);
CHECK_VALUE_OR( time&&, time);
CHECK_VALUE_OR( time const, time);
CHECK_VALUE_OR( date, date);
CHECK_VALUE_OR( date&, date);
CHECK_VALUE_OR( date&&, date);
CHECK_VALUE_OR( date const, date);
CHECK_VALUE_OR( date_time, date_time);
CHECK_VALUE_OR( date_time&, date_time);
CHECK_VALUE_OR( date_time&&, date_time);
CHECK_VALUE_OR( date_time const, date_time);
CHECK_VALUE_OR( bool, bool);
CHECK_VALUE_OR( bool&, bool);
CHECK_VALUE_OR( bool&&, bool);
CHECK_VALUE_OR( bool const, bool);
CHECK_VALUE_OR( int32_t, int32_t);
CHECK_VALUE_OR( int32_t&, int32_t);
CHECK_VALUE_OR( int32_t&&, int32_t);
CHECK_VALUE_OR( int32_t const, int32_t);
CHECK_VALUE_OR( int64_t, int64_t);
CHECK_VALUE_OR( int64_t&, int64_t);
CHECK_VALUE_OR( int64_t&&, int64_t);
CHECK_VALUE_OR( int64_t const, int64_t);
#ifdef int128
CHECK_VALUE_OR( int128, int128);
CHECK_VALUE_OR( int128&, int128);
CHECK_VALUE_OR( int128&&, int128);
CHECK_VALUE_OR( int128 const, int128);
CHECK_VALUE_OR( uint128, uint128);
CHECK_VALUE_OR( uint128&, uint128);
CHECK_VALUE_OR( uint128&&, uint128);
CHECK_VALUE_OR( uint128 const, uint128);
#endif
CHECK_VALUE_OR( float, float);
CHECK_VALUE_OR( float&, float);
CHECK_VALUE_OR( float&&, float);
CHECK_VALUE_OR( float const, float);
CHECK_VALUE_OR( double, double);
CHECK_VALUE_OR( double&, double);
CHECK_VALUE_OR( double&&, double);
CHECK_VALUE_OR( double const, double);
CHECK_VALUE_OR( char*, const char*);
CHECK_VALUE_OR( char*&, const char*);
CHECK_VALUE_OR( char*&&, const char*);
CHECK_VALUE_OR( char*const, const char*);
CHECK_VALUE_OR( char[2], const char*);
CHECK_VALUE_OR( char(&)[2], const char*);
CHECK_VALUE_OR( char(&&)[2], const char*);
CHECK_VALUE_OR( const char*, const char*);
CHECK_VALUE_OR( const char*&, const char*);
CHECK_VALUE_OR( const char*&&, const char*);
CHECK_VALUE_OR( const char*const, const char*);
CHECK_VALUE_OR( const char[2], const char*);
CHECK_VALUE_OR( const char(&)[2], const char*);
CHECK_VALUE_OR( const char(&&)[2], const char*);
CHECK_VALUE_OR( std::string_view, std::string_view);
CHECK_VALUE_OR( std::string_view&, std::string_view);
CHECK_VALUE_OR( std::string_view&&, std::string_view);
CHECK_VALUE_OR( const std::string_view, std::string_view);
CHECK_VALUE_OR( const std::string_view&, std::string_view);
CHECK_VALUE_OR( const std::string_view&&, std::string_view);
CHECK_VALUE_OR( std::string, std::string);
CHECK_VALUE_OR( std::string&, std::string);
CHECK_VALUE_OR( std::string&&, std::string);
CHECK_VALUE_OR( const std::string, std::string);
CHECK_VALUE_OR( const std::string&, std::string);
CHECK_VALUE_OR( const std::string&&, std::string);
#ifdef __cpp_lib_char8_t
CHECK_VALUE_OR( char8_t*, const char8_t*);
CHECK_VALUE_OR( char8_t*&, const char8_t*);
CHECK_VALUE_OR( char8_t*&&, const char8_t*);
CHECK_VALUE_OR( char8_t*const, const char8_t*);
CHECK_VALUE_OR( char8_t[2], const char8_t*);
CHECK_VALUE_OR( char8_t(&)[2], const char8_t*);
CHECK_VALUE_OR( char8_t(&&)[2], const char8_t*);
CHECK_VALUE_OR( const char8_t*, const char8_t*);
CHECK_VALUE_OR( const char8_t*&, const char8_t*);
CHECK_VALUE_OR( const char8_t*&&, const char8_t*);
CHECK_VALUE_OR( const char8_t*const, const char8_t*);
CHECK_VALUE_OR( const char8_t[2], const char8_t*);
CHECK_VALUE_OR( const char8_t(&)[2], const char8_t*);
CHECK_VALUE_OR( const char8_t(&&)[2], const char8_t*);
CHECK_VALUE_OR( std::u8string_view, std::u8string_view);
CHECK_VALUE_OR( std::u8string_view&, std::u8string_view);
CHECK_VALUE_OR( std::u8string_view&&, std::u8string_view);
CHECK_VALUE_OR( const std::u8string_view, std::u8string_view);
CHECK_VALUE_OR( const std::u8string_view&, std::u8string_view);
CHECK_VALUE_OR( const std::u8string_view&&, std::u8string_view);
CHECK_VALUE_OR( std::u8string, std::u8string);
CHECK_VALUE_OR( std::u8string&, std::u8string);
CHECK_VALUE_OR( std::u8string&&, std::u8string);
CHECK_VALUE_OR( const std::u8string, std::u8string);
CHECK_VALUE_OR( const std::u8string&, std::u8string);
CHECK_VALUE_OR( const std::u8string&&, std::u8string);
#endif
#if TOML_WINDOWS_COMPAT
CHECK_VALUE_OR( wchar_t*, std::wstring);
CHECK_VALUE_OR( wchar_t*&, std::wstring);
CHECK_VALUE_OR( wchar_t*&&, std::wstring);
CHECK_VALUE_OR( wchar_t*const, std::wstring);
CHECK_VALUE_OR( wchar_t[2], std::wstring);
CHECK_VALUE_OR( wchar_t(&)[2], std::wstring);
CHECK_VALUE_OR( wchar_t(&&)[2], std::wstring);
CHECK_VALUE_OR( const wchar_t*, std::wstring);
CHECK_VALUE_OR( const wchar_t*&, std::wstring);
CHECK_VALUE_OR( const wchar_t*&&, std::wstring);
CHECK_VALUE_OR( const wchar_t*const, std::wstring);
CHECK_VALUE_OR( const wchar_t[2], std::wstring);
CHECK_VALUE_OR( const wchar_t(&)[2], std::wstring);
CHECK_VALUE_OR( const wchar_t(&&)[2], std::wstring);
CHECK_VALUE_OR( std::wstring_view, std::wstring);
CHECK_VALUE_OR( std::wstring_view&, std::wstring);
CHECK_VALUE_OR( std::wstring_view&&, std::wstring);
CHECK_VALUE_OR( const std::wstring_view, std::wstring);
CHECK_VALUE_OR( const std::wstring_view&, std::wstring);
CHECK_VALUE_OR( const std::wstring_view&&, std::wstring);
CHECK_VALUE_OR( std::wstring, std::wstring);
CHECK_VALUE_OR( std::wstring&, std::wstring);
CHECK_VALUE_OR( std::wstring&&, std::wstring);
CHECK_VALUE_OR( const std::wstring, std::wstring);
CHECK_VALUE_OR( const std::wstring&, std::wstring);
CHECK_VALUE_OR( const std::wstring&&, std::wstring);
#endif
static_assert(is_same_v<decltype(declval<node&>().ref<double>()), double&>); static_assert(is_same_v<decltype(declval<node&>().ref<double>()), double&>);
static_assert(is_same_v<decltype(declval<node&&>().ref<double>()), double&&>); static_assert(is_same_v<decltype(declval<node&&>().ref<double>()), double&&>);
static_assert(is_same_v<decltype(declval<const node&>().ref<double>()), const double&>); static_assert(is_same_v<decltype(declval<const node&>().ref<double>()), const double&>);

View File

@ -48,9 +48,3 @@ TEST_CASE("values - printing")
CHECK(print_value(10000000000) == "10000000000"); CHECK(print_value(10000000000) == "10000000000");
CHECK(print_value(100000000000000) == "100000000000000"); CHECK(print_value(100000000000000) == "100000000000000");
} }
static_assert(std::is_same_v<string, decltype(std::declval<node>().value_or(S(""s)))>);
static_assert(std::is_same_v<string_view, decltype(std::declval<node>().value_or(S(""sv)))>);
static_assert(std::is_same_v<string_view, decltype(std::declval<node>().value_or(S("")))>);

View File

@ -24,65 +24,153 @@ test_sources = [
'windows_compat.cpp' 'windows_compat.cpp'
] ]
compiler_supports_char8_strings = compiler.compiles(''' compiler_supports_cpp20 = compiler.links('''
#include <version>
#include <string>
#include <iostream>
int main()
{
std::string s = "kek";
std::cout << s << std::endl;
return 0;
}
''',
name : 'supports C++20',
args : [ '-std=c++2a' ]
)
compiler_supports_char8 = compiler_supports_cpp20 and compiler.links('''
#include <version>
#include <string_view> #include <string_view>
#include <string> #include <string>
#include <type_traits>
using namespace std::string_view_literals; using namespace std::string_view_literals;
#ifndef __cpp_lib_char8_t
#error oh noes
#endif
static_assert(!std::is_same_v<char, char8_t>);
static_assert(!std::is_same_v<std::string, std::u8string>);
std::u8string func() std::u8string func()
{ {
return std::u8string{ u8"this is a test."sv }; return std::u8string{ u8"this is a test."sv };
} }
int main()
{
return 0;
}
''', ''',
name : 'char8 string check', name : 'supports char8_t',
args : [ '-std=c++2a' ] args : [ '-std=c++2a', '-fchar8_t' ]
) )
character_types = ['char', 'char8'] compiler_supports_float16 = compiler.links('''
int main()
{
static_assert(sizeof(_Float16) = 2);
_Float16 f = static_cast<_Float16>(1);
return 0;
}
''',
name : 'supports float16',
args : [ '-mfp16-format=ieee' ]
)
compiler_supports_fast_math = compiler.links('''
#include <cmath>
#include <iostream>
int main()
{
std::cout << std::exp2(2.0) << std::pow(2.0, 3.0) << std::endl;
return 0;
}
''',
name : 'supports fast-math',
args : [ '-ffast-math', '-ffp-contract=fast' ]
)
fast_math_modes = [ false, true ]
char8_modes = [ false, true ]
exception_modes = [ true, false ] exception_modes = [ true, false ]
unreleased_feature_modes = [ true, false ] strict_modes = [ false, true ]
tloptional_modes = [ true, false ] cpp20_modes = [ false, true ]
executables = [] executables = []
counter = 0 counter = 0
foreach character_type : character_types foreach cpp20 : cpp20_modes
if character_type == 'char8' and not compiler_supports_char8_strings foreach strict : strict_modes
if cpp20 and not compiler_supports_cpp20
continue
endif
foreach char8 : char8_modes
if char8 and (not cpp20 or not compiler_supports_char8 or strict)
continue
endif
foreach fast_math : fast_math_modes
if fast_math and not compiler_supports_fast_math
continue continue
endif endif
foreach exceptions : exception_modes foreach exceptions : exception_modes
foreach unreleased_features : unreleased_feature_modes
foreach tloptional : tloptional_modes
if tloptional and not (character_type =='char' and unreleased_features and exceptions) name = ''
continue
endif
name = character_type
overrides = [] overrides = []
args = [] args = []
if character_type =='char8' if compiler_supports_float16
overrides += 'cpp_std=none' args += '-mfp16-format=ieee'
args += '-DTOML_CHAR_8_STRINGS=1'
args += '-std=c++2a'
endif endif
if unreleased_features if cpp20
args += '-DTOML_UNRELEASED_FEATURES=1' name = 'cpp20'
overrides += 'cpp_std=none'
args += '-std=c++2a'
if compiler_supports_char8
args += '-fchar8_t'
endif
else else
name = 'cpp17'
endif
if strict
name = name + '_strict' name = name + '_strict'
args += '-DTOML_UNRELEASED_FEATURES=0' args += '-DTOML_UNRELEASED_FEATURES=0'
else
args += '-DTOML_UNRELEASED_FEATURES=1'
endif endif
if char8
name = name + '_char8'
args += '-DTOML_CHAR_8_STRINGS=1'
endif
if not exceptions if not exceptions
name = name + '_noexcept' name = name + '_noexcept'
overrides += 'cpp_eh=none' overrides += 'cpp_eh=none'
endif endif
if tloptional if fast_math
name = name + '_tlopt' name = name + '_fastmath'
args += '-DUSE_TARTANLLAMA_OPTIONAL=1' if compiler.get_id() == 'gcc' or compiler.get_id() == 'clang'
args += '-ffast-math'
args += '-ffp-contract=fast'
endif
endif endif
args += '-DTEST_BUILD_ID=@0@'.format(counter) if counter % 6 == 3
args += '-DTOML_ALL_INLINE=1'
endif
if counter % 2 == 1
args += '-DUSE_SINGLE_HEADER=1'
endif
if counter % 4 == 2 and exceptions
args += '-DUSE_TARTANLLAMA_OPTIONAL=1'
name = name + '_tlopt'
endif
if compiler.get_id() == 'gcc' if compiler.get_id() == 'gcc'
args += '-Wno-padded' args += '-Wno-padded'
@ -108,10 +196,11 @@ foreach character_type : character_types
counter = counter + 1 counter = counter + 1
endforeach # tloptional_modes endforeach # exceptions
endforeach # unreleased_feature_modes endforeach # fast_math
endforeach # exception_modes endforeach # char8
endforeach # character_type endforeach # strict
endforeach # cpp20
locales = [ locales = [
'C', 'C',
@ -125,8 +214,8 @@ locales = [
'de_DE.utf8' 'de_DE.utf8'
] ]
foreach locale : locales
foreach executable : executables foreach executable : executables
test(locale + '_' + executable[0], executable[1], env : ['LC_ALL=' + locale]) foreach locale : locales
test(executable[0] + ' (' + locale + ')', executable[1], env : ['LC_ALL=' + locale])
endforeach endforeach
endforeach endforeach

View File

@ -200,12 +200,12 @@ TEST_CASE("parsing - inf and nan")
)"sv, )"sv,
[](table&& tbl) [](table&& tbl)
{ {
CHECK(tbl[S("sf1")] == std::numeric_limits<double>::infinity()); CHECK(impl::fpclassify(**tbl[S("sf1")].as<double>()) == impl::fp_class::pos_inf);
CHECK(tbl[S("sf2")] == std::numeric_limits<double>::infinity()); CHECK(impl::fpclassify(**tbl[S("sf2")].as<double>()) == impl::fp_class::pos_inf);
CHECK(tbl[S("sf3")] == -std::numeric_limits<double>::infinity()); CHECK(impl::fpclassify(**tbl[S("sf3")].as<double>()) == impl::fp_class::neg_inf);
CHECK(std::isnan(tbl[S("sf4")].as<double>()->get())); CHECK(impl::fpclassify(**tbl[S("sf4")].as<double>()) == impl::fp_class::nan);
CHECK(std::isnan(tbl[S("sf5")].as<double>()->get())); CHECK(impl::fpclassify(**tbl[S("sf5")].as<double>()) == impl::fp_class::nan);
CHECK(std::isnan(tbl[S("sf6")].as<double>()->get())); CHECK(impl::fpclassify(**tbl[S("sf6")].as<double>()) == impl::fp_class::nan);
} }
); );

View File

@ -7,14 +7,6 @@
// toml++ config // toml++ config
#define TOML_UNDEF_MACROS 0 #define TOML_UNDEF_MACROS 0
#ifdef TEST_BUILD_ID
#if (TEST_BUILD_ID % 3 == 0) && !defined(TOML_ALL_INLINE)
#define TOML_ALL_INLINE 1
#endif
#if (TEST_BUILD_ID % 2 == 0) && !defined(USE_SINGLE_HEADER)
#define USE_SINGLE_HEADER 1
#endif
#endif
#ifndef TOML_ALL_INLINE #ifndef TOML_ALL_INLINE
#define TOML_ALL_INLINE 0 #define TOML_ALL_INLINE 0
#endif #endif
@ -38,6 +30,7 @@
#define CATCH_CONFIG_CONSOLE_WIDTH 120 #define CATCH_CONFIG_CONSOLE_WIDTH 120
#define CATCH_CONFIG_CPP11_TO_STRING #define CATCH_CONFIG_CPP11_TO_STRING
#define CATCH_CONFIG_DISABLE_MATCHERS #define CATCH_CONFIG_DISABLE_MATCHERS
#define CATCH_CONFIG_NO_NOMINMAX
//windows.h config (included transitively by catch2 on windows) //windows.h config (included transitively by catch2 on windows)
#ifdef _WIN32 #ifdef _WIN32
@ -63,7 +56,7 @@
#define NOMENUS // - MF_* #define NOMENUS // - MF_*
#define NOMEMMGR // - GMEM_*, LMEM_*, GHND, LHND, associated routines #define NOMEMMGR // - GMEM_*, LMEM_*, GHND, LHND, associated routines
#define NOMETAFILE // - typedef METAFILEPICT #define NOMETAFILE // - typedef METAFILEPICT
#define NOMINMAX // - Macros min(a,b) and max(a,b) //#define NOMINMAX // - Macros min(a,b) and max(a,b)
#define NOMSG // - typedef MSG and associated routines #define NOMSG // - typedef MSG and associated routines
//#define NONLS // - All NLS defines and routines //#define NONLS // - All NLS defines and routines
#define NOOPENFILE // - OpenFile(), OemToAnsi, AnsiToOem, and OF_* #define NOOPENFILE // - OpenFile(), OemToAnsi, AnsiToOem, and OF_*

View File

@ -2,13 +2,11 @@
#include "settings.h" #include "settings.h"
#if USE_TARTANLLAMA_OPTIONAL #if USE_TARTANLLAMA_OPTIONAL
#if __has_include(<tloptional/include/tl/optional.hpp>) #include "tloptional.h"
#include <tloptional/include/tl/optional.hpp>
#else
#error TartanLlama/optional is missing! You probably need to fetch submodules ("git submodule update --init extern/tloptional")
#endif
#endif #endif
#include "evil_macros.h"
#if USE_SINGLE_HEADER #if USE_SINGLE_HEADER
#include "../toml.hpp" #include "../toml.hpp"
#else #else
@ -43,6 +41,27 @@ TOML_POP_WARNINGS
while (false) while (false)
#endif #endif
[[nodiscard]]
TOML_ATTR(const)
inline double make_infinity(int sign = 1) noexcept
{
constexpr uint64_t pos_inf = 0b0111111111110000000000000000000000000000000000000000000000000000ull;
constexpr uint64_t neg_inf = 0b1111111111110000000000000000000000000000000000000000000000000000ull;
double val;
std::memcpy(&val, sign >= 0 ? &pos_inf : &neg_inf, sizeof(double));
return val;
}
[[nodiscard]]
TOML_ATTR(const)
inline double make_nan() noexcept
{
constexpr uint64_t qnan = 0b0111111111111000000000000000000000000000000000000000000000000000ull;
double val;
std::memcpy(&val, &qnan, sizeof(double));
return val;
}
// function_view - adapted from here: https://vittorioromeo.info/index/blog/passing_functions_to_functions.html // function_view - adapted from here: https://vittorioromeo.info/index/blog/passing_functions_to_functions.html
template <typename Func> template <typename Func>
class function_view; class function_view;
@ -112,7 +131,7 @@ inline bool parse_expected_value(
static constexpr auto is_val = [](char32_t codepoint) static constexpr auto is_val = [](char32_t codepoint)
{ {
if constexpr (std::is_same_v<string, impl::promoted<T>>) if constexpr (impl::node_type_of<T> == node_type::string)
return codepoint == U'"' || codepoint == U'\''; return codepoint == U'"' || codepoint == U'\'';
else else
return !impl::is_whitespace(codepoint); return !impl::is_whitespace(codepoint);
@ -152,7 +171,7 @@ inline bool parse_expected_value(
end.column++; end.column++;
} }
using value_type = impl::promoted<impl::remove_cvref_t<T>>; using value_type = impl::native_type_of<impl::remove_cvref_t<T>>;
value<value_type> val_parsed; value<value_type> val_parsed;
{ {
INFO("["sv << test_file << ", line "sv << test_line << "] "sv << "parse_expected_value: Checking initial parse"sv) INFO("["sv << test_file << ", line "sv << test_line << "] "sv << "parse_expected_value: Checking initial parse"sv)

22
tests/tloptional.h Normal file
View File

@ -0,0 +1,22 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#pragma once
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#endif
#if __has_include(<tloptional/include/tl/optional.hpp>)
#include <tloptional/include/tl/optional.hpp>
#else
#error TartanLlama/optional is missing! You probably need to fetch submodules ("git submodule update --init extern/tloptional")
#endif
#ifdef __clang__
#pragma clang diagnostic pop
#endif

View File

@ -70,8 +70,4 @@ TEST_CASE("windows compat")
REQUIRE(!tbl.contains(L"foo")); REQUIRE(!tbl.contains(L"foo"));
} }
static_assert(std::is_same_v<std::wstring, decltype(std::declval<toml::node>().value_or(L""s))>);
static_assert(std::is_same_v<std::wstring, decltype(std::declval<toml::node>().value_or(L""sv))>);
static_assert(std::is_same_v<std::wstring, decltype(std::declval<toml::node>().value_or(L""))>);
#endif // TOML_WINDOWS_COMPAT #endif // TOML_WINDOWS_COMPAT

1253
toml.hpp

File diff suppressed because it is too large Load Diff

View File

@ -97,8 +97,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" /> <ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -96,8 +96,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -98,8 +98,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -96,8 +96,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -98,8 +98,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -98,8 +98,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -96,8 +96,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -98,8 +98,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -96,8 +96,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -96,8 +96,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -98,8 +98,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -96,8 +96,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -98,8 +98,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -98,8 +98,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -96,8 +96,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -98,8 +98,11 @@
<Natvis Include="toml++.natvis" /> <Natvis Include="toml++.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\tests\catch2.h" />
<ClInclude Include="..\tests\evil_macros.h" />
<ClInclude Include="..\tests\settings.h" /> <ClInclude Include="..\tests\settings.h" />
<ClInclude Include="..\tests\tests.h" /> <ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\tloptional.h" />
<ClInclude Include="..\tests\unicode.h" /> <ClInclude Include="..\tests\unicode.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>