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
*.md 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_options.txt text encoding=UTF-8 eol=lf
Doxyfile text encoding=UTF-8 eol=lf

View File

@ -8,6 +8,14 @@ body
margin-top: 3rem;
}
@media screen and (min-width: 768px) and (max-width: 991px)
{
body
{
margin-top: 6rem;
}
}
a
{
text-decoration: none !important;
@ -22,6 +30,11 @@ header
z-index: 100;
}
header, header *
{
white-space: nowrap;
}
article, article > header, article div > section
{
margin-bottom: 5rem;
@ -65,13 +78,10 @@ pre a.tpp-injected
.tpp-enable-if > a
{
white-space: nowrap;
font-size: 0.8rem;
font-size: 0.7rem;
font-weight: bold;
background-color: #858585;
color: #050505;
padding-bottom: 0px;
margin: 0px 1px;
margin-bottom: 2px;
}
.tpp-enable-if > a:hover
@ -135,7 +145,7 @@ pre.m-code + pre.m-console span
}
/* comments */
.m-code .c1
pre.m-code .c1
{
color: rgb(87,166,74);
}
@ -149,25 +159,31 @@ pre.m-code .mh
}
/* keywords */
.m-code .k
pre.m-code .k
{
color: rgb(86,156,214);
}
pre.m-code .kt,
pre.m-code .k,
pre.m-code .nc
{
font-weight: normal;
}
/* identifier names */
.m-code .n
pre.m-code .n
{
color: rgb(220,220,220);
}
/* punctuators (brackets etc) */
.m-code .p
pre.m-code .p
{
color: rgb(120,120,120);
}
/* preprocessor directives */
.m-code .cp
pre.m-code .cp
{
color: rgb(120,120,120);
}
@ -209,7 +225,6 @@ pre.m-code .mh
margin-right: -0.9rem;
margin-top: -0.5rem;
}
@media screen and (min-width: 992px)
{
.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);
}
/* 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" */
.godbolt
{

View File

@ -5,47 +5,6 @@
#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>
inline void init_utf8_console() noexcept

View File

@ -103,6 +103,10 @@
#undef TOML_EVAL_BOOL_1
#undef TOML_EVAL_BOOL_0
#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
//# {{

View File

@ -174,7 +174,7 @@ namespace toml::impl
TOML_ALWAYS_INLINE
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>)
{
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."
);
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"
);
#if TOML_WINDOWS_COMPAT
if constexpr (is_wide_string<T>)
{
#if TOML_WINDOWS_COMPAT
return new value{ narrow(std::forward<T>(val)) };
#else
static_assert(dependent_false<T>, "Evaluated unreachable branch!");
#endif
}
else
#endif
return new value{ std::forward<T>(val) };
}
}
@ -613,13 +617,14 @@ namespace toml
template <typename U, typename... V>
iterator emplace(const_iterator pos, V&&... args) noexcept
{
using type = impl::unwrapped<U>;
using type = impl::unwrap_node<U>;
static_assert(
impl::is_value_or_node<type>,
"Emplacement type parameter must be one of the basic value types, a toml::table, or a toml::array"
impl::is_native<type> || impl::is_one_of<type, table, 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.
@ -774,13 +779,14 @@ namespace toml
template <typename U, typename... V>
decltype(auto) emplace_back(V&&... args) noexcept
{
using type = impl::unwrapped<U>;
using type = impl::unwrap_node<U>;
static_assert(
impl::is_value_or_node<type>,
"Emplacement type parameter must be one of the basic value types, a toml::table, or a toml::array"
impl::is_native<type> || impl::is_one_of<type, table, 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);
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.
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))
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.
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))
return val->as<T>();
@ -879,9 +885,9 @@ namespace toml
template <typename T>
[[nodiscard]] static bool container_equality(const array& lhs, const T& rhs) noexcept
{
using elem_t = std::remove_const_t<typename T::value_type>;
using element_type = std::remove_const_t<typename T::value_type>;
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"
);
@ -893,7 +899,7 @@ namespace toml
size_t i{};
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)
return false;
}

View File

@ -16,6 +16,9 @@ TOML_DISABLE_ALL_WARNINGS
#endif
#include <cstdint>
#include <cstring> //memcpy, memset
#include <cfloat>
#include <climits>
#include <limits>
#include <memory>
#include <string_view>
#include <string>
@ -38,6 +41,17 @@ TOML_POP_WARNINGS
#define TOML_LAUNDER(x) x
#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
TOML_PUSH_WARNINGS
@ -287,9 +301,9 @@ namespace toml
/// \brief The end of the region (exclusive).
source_position end;
/// \brief The path to the corresponding source document.
///
/// \remarks This will be `nullptr` if no path was provided to toml::parse().
/// \brief The path to the corresponding source document.
///
/// \remarks This will be `nullptr` if no path was provided to toml::parse().
source_path_ptr path;
#if TOML_WINDOWS_COMPAT
@ -338,6 +352,27 @@ namespace toml::impl
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??"
// 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.
@ -368,16 +403,217 @@ namespace toml::impl
TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS
template <typename T>
inline constexpr bool is_value =
std::is_same_v<T, string>
|| std::is_same_v<T, int64_t>
|| std::is_same_v<T, double>
|| std::is_same_v<T, bool>
|| std::is_same_v<T, date>
|| std::is_same_v<T, time>
|| std::is_same_v<T, date_time>;
// general value traits
// (as they relate to their equivalent native TOML type)
template <typename T>
struct value_traits
{
using native_type = void;
static constexpr bool is_native = false;
static constexpr bool is_losslessly_convertible_to_native = false;
static constexpr bool can_represent_native = false;
static constexpr bool can_partially_represent_native = false;
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>
inline constexpr bool is_wide_string = is_one_of<
std::decay_t<T>,
@ -387,103 +623,40 @@ namespace toml::impl
std::wstring
>;
template <typename T>
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
;
// native value category queries
template <typename T>
inline constexpr bool is_value_or_node =
is_value<T>
|| std::is_same_v<T, array>
|| std::is_same_v<T, table>;
template <typename T> struct node_wrapper { using type = T; };
template <> struct node_wrapper<string> { using type = value<string>; };
template <> struct node_wrapper<int64_t> { using type = value<int64_t>; };
template <> struct node_wrapper<double> { using type = value<double>; };
template <> struct node_wrapper<bool> { using type = value<bool>; };
template <> struct node_wrapper<date> { using type = value<date>; };
template <> struct node_wrapper<time> { using type = value<time>; };
template <> struct node_wrapper<date_time> { using type = value<date_time>; };
using native_type_of = typename value_traits<T>::native_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<value<T>> { using type = T; };
template <typename T> using unwrapped = typename impl::node_unwrapper<T>::type;
template <typename T> struct value_promoter { using type = T; };
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 <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; };
inline constexpr bool is_native = value_traits<T>::is_native;
template <typename T>
inline constexpr auto node_type_of = node_type_of_<promoted<typename node_unwrapper<remove_cvref_t<T>>::type>>::value;
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 <> struct node_wrapper<string> { using type = value<string>; };
template <> struct node_wrapper<int64_t> { using type = value<int64_t>; };
template <> struct node_wrapper<double> { using type = value<double>; };
template <> struct node_wrapper<bool> { using type = value<bool>; };
template <> struct node_wrapper<date> { using type = value<date>; };
template <> struct node_wrapper<time> { using type = value<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> struct node_unwrapper { 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> 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 <> struct node_type_getter<array> { static constexpr auto value = node_type::array; };
template <typename T> inline constexpr node_type node_type_of = node_type_getter<unwrap_node<remove_cvref_t<T>>>::value;
inline constexpr std::string_view low_character_escape_table[] =
{
"\\u0000"sv,
@ -534,26 +707,28 @@ namespace toml::impl
"date-time"sv
};
#define TOML_P2S_DECL(Linkage, Type) \
#define TOML_P2S_DECL(Type) \
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(TOML_ALWAYS_INLINE, int16_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, int32_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, int64_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, uint8_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, uint16_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, uint32_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, uint64_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, float);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, double);
TOML_P2S_DECL(inline, const date&);
TOML_P2S_DECL(inline, const time&);
TOML_P2S_DECL(inline, time_offset);
TOML_P2S_DECL(inline, const date_time&);
TOML_P2S_DECL(int8_t);
TOML_P2S_DECL(int16_t);
TOML_P2S_DECL(int32_t);
TOML_P2S_DECL(int64_t);
TOML_P2S_DECL(uint8_t);
TOML_P2S_DECL(uint16_t);
TOML_P2S_DECL(uint32_t);
TOML_P2S_DECL(uint64_t);
TOML_P2S_DECL(float);
TOML_P2S_DECL(const date&);
TOML_P2S_DECL(const time&);
TOML_P2S_DECL(time_offset);
TOML_P2S_DECL(const date_time&);
#undef TOML_P2S_DECL
template <typename T>
inline constexpr bool dependent_false = false;
}
namespace toml
@ -566,28 +741,28 @@ namespace toml
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>.
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>.
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>.
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`.
template <typename T>
inline constexpr bool is_number = is_integer<T> || is_floating_point<T>;
/// \brief Metafunction for determining if a type is a bool toml::value<bool>.
template <typename T>
inline constexpr bool is_boolean = std::is_same_v<impl::node_of<impl::remove_cvref_t<T>>, value<bool>>;
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>.
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>.
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>.
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.
///

View File

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

View File

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

View File

@ -9,6 +9,41 @@
TOML_PUSH_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
{
/// \brief A TOML node.
@ -27,24 +62,27 @@ namespace toml
node& operator= (node&& rhs) noexcept;
template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE
impl::node_of<T>& ref_cast() & noexcept
[[nodiscard]]
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>
[[nodiscard]] TOML_ALWAYS_INLINE
impl::node_of<T>&& ref_cast() && noexcept
[[nodiscard]]
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>
[[nodiscard]] TOML_ALWAYS_INLINE
const impl::node_of<T>& ref_cast() const & noexcept
[[nodiscard]]
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>
@ -93,13 +131,14 @@ namespace toml
///
/// \returns Returns true if this node is an instance of the specified type.
template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE
[[nodiscard]]
bool is() const noexcept
{
using type = impl::unwrapped<impl::remove_cvref_t<T>>;
using type = impl::unwrap_node<T>;
static_assert(
impl::is_value_or_node<type>,
"Template type parameter must be one of the TOML value types, a toml::table, or a toml::array"
impl::is_native<type> || impl::is_one_of<type, table, 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();
@ -142,6 +181,19 @@ namespace toml
[[nodiscard]] virtual const toml::value<time>* as_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.
///
/// \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.
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.
///
@ -202,7 +255,8 @@ namespace toml
///
/// \see node::value()
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.
///
@ -226,13 +280,14 @@ namespace toml
///
/// \returns A pointer to the node as the given type, or nullptr if it was a different type.
template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE
impl::node_of<T>* as() noexcept
[[nodiscard]]
impl::wrap_node<T>* as() noexcept
{
using type = impl::unwrapped<T>;
using type = impl::unwrap_node<T>;
static_assert(
impl::is_value_or_node<type>,
"Template type parameter must be one of the TOML value types, a toml::table, or a toml::array"
impl::is_native<type> || impl::is_one_of<type, table, 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();
@ -248,13 +303,14 @@ namespace toml
/// \brief Gets a pointer to the node as a more specific node type (const overload).
template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE
const impl::node_of<T>* as() const noexcept
[[nodiscard]]
const impl::wrap_node<T>* as() const noexcept
{
using type = impl::unwrapped<T>;
using type = impl::unwrap_node<T>;
static_assert(
impl::is_value_or_node<type>,
"Template type parameter must be one of the TOML value types, a toml::table, or a toml::array"
impl::is_native<type> || impl::is_one_of<type, table, 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();
@ -424,18 +480,20 @@ namespace toml
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(
impl::is_value_or_node<type>,
"Template type parameter must be one of the TOML value types, a toml::table, or a toml::array"
impl::is_native<type> || impl::is_one_of<type, table, array>,
"The template type argument of node::ref() must be one of the following:"
TOML_UNWRAPPED_NODE_TYPE_LIST
);
TOML_ASSERT(
n.template is<T>()
&& "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();
else
return std::forward<N>(n).template ref_cast<type>();
@ -511,21 +569,24 @@ namespace toml
///
/// \returns A reference to the underlying data.
template <typename T>
[[nodiscard]] impl::unwrapped<T>& ref() & noexcept
[[nodiscard]]
impl::unwrap_node<T>& ref() & noexcept
{
return do_ref<T>(*this);
}
/// \brief Gets a raw reference to a value node's underlying data (rvalue overload).
template <typename T>
[[nodiscard]] impl::unwrapped<T>&& ref() && noexcept
[[nodiscard]]
impl::unwrap_node<T>&& ref() && noexcept
{
return do_ref<T>(std::move(*this));
}
/// \brief Gets a raw reference to a value node's underlying data (const lvalue overload).
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);
}

View File

@ -147,12 +147,14 @@ namespace toml
[[nodiscard]]
auto as() const noexcept
{
using type = impl::unwrap_node<U>;
static_assert(
impl::is_value_or_node<impl::unwrapped<U>>,
"Template type parameter must be one of the basic value types, a toml::table, or a toml::array"
impl::is_native<type> || impl::is_one_of<type, table, 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.
@ -174,6 +176,15 @@ namespace toml
/// \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>(); }
template <typename U>
[[nodiscard]]
optional<U> value_exact() const noexcept
{
if (node_)
return node_->template value_exact<U>();
return {};
}
TOML_PUSH_WARNINGS
TOML_DISABLE_INIT_WARNINGS
@ -185,7 +196,8 @@ namespace toml
///
/// \see node::value()
template <typename U>
[[nodiscard]] optional<U> value() const noexcept
[[nodiscard]]
optional<U> value() const noexcept
{
if (node_)
return node_->template value<U>();
@ -205,12 +217,45 @@ namespace toml
///
/// \see node::value_or()
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)));
if (node_)
return node_->value_or(std::forward<U>(default_value));
return return_type{ 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_)
return node_->value_or(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.
@ -252,10 +297,11 @@ namespace toml
template <typename U>
[[nodiscard]] decltype(auto) ref() const noexcept
{
using type = impl::unwrapped<U>;
using type = impl::unwrap_node<U>;
static_assert(
impl::is_value_or_node<type>,
"Template type parameter must be one of the TOML value types, a toml::table, or a toml::array"
impl::is_native<type> || impl::is_one_of<type, table, array>,
"Template type parameter must be one of the following:"
TOML_UNWRAPPED_NODE_TYPE_LIST
);
TOML_ASSERT(
node_
@ -296,7 +342,10 @@ namespace toml
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.
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
{
static_assert(
@ -304,20 +353,27 @@ namespace toml
"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>)
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;
}
}
TOML_ASYMMETRICAL_EQUALITY_OPS(
const node_view&,
const U&,
template <typename U, typename = std::enable_if_t<impl::is_value_or_promotable<U>>>
template <typename U, 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.

View File

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

View File

@ -34,8 +34,7 @@ namespace toml::impl
// - Strings in C++. Honestly.
template <typename Char1, typename Char2>
TOML_ALWAYS_INLINE
void print_to_stream(std::basic_string_view<Char1> str, std::basic_ostream<Char2>& stream)
inline void print_to_stream(std::basic_string_view<Char1> str, std::basic_ostream<Char2>& stream)
{
static_assert(sizeof(Char1) == 1);
static_assert(sizeof(Char2) == 1);
@ -43,8 +42,7 @@ namespace toml::impl
}
template <typename Char1, typename Char2>
TOML_ALWAYS_INLINE
void print_to_stream(const std::basic_string<Char1>& str, std::basic_ostream<Char2>& stream)
inline void print_to_stream(const std::basic_string<Char1>& str, std::basic_ostream<Char2>& stream)
{
static_assert(sizeof(Char1) == 1);
static_assert(sizeof(Char2) == 1);
@ -52,8 +50,7 @@ namespace toml::impl
}
template <typename Char>
TOML_ALWAYS_INLINE
void print_to_stream(char character, std::basic_ostream<Char>& stream)
inline void print_to_stream(char character, std::basic_ostream<Char>& stream)
{
static_assert(sizeof(Char) == 1);
stream.put(static_cast<Char>(character));
@ -61,8 +58,7 @@ namespace toml::impl
template <typename Char>
TOML_ATTR(nonnull)
TOML_ALWAYS_INLINE
void print_to_stream(const char* str, size_t len, std::basic_ostream<Char>& stream)
inline void print_to_stream(const char* str, size_t len, std::basic_ostream<Char>& stream)
{
static_assert(sizeof(Char) == 1);
stream.write(reinterpret_cast<const Char*>(str), static_cast<std::streamsize>(len));
@ -71,8 +67,7 @@ namespace toml::impl
#ifdef __cpp_lib_char8_t
template <typename Char>
TOML_ALWAYS_INLINE
void print_to_stream(char8_t character, std::basic_ostream<Char>& stream)
inline void print_to_stream(char8_t character, std::basic_ostream<Char>& stream)
{
static_assert(sizeof(Char) == 1);
stream.put(static_cast<Char>(character));
@ -80,8 +75,7 @@ namespace toml::impl
template <typename Char>
TOML_ATTR(nonnull)
TOML_ALWAYS_INLINE
void print_to_stream(const char8_t* str, size_t len, std::basic_ostream<Char>& stream)
inline void print_to_stream(const char8_t* str, size_t len, std::basic_ostream<Char>& stream)
{
static_assert(sizeof(Char) == 1);
stream.write(reinterpret_cast<const Char*>(str), static_cast<std::streamsize>(len));
@ -128,13 +122,12 @@ namespace toml::impl
#endif
}
#define TOML_P2S_OVERLOAD(Type) \
template <typename Char> \
TOML_ALWAYS_INLINE \
void print_to_stream(Type val, std::basic_ostream<Char>& stream) \
{ \
static_assert(sizeof(Char) == 1); \
print_integer_to_stream(val, stream); \
#define TOML_P2S_OVERLOAD(Type) \
template <typename Char> \
inline void print_to_stream(Type val, std::basic_ostream<Char>& stream) \
{ \
static_assert(sizeof(Char) == 1); \
print_integer_to_stream(val, stream); \
}
TOML_P2S_OVERLOAD(int8_t)
@ -157,19 +150,21 @@ namespace toml::impl
"The stream's underlying character type must be 1 byte in size."
);
switch (std::fpclassify(val))
switch (impl::fpclassify(val))
{
case FP_INFINITE:
if (val < T{})
print_to_stream('-', stream);
case fp_class::neg_inf:
print_to_stream("-inf"sv, stream);
break;
case fp_class::pos_inf:
print_to_stream("inf"sv, stream);
return;
break;
case FP_NAN:
case fp_class::nan:
print_to_stream("nan"sv, stream);
return;
break;
default:
case fp_class::ok:
{
static constexpr auto needs_decimal_point = [](auto&& s) noexcept
{
@ -204,32 +199,32 @@ namespace toml::impl
print_to_stream(".0"sv, stream);
}
#endif
break;
}
TOML_NO_DEFAULT_CASE;
}
}
#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);
#endif
#define TOML_P2S_OVERLOAD(Type) \
template <typename Char> \
TOML_ALWAYS_INLINE \
void print_to_stream(Type val, std::basic_ostream<Char>& stream) \
inline void print_to_stream(Type val, std::basic_ostream<Char>& stream) \
{ \
static_assert(sizeof(Char) == 1); \
print_floating_point_to_stream(val, stream); \
}
TOML_P2S_OVERLOAD(float)
TOML_P2S_OVERLOAD(double)
#undef TOML_P2S_OVERLOAD
template <typename Char>
TOML_ALWAYS_INLINE
void print_to_stream(bool val, std::basic_ostream<Char>& stream)
inline void print_to_stream(bool val, std::basic_ostream<Char>& stream)
{
static_assert(sizeof(Char) == 1);
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."
);
#if TOML_WINDOWS_COMPAT
if constexpr (impl::is_wide_string<K>)
{
#if TOML_WINDOWS_COMPAT
return insert(impl::narrow(std::forward<K>(key)), std::forward<V>(val));
#else
static_assert(impl::dependent_false<K>, "Evaluated unreachable branch!");
#endif
}
else
#endif
{
auto ipos = values.lower_bound(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."
);
#if TOML_WINDOWS_COMPAT
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));
#else
static_assert(impl::dependent_false<K>, "Evaluated unreachable branch!");
#endif
}
else
#endif
{
auto ipos = values.lower_bound(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."
);
#if TOML_WINDOWS_COMPAT
if constexpr (impl::is_wide_string<K>)
{
#if TOML_WINDOWS_COMPAT
return emplace<U>(impl::narrow(std::forward<K>(key)), std::forward<V>(args)...);
#else
static_assert(impl::dependent_false<U>, "Evaluated unreachable branch!");
#endif
}
else
#endif
{
using type = impl::unwrapped<U>;
using type = impl::unwrap_node<U>;
static_assert(
impl::is_value_or_node<type>,
"Emplacement type parameter must be one of the basic value types, a toml::table, or a toml::array"
impl::is_native<type> || impl::is_one_of<type, table, 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);
@ -645,7 +658,7 @@ namespace toml
ipos = values.emplace_hint(
ipos,
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 };
}
@ -782,11 +795,15 @@ namespace toml
"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 TOML_WINDOWS_COMPAT
return do_get(vals, impl::narrow(key));
#else
static_assert(impl::dependent_false<Key>, "Evaluated unreachable branch!");
#endif
}
else
#endif
{
if (auto it = vals.find(key); it != vals.end())
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.
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);
}
@ -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.
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);
}
@ -955,7 +972,7 @@ namespace toml
///
/// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled.
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));
}
@ -969,7 +986,7 @@ namespace toml
///
/// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled.
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));
}

View File

@ -20,25 +20,27 @@ namespace toml
///
/// \tparam T The value's data type. Can be one of:
/// - toml::string
/// - int64_t
/// - double
/// - bool
/// - toml::date
/// - toml::time
/// - toml::date_time
/// - int64_t
/// - double
/// - bool
template <typename T>
class TOML_API value final : public node
{
static_assert(
impl::is_value<T>,
"Template type parameter must be one of the TOML value types"
impl::is_native<T>,
"A toml::value<> must model one of the native TOML value types:"
TOML_NATIVE_VALUE_TYPE_LIST
);
private:
friend class TOML_PARSER_TYPENAME;
template <typename U, typename V>
[[nodiscard]] TOML_ALWAYS_INLINE
[[nodiscard]]
TOML_ALWAYS_INLINE
static auto as_value([[maybe_unused]] V* ptr) noexcept
{
if constexpr (std::is_same_v<T, U>)
@ -194,21 +196,40 @@ namespace toml
}
/// \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>)
{
using namespace ::toml::impl;
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_NAN, FP_NAN): return true;
default: return lhs.val_ == rhs;
case pack(fp_class::pos_inf, fp_class::neg_inf): [[fallthrough]];
case pack(fp_class::pos_inf, fp_class::nan): [[fallthrough]];
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
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.
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>)
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.
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);
}
@ -269,7 +292,8 @@ namespace toml
/// \returns <strong><em>Same value types:</em></strong> `lhs.get() < rhs.get()` <br>
/// <strong><em>Different value types:</em></strong> `lhs.type() < rhs.type()`
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>)
return lhs.val_ < rhs.val_;
@ -285,7 +309,8 @@ namespace toml
/// \returns <strong><em>Same value types:</em></strong> `lhs.get() <= rhs.get()` <br>
/// <strong><em>Different value types:</em></strong> `lhs.type() <= rhs.type()`
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>)
return lhs.val_ <= rhs.val_;
@ -301,7 +326,8 @@ namespace toml
/// \returns <strong><em>Same value types:</em></strong> `lhs.get() > rhs.get()` <br>
/// <strong><em>Different value types:</em></strong> `lhs.type() > rhs.type()`
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>)
return lhs.val_ > rhs.val_;
@ -317,7 +343,8 @@ namespace toml
/// \returns <strong><em>Same value types:</em></strong> `lhs.get() >= rhs.get()` <br>
/// <strong><em>Different value types:</em></strong> `lhs.type() >= rhs.type()`
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>)
return lhs.val_ >= rhs.val_;
@ -338,129 +365,266 @@ namespace toml
extern template class TOML_API value<date_time>;
TOML_POP_WARNINGS
#endif
template <size_t N> value(const string_char(&)[N]) -> value<string>;
template <size_t N> value(const string_char(&&)[N]) -> value<string>;
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
template <typename T>
value(T) -> value<impl::native_type_of<impl::remove_cvref_t<T>>>;
TOML_PUSH_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>
inline optional<T> node::value() const noexcept
{
using namespace ::toml::impl;
static_assert(
!impl::is_wide_string<T> || TOML_WINDOWS_COMPAT,
"Retrieving values as wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled."
!is_wide_string<T> || TOML_WINDOWS_COMPAT,
"Retrieving values as wide-character strings with node::value() is only "
"supported on Windows with TOML_WINDOWS_COMPAT enabled."
);
static_assert(
impl::is_value<T>
|| std::is_same_v<T, string_view>
|| (TOML_WINDOWS_COMPAT && std::is_same_v<T, std::wstring>),
"Value type must be one of the TOML value types (or string_view)"
is_native<T> || can_represent_native<T> || can_partially_represent_native<T>,
"The return type of node::value() 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| 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
);
switch (type())
// 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>)
{
case node_type::none: [[fallthrough]];
case node_type::table: [[fallthrough]];
case node_type::array:
if (type() == node_type_of<T>)
return { this->get_value_exact<T>() };
else
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:
{
if constexpr (std::is_same_v<T, int64_t>)
return ref_cast<int64_t>().get();
else if constexpr (std::is_same_v<T, double>)
return static_cast<double>(ref_cast<int64_t>().get());
else
return {};
}
case node_type::floating_point:
{
if constexpr (std::is_same_v<T, double>)
return ref_cast<double>().get();
else if constexpr (std::is_same_v<T, int64_t>)
return static_cast<int64_t>(ref_cast<double>().get());
else
return {};
}
case node_type::boolean:
{
if constexpr (std::is_same_v<T, bool>)
return ref_cast<bool>().get();
else
return {};
}
case node_type::date:
{
if constexpr (std::is_same_v<T, date>)
return ref_cast<date>().get();
else
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;
// everything else requires a bit of logicking.
else
{
switch (type())
{
// int -> *
case node_type::integer:
{
// int -> int
if constexpr (is_natively_one_of<T, int64_t>)
{
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
return {};
}
// float -> *
case node_type::floating_point:
{
// float -> float
if constexpr (is_natively_one_of<T, double>)
{
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
return {};
}
// bool -> *
case node_type::boolean:
{
// bool -> bool
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
return {};
}
}
// non-values, or 'exact' types covered above
return {};
}
}
TOML_POP_WARNINGS
@ -468,35 +632,95 @@ namespace toml
template <typename T>
inline auto node::value_or(T&& default_value) const noexcept
{
using namespace ::toml::impl;
static_assert(
!impl::is_wide_string<T> || TOML_WINDOWS_COMPAT,
"Retrieving values as wide-character strings 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"
!is_wide_string<T> || TOML_WINDOWS_COMPAT,
"Retrieving values as wide-character strings with node::value_or() is only "
"supported on Windows with TOML_WINDOWS_COMPAT enabled."
);
#if TOML_WINDOWS_COMPAT
if constexpr (impl::is_wide_string<T>)
if constexpr (is_wide_string<T>)
{
if (this->type() == node_type::string)
return impl::widen(ref_cast<string>().get());
#if TOML_WINDOWS_COMPAT
if (type() == node_type::string)
return widen(*ref_cast<string>());
return std::wstring{ std::forward<T>(default_value) };
#else
static_assert(dependent_false<T>, "Evaluated unreachable branch!");
#endif
}
else
#endif
{
using value_type = impl::promoted<impl::remove_cvref_t<T>>;
using return_type = std::conditional_t<
std::is_same_v<value_type, string>,
std::conditional_t<std::is_same_v<impl::remove_cvref_t<T>, string>, string, string_view>,
value_type
using value_type = std::conditional_t<
std::is_pointer_v<std::decay_t<T>>,
std::add_pointer_t<std::add_const_t<std::remove_pointer_t<std::decay_t<T>>>>,
std::decay_t<T>
>;
using traits = value_traits<value_type>;
if (auto val = this->value<return_type>())
return *val;
return return_type{ std::forward<T>(default_value) };
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;
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
#define TOML_LIB_MAJOR 1
#define TOML_LIB_MINOR 3
#define TOML_LIB_PATCH 4
#define TOML_LIB_MINOR 4
#define TOML_LIB_PATCH 0
#define TOML_LANG_MAJOR 1
#define TOML_LANG_MINOR 0

View File

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

View File

@ -35,9 +35,9 @@ def python_value_to_tomlpp(val):
return 'true' if val else 'false'
elif isinstance(val, float):
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):
return 'std::numeric_limits<double>::quiet_NaN()'
return 'make_nan()'
else:
return str(val)
elif isinstance(val, int):

View File

@ -65,7 +65,7 @@ type_names = [
'byte',
'optional',
#------ toml++ types
'node',
'node',
'table',
'array',
'value',
@ -155,7 +155,6 @@ class HTMLDocument(object):
def html_find_parent(tag, names, cutoff=None):
if not utils.is_collection(names):
names = [ names ]
@ -861,13 +860,10 @@ class ExtDocLinksFix(object):
def __call__(self, file, doc):
changed = False
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)
strings = []
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 string in strings:
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()):
new_tag.insert_before(' ')
changed = True
return changed
@ -1009,11 +1004,6 @@ def postprocess_file(dir, file, fixes):
def preprocess_xml(xml_dir):
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):
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):

View File

@ -6,8 +6,17 @@
#pragma once
#include "settings.h"
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#endif
#if __has_include(<Catch2/single_include/catch2/catch.hpp>)
#include <Catch2/single_include/catch2/catch.hpp>
#else
#error Catch2 is missing! You probably need to fetch submodules ("git submodule update --init extern/Catch2")
#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)
{
auto expected = toml::table{{
{ S(R"(sf1)"sv), std::numeric_limits<double>::infinity() },
{ S(R"(sf1)"sv), make_infinity() },
}};
REQUIRE(tbl == expected);
});
@ -752,7 +752,7 @@ TEST_CASE("conformance - iarna/valid")
parsing_should_succeed(FILE_LINE_ARGS, spec_float_11, [](toml::table&& tbl)
{
auto expected = toml::table{{
{ S(R"(sf2)"sv), std::numeric_limits<double>::infinity() },
{ S(R"(sf2)"sv), make_infinity() },
}};
REQUIRE(tbl == expected);
});
@ -760,7 +760,7 @@ TEST_CASE("conformance - iarna/valid")
parsing_should_succeed(FILE_LINE_ARGS, spec_float_12, [](toml::table&& tbl)
{
auto expected = toml::table{{
{ S(R"(sf2)"sv), -std::numeric_limits<double>::infinity() },
{ S(R"(sf2)"sv), make_infinity(-1) },
}};
REQUIRE(tbl == expected);
});
@ -768,7 +768,7 @@ TEST_CASE("conformance - iarna/valid")
parsing_should_succeed(FILE_LINE_ARGS, spec_float_13, [](toml::table&& tbl)
{
auto expected = toml::table{{
{ S(R"(sf4)"sv), std::numeric_limits<double>::quiet_NaN() },
{ S(R"(sf4)"sv), make_nan() },
}};
REQUIRE(tbl == expected);
});
@ -776,7 +776,7 @@ TEST_CASE("conformance - iarna/valid")
parsing_should_succeed(FILE_LINE_ARGS, spec_float_14, [](toml::table&& tbl)
{
auto expected = toml::table{{
{ S(R"(sf5)"sv), std::numeric_limits<double>::quiet_NaN() },
{ S(R"(sf5)"sv), make_nan() },
}};
REQUIRE(tbl == expected);
});
@ -784,7 +784,7 @@ TEST_CASE("conformance - iarna/valid")
parsing_should_succeed(FILE_LINE_ARGS, spec_float_15, [](toml::table&& tbl)
{
auto expected = toml::table{{
{ S(R"(sf6)"sv), std::numeric_limits<double>::quiet_NaN() },
{ S(R"(sf6)"sv), make_nan() },
}};
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
#if USE_TARTANLLAMA_OPTIONAL
#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
#include "tloptional.h"
#endif
#if USE_SINGLE_HEADER
@ -30,11 +26,234 @@
#error TOML_WINDOWS_COMPAT does not match _WIN32 (default behaviour should be to match)
#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
{
using std::declval;
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<const node&>().ref<double>()), const double&>);

View File

@ -48,9 +48,3 @@ TEST_CASE("values - printing")
CHECK(print_value(10000000000) == "10000000000");
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,94 +24,183 @@ test_sources = [
'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>
#include <type_traits>
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()
{
return std::u8string{ u8"this is a test."sv };
}
int main()
{
return 0;
}
''',
name : 'char8 string check',
args : [ '-std=c++2a' ]
name : 'supports char8_t',
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 ]
unreleased_feature_modes = [ true, false ]
tloptional_modes = [ true, false ]
strict_modes = [ false, true ]
cpp20_modes = [ false, true ]
executables = []
counter = 0
foreach character_type : character_types
if character_type == 'char8' and not compiler_supports_char8_strings
continue
endif
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)
foreach cpp20 : cpp20_modes
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
endif
foreach exceptions : exception_modes
name = character_type
overrides = []
args = []
name = ''
overrides = []
args = []
if character_type =='char8'
overrides += 'cpp_std=none'
args += '-DTOML_CHAR_8_STRINGS=1'
args += '-std=c++2a'
endif
if compiler_supports_float16
args += '-mfp16-format=ieee'
endif
if unreleased_features
args += '-DTOML_UNRELEASED_FEATURES=1'
else
name = name + '_strict'
args += '-DTOML_UNRELEASED_FEATURES=0'
endif
if cpp20
name = 'cpp20'
overrides += 'cpp_std=none'
args += '-std=c++2a'
if not exceptions
name = name + '_noexcept'
overrides += 'cpp_eh=none'
endif
if compiler_supports_char8
args += '-fchar8_t'
endif
else
name = 'cpp17'
endif
if tloptional
name = name + '_tlopt'
args += '-DUSE_TARTANLLAMA_OPTIONAL=1'
endif
if strict
name = name + '_strict'
args += '-DTOML_UNRELEASED_FEATURES=0'
else
args += '-DTOML_UNRELEASED_FEATURES=1'
endif
args += '-DTEST_BUILD_ID=@0@'.format(counter)
if char8
name = name + '_char8'
args += '-DTOML_CHAR_8_STRINGS=1'
endif
if compiler.get_id() == 'gcc'
args += '-Wno-padded'
args += '-Wno-float-equal'
endif
if compiler.get_id() == 'clang'
args += '-Wno-padded'
args += '-Wno-float-equal'
args += '-Wno-double-promotion'
endif
if not exceptions
name = name + '_noexcept'
overrides += 'cpp_eh=none'
endif
executables += [[
name,
executable(
name,
test_sources,
include_directories : inc,
cpp_args : args,
override_options : overrides
)
]]
if fast_math
name = name + '_fastmath'
if compiler.get_id() == 'gcc' or compiler.get_id() == 'clang'
args += '-ffast-math'
args += '-ffp-contract=fast'
endif
endif
counter = counter + 1
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
endforeach # tloptional_modes
endforeach # unreleased_feature_modes
endforeach # exception_modes
endforeach # character_type
if compiler.get_id() == 'gcc'
args += '-Wno-padded'
args += '-Wno-float-equal'
endif
if compiler.get_id() == 'clang'
args += '-Wno-padded'
args += '-Wno-float-equal'
args += '-Wno-double-promotion'
endif
executables += [[
name,
executable(
name,
test_sources,
include_directories : inc,
cpp_args : args,
override_options : overrides
)
]]
counter = counter + 1
endforeach # exceptions
endforeach # fast_math
endforeach # char8
endforeach # strict
endforeach # cpp20
locales = [
'C',
@ -125,8 +214,8 @@ locales = [
'de_DE.utf8'
]
foreach locale : locales
foreach executable : executables
test(locale + '_' + executable[0], executable[1], env : ['LC_ALL=' + locale])
foreach executable : executables
foreach locale : locales
test(executable[0] + ' (' + locale + ')', executable[1], env : ['LC_ALL=' + locale])
endforeach
endforeach

View File

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

View File

@ -7,14 +7,6 @@
// toml++ config
#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
#define TOML_ALL_INLINE 0
#endif
@ -38,6 +30,7 @@
#define CATCH_CONFIG_CONSOLE_WIDTH 120
#define CATCH_CONFIG_CPP11_TO_STRING
#define CATCH_CONFIG_DISABLE_MATCHERS
#define CATCH_CONFIG_NO_NOMINMAX
//windows.h config (included transitively by catch2 on windows)
#ifdef _WIN32
@ -63,7 +56,7 @@
#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 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_*

View File

@ -2,13 +2,11 @@
#include "settings.h"
#if USE_TARTANLLAMA_OPTIONAL
#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
#include "tloptional.h"
#endif
#include "evil_macros.h"
#if USE_SINGLE_HEADER
#include "../toml.hpp"
#else
@ -43,6 +41,27 @@ TOML_POP_WARNINGS
while (false)
#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
template <typename Func>
class function_view;
@ -112,7 +131,7 @@ inline bool parse_expected_value(
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'\'';
else
return !impl::is_whitespace(codepoint);
@ -152,7 +171,7 @@ inline bool parse_expected_value(
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;
{
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"));
}
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

1327
toml.hpp

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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