tomlplusplus/include/toml++/impl/forward_declarations.h
Mark Gillard 2414d904a8 fixed value_flags not preserved during insertion (#108)
also:
- fixed `toml::value::flags()` not being cleared when `std::move`-ing a value
- fixed #195
2023-04-03 19:34:39 +03:00

1059 lines
33 KiB
C++

//# This file is a part of toml++ and is subject to the the terms of the MIT license.
//# Copyright (c) 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
#include "std_string.h"
#include "std_new.h"
TOML_DISABLE_WARNINGS;
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <cfloat>
#include <climits>
#include <cmath>
#include <limits>
#include <memory>
#include <iosfwd>
#include <type_traits>
TOML_ENABLE_WARNINGS;
#include "header_start.h"
//#---------------------------------------------------------------------------------------------------------------------
//# ENVIRONMENT GROUND-TRUTHS
//#---------------------------------------------------------------------------------------------------------------------
/// \cond
#ifndef TOML_DISABLE_ENVIRONMENT_CHECKS
#define TOML_ENV_MESSAGE \
"If you're seeing this error it's because you're building toml++ for an environment that doesn't conform to " \
"one of the 'ground truths' assumed by the library. Essentially this just means that I don't have the " \
"resources to test on more platforms, but I wish I did! You can try disabling the checks by defining " \
"TOML_DISABLE_ENVIRONMENT_CHECKS, but your mileage may vary. Please consider filing an issue at " \
"https://github.com/marzer/tomlplusplus/issues to help me improve support for your target environment. " \
"Thanks!"
static_assert(CHAR_BIT == 8, TOML_ENV_MESSAGE);
static_assert(FLT_RADIX == 2, TOML_ENV_MESSAGE);
static_assert('A' == 65, TOML_ENV_MESSAGE);
static_assert(sizeof(double) == 8, TOML_ENV_MESSAGE);
static_assert(std::numeric_limits<double>::is_iec559, TOML_ENV_MESSAGE);
static_assert(std::numeric_limits<double>::digits == 53, TOML_ENV_MESSAGE);
static_assert(std::numeric_limits<double>::digits10 == 15, TOML_ENV_MESSAGE);
#undef TOML_ENV_MESSAGE
#endif // !TOML_DISABLE_ENVIRONMENT_CHECKS
/// \endcond
//#---------------------------------------------------------------------------------------------------------------------
//# UNDOCUMENTED TYPEDEFS AND FORWARD DECLARATIONS
//#---------------------------------------------------------------------------------------------------------------------
/// \cond
// undocumented forward declarations are hidden from doxygen because they fuck it up =/
namespace toml // non-abi namespace; this is not an error
{
using ::std::size_t;
using ::std::intptr_t;
using ::std::uintptr_t;
using ::std::ptrdiff_t;
using ::std::nullptr_t;
using ::std::int8_t;
using ::std::int16_t;
using ::std::int32_t;
using ::std::int64_t;
using ::std::uint8_t;
using ::std::uint16_t;
using ::std::uint32_t;
using ::std::uint64_t;
using ::std::uint_least32_t;
using ::std::uint_least64_t;
}
TOML_NAMESPACE_START
{
struct date;
struct time;
struct time_offset;
TOML_ABI_NAMESPACE_BOOL(TOML_HAS_CUSTOM_OPTIONAL_TYPE, custopt, stdopt);
struct date_time;
TOML_ABI_NAMESPACE_END;
struct source_position;
struct source_region;
class node;
template <typename>
class node_view;
class key;
class array;
class table;
template <typename>
class value;
class path;
class toml_formatter;
class json_formatter;
class yaml_formatter;
TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex);
#if TOML_EXCEPTIONS
using parse_result = table;
#else
class parse_result;
#endif
TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS
}
TOML_NAMESPACE_END;
TOML_IMPL_NAMESPACE_START
{
using node_ptr = std::unique_ptr<node>;
TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, impl_ex, impl_noex);
class parser;
TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS
// clang-format off
inline constexpr std::string_view control_char_escapes[] =
{
"\\u0000"sv,
"\\u0001"sv,
"\\u0002"sv,
"\\u0003"sv,
"\\u0004"sv,
"\\u0005"sv,
"\\u0006"sv,
"\\u0007"sv,
"\\b"sv,
"\\t"sv,
"\\n"sv,
"\\u000B"sv,
"\\f"sv,
"\\r"sv,
"\\u000E"sv,
"\\u000F"sv,
"\\u0010"sv,
"\\u0011"sv,
"\\u0012"sv,
"\\u0013"sv,
"\\u0014"sv,
"\\u0015"sv,
"\\u0016"sv,
"\\u0017"sv,
"\\u0018"sv,
"\\u0019"sv,
"\\u001A"sv,
"\\u001B"sv,
"\\u001C"sv,
"\\u001D"sv,
"\\u001E"sv,
"\\u001F"sv,
};
inline constexpr std::string_view node_type_friendly_names[] =
{
"none"sv,
"table"sv,
"array"sv,
"string"sv,
"integer"sv,
"floating-point"sv,
"boolean"sv,
"date"sv,
"time"sv,
"date-time"sv
};
// clang-format on
}
TOML_IMPL_NAMESPACE_END;
#if TOML_ABI_NAMESPACES
#if TOML_EXCEPTIONS
#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::impl_ex::parser
#else
#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::impl_noex::parser
#endif
#else
#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::parser
#endif
/// \endcond
//#---------------------------------------------------------------------------------------------------------------------
//# DOCUMENTED TYPEDEFS AND FORWARD DECLARATIONS
//#---------------------------------------------------------------------------------------------------------------------
/// \brief The root namespace for all toml++ functions and types.
namespace toml
{
}
TOML_NAMESPACE_START // abi namespace
{
/// \brief Convenience literal operators for working with toml++.
///
/// \detail This namespace exists so you can safely hoist the toml++ literal operators into another scope
/// without dragging in everything from the toml namespace: \cpp
///
/// #include <toml++/toml.h>
/// using namespace toml::literals;
///
/// int main()
/// {
/// toml::table tbl = "vals = [1, 2, 3]"_toml;
///
/// // ... do stuff with the table generated by the "_toml" literal ...
///
/// return 0;
/// }
/// \ecpp
///
inline namespace literals
{
}
/// \brief TOML node type identifiers.
enum class TOML_CLOSED_ENUM node_type : uint8_t
{
none, ///< Not-a-node.
table, ///< The node is a toml::table.
array, ///< The node is a toml::array.
string, ///< The node is a toml::value<std::string>.
integer, ///< The node is a toml::value<int64_t>.
floating_point, ///< The node is a toml::value<double>.
boolean, ///< The node is a toml::value<bool>.
date, ///< The node is a toml::value<date>.
time, ///< The node is a toml::value<time>.
date_time ///< The node is a toml::value<date_time>.
};
/// \brief Pretty-prints the value of a node_type to a stream.
///
/// \detail \cpp
/// auto arr = toml::array{ 1, 2.0, "3", false };
/// for (size_t i = 0; i < arr.size() i++)
/// std::cout << "Element ["sv << i << "] is: "sv << arr[i].type() << "\n";
/// \ecpp
///
/// \out
/// Element [0] is: integer
/// Element [1] is: floating-point
/// Element [2] is: string
/// Element [3] is: boolean
/// \eout
template <typename Char>
inline std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& lhs, node_type rhs)
{
const auto str = impl::node_type_friendly_names[static_cast<std::underlying_type_t<node_type>>(rhs)];
using str_char_t = decltype(str)::value_type;
if constexpr (std::is_same_v<Char, str_char_t>)
return lhs << str;
else
{
if constexpr (sizeof(Char) == sizeof(str_char_t))
return lhs << std::basic_string_view<Char>{ reinterpret_cast<const Char*>(str.data()), str.length() };
else
return lhs << str.data();
}
}
/// \brief Metadata associated with TOML values.
enum class TOML_OPEN_FLAGS_ENUM value_flags : uint16_t // being an "OPEN" flags enum is not an error
{
/// \brief None.
none,
/// \brief Format integer values as binary.
format_as_binary = 1,
/// \brief Format integer values as octal.
format_as_octal = 2,
/// \brief Format integer values as hexadecimal.
format_as_hexadecimal = 3,
};
TOML_MAKE_FLAGS(value_flags);
/// \brief Special #toml::value_flags constant used for array + table insert functions to specify that any value
/// nodes being copied should not have their flags property overridden by the inserting function's `flags` argument.
inline constexpr value_flags preserve_source_value_flags =
POXY_IMPLEMENTATION_DETAIL(value_flags{ static_cast<std::underlying_type_t<value_flags>>(-1) });
/// \brief Format flags for modifying how TOML data is printed to streams.
///
/// \note Formatters may disregard/override any of these flags according to the requirements of their
/// output target (e.g. #toml::json_formatter will always apply quotes to dates and times).
enum class TOML_CLOSED_FLAGS_ENUM format_flags : uint64_t
{
/// \brief None.
none,
/// \brief Dates and times will be emitted as quoted strings.
quote_dates_and_times = (1ull << 0),
/// \brief Infinities and NaNs will be emitted as quoted strings.
quote_infinities_and_nans = (1ull << 1),
/// \brief Strings will be emitted as single-quoted literal strings where possible.
allow_literal_strings = (1ull << 2),
/// \brief Strings containing newlines will be emitted as triple-quoted 'multi-line' strings where possible.
allow_multi_line_strings = (1ull << 3),
/// \brief Allow real tab characters in string literals (as opposed to the escaped form `\t`).
allow_real_tabs_in_strings = (1ull << 4),
/// \brief Allow non-ASCII characters in strings (as opposed to their escaped form, e.g. `\u00DA`).
allow_unicode_strings = (1ull << 5),
/// \brief Allow integers with #value_flags::format_as_binary to be emitted as binary.
allow_binary_integers = (1ull << 6),
/// \brief Allow integers with #value_flags::format_as_octal to be emitted as octal.
allow_octal_integers = (1ull << 7),
/// \brief Allow integers with #value_flags::format_as_hexadecimal to be emitted as hexadecimal.
allow_hexadecimal_integers = (1ull << 8),
/// \brief Apply indentation to tables nested within other tables/arrays.
indent_sub_tables = (1ull << 9),
/// \brief Apply indentation to array elements when the array is forced to wrap over multiple lines.
indent_array_elements = (1ull << 10),
/// \brief Combination mask of all indentation-enabling flags.
indentation = indent_sub_tables | indent_array_elements,
/// \brief Emit floating-point values with relaxed (human-friendly) precision.
/// \warning Setting this flag may cause serialized documents to no longer round-trip correctly
/// since floats might have a less precise value upon being written out than they did when being
/// read in. Use this flag at your own risk.
relaxed_float_precision = (1ull << 11),
/// \brief Avoids the use of whitespace around key-value pairs.
terse_key_value_pairs = (1ull << 12),
};
TOML_MAKE_FLAGS(format_flags);
/// \brief Helper class for suppressing move-construction in single-argument array constructors.
///
/// \detail \cpp
/// // desired result: [ [ 42 ] ]
/// auto bad = toml::array{ toml::array{ 42 } };
/// auto good = toml::array{ toml::inserter{ toml::array{ 42 } } };
/// std::cout << "bad: " << bad << "\n";
/// std::cout << "good:" << good << "\n";
/// \ecpp
/// \out
/// bad: [ 42 ]
/// good: [ [ 42 ] ]
/// \eout
///
/// \see toml::array
template <typename T>
struct TOML_TRIVIAL_ABI inserter
{
static_assert(std::is_reference_v<T>);
T value;
};
template <typename T>
inserter(T&&) -> inserter<T&&>;
template <typename T>
inserter(T&) -> inserter<T&>;
/// \brief The 'default' formatter used by TOML objects when they are printed to a stream.
/// \detail This is an alias for #toml::toml_formatter.
using default_formatter = toml_formatter;
}
TOML_NAMESPACE_END;
//#---------------------------------------------------------------------------------------------------------------------
//# METAFUNCTIONS & TYPE TRAITS
//#---------------------------------------------------------------------------------------------------------------------
/// \cond
TOML_IMPL_NAMESPACE_START
{
template <typename T>
using remove_cvref = std::remove_cv_t<std::remove_reference_t<T>>;
template <typename... T>
using common_signed_type = std::common_type_t<std::make_signed_t<T>...>;
template <typename T, typename... U>
inline constexpr bool is_one_of = (false || ... || std::is_same_v<T, U>);
template <typename... T>
inline constexpr bool all_integral = (std::is_integral_v<T> && ...);
template <typename T>
inline constexpr bool is_cvref = std::is_reference_v<T> || std::is_const_v<T> || std::is_volatile_v<T>;
template <typename T>
inline constexpr bool is_wide_string =
is_one_of<std::decay_t<T>, const wchar_t*, wchar_t*, std::wstring_view, std::wstring>;
template <typename T>
inline constexpr bool value_retrieval_is_nothrow = !std::is_same_v<remove_cvref<T>, std::string>
#if TOML_HAS_CHAR8
&& !std::is_same_v<remove_cvref<T>, std::u8string>
#endif
&& !is_wide_string<T>;
template <typename, typename>
struct copy_ref_;
template <typename Dest, typename Src>
using copy_ref = typename copy_ref_<Dest, Src>::type;
template <typename Dest, typename Src>
struct copy_ref_
{
using type = Dest;
};
template <typename Dest, typename Src>
struct copy_ref_<Dest, Src&>
{
using type = std::add_lvalue_reference_t<Dest>;
};
template <typename Dest, typename Src>
struct copy_ref_<Dest, Src&&>
{
using type = std::add_rvalue_reference_t<Dest>;
};
template <typename, typename>
struct copy_cv_;
template <typename Dest, typename Src>
using copy_cv = typename copy_cv_<Dest, Src>::type;
template <typename Dest, typename Src>
struct copy_cv_
{
using type = Dest;
};
template <typename Dest, typename Src>
struct copy_cv_<Dest, const Src>
{
using type = std::add_const_t<Dest>;
};
template <typename Dest, typename Src>
struct copy_cv_<Dest, volatile Src>
{
using type = std::add_volatile_t<Dest>;
};
template <typename Dest, typename Src>
struct copy_cv_<Dest, const volatile Src>
{
using type = std::add_cv_t<Dest>;
};
template <typename Dest, typename Src>
using copy_cvref =
copy_ref<copy_ref<copy_cv<std::remove_reference_t<Dest>, std::remove_reference_t<Src>>, Dest>, Src>;
template <typename T>
inline constexpr bool dependent_false = false;
template <typename T, typename... U>
inline constexpr bool first_is_same = false;
template <typename T, typename... U>
inline constexpr bool first_is_same<T, T, U...> = true;
// 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 type = node_type::none;
};
template <typename T>
struct value_traits<const T> : value_traits<T>
{};
template <typename T>
struct value_traits<volatile T> : value_traits<T>
{};
template <typename T>
struct value_traits<const volatile T> : value_traits<T>
{};
template <typename T>
struct value_traits<T&> : value_traits<T>
{};
template <typename T>
struct value_traits<T&&> : value_traits<T>
{};
// integer value_traits specializations - standard types
template <typename T>
struct integer_limits
{
static constexpr auto min = (std::numeric_limits<T>::min)();
static constexpr auto max = (std::numeric_limits<T>::max)();
};
template <typename T>
struct integer_traits_base : integer_limits<T>
{
using native_type = int64_t;
static constexpr bool is_native = std::is_same_v<T, native_type>;
static constexpr bool is_signed = static_cast<T>(-1) < T{}; // for impls not specializing std::is_signed<T>
static constexpr auto type = node_type::integer;
static constexpr bool can_partially_represent_native = true;
};
template <typename T>
struct unsigned_integer_traits : integer_traits_base<T>
{
static constexpr bool is_losslessly_convertible_to_native = integer_limits<T>::max <= 9223372036854775807ULL;
static constexpr bool can_represent_native = false;
};
template <typename T>
struct signed_integer_traits : integer_traits_base<T>
{
using native_type = int64_t;
static constexpr bool is_losslessly_convertible_to_native =
integer_limits<T>::min >= (-9223372036854775807LL - 1LL) && integer_limits<T>::max <= 9223372036854775807LL;
static constexpr bool can_represent_native =
integer_limits<T>::min <= (-9223372036854775807LL - 1LL) && integer_limits<T>::max >= 9223372036854775807LL;
};
template <typename T, bool S = integer_traits_base<T>::is_signed>
struct integer_traits : signed_integer_traits<T>
{};
template <typename T>
struct integer_traits<T, false> : unsigned_integer_traits<T>
{};
template <>
struct value_traits<signed char> : integer_traits<signed char>
{};
template <>
struct value_traits<unsigned char> : integer_traits<unsigned char>
{};
template <>
struct value_traits<signed short> : integer_traits<signed short>
{};
template <>
struct value_traits<unsigned short> : integer_traits<unsigned short>
{};
template <>
struct value_traits<signed int> : integer_traits<signed int>
{};
template <>
struct value_traits<unsigned int> : integer_traits<unsigned int>
{};
template <>
struct value_traits<signed long> : integer_traits<signed long>
{};
template <>
struct value_traits<unsigned long> : integer_traits<unsigned long>
{};
template <>
struct value_traits<signed long long> : integer_traits<signed long long>
{};
template <>
struct value_traits<unsigned long long> : integer_traits<unsigned long long>
{};
static_assert(value_traits<int64_t>::is_native);
static_assert(value_traits<int64_t>::is_signed);
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);
// integer value_traits specializations - non-standard types
#ifdef TOML_INT128
template <>
struct integer_limits<TOML_INT128>
{
static constexpr TOML_INT128 max =
static_cast<TOML_INT128>((TOML_UINT128{ 1u } << ((__SIZEOF_INT128__ * CHAR_BIT) - 1)) - 1);
static constexpr TOML_INT128 min = -max - TOML_INT128{ 1 };
};
template <>
struct integer_limits<TOML_UINT128>
{
static constexpr TOML_UINT128 min = TOML_UINT128{};
static constexpr TOML_UINT128 max = (2u * static_cast<TOML_UINT128>(integer_limits<TOML_INT128>::max)) + 1u;
};
template <>
struct value_traits<TOML_INT128> : integer_traits<TOML_INT128>
{};
template <>
struct value_traits<TOML_UINT128> : integer_traits<TOML_UINT128>
{};
#endif
#ifdef TOML_SMALL_INT_TYPE
template <>
struct value_traits<TOML_SMALL_INT_TYPE> : signed_integer_traits<TOML_SMALL_INT_TYPE>
{};
#endif
// floating-point traits base
template <typename T, int MantissaDigits, int DecimalDigits>
struct float_traits_base
{
static constexpr auto type = node_type::floating_point;
using native_type = double;
static constexpr bool is_native = std::is_same_v<T, native_type>;
static constexpr bool is_signed = true;
static constexpr int bits = static_cast<int>(sizeof(T) * CHAR_BIT);
static constexpr int digits = MantissaDigits;
static constexpr int digits10 = DecimalDigits;
static constexpr bool is_losslessly_convertible_to_native = bits <= 64 //
&& digits <= 53 // DBL_MANT_DIG
&& digits10 <= 15; // DBL_DIG
static constexpr bool can_represent_native = digits >= 53 // DBL_MANT_DIG
&& digits10 >= 15; // DBL_DIG
static constexpr bool can_partially_represent_native = digits > 0 && digits10 > 0;
};
template <typename T>
struct float_traits : float_traits_base<T, std::numeric_limits<T>::digits, std::numeric_limits<T>::digits10>
{};
#if TOML_ENABLE_FLOAT16
template <>
struct float_traits<_Float16> : float_traits_base<_Float16, __FLT16_MANT_DIG__, __FLT16_DIG__>
{};
#endif
#ifdef TOML_FLOAT128
template <>
struct float_traits<TOML_FLOAT128> : float_traits_base<TOML_FLOAT128, __FLT128_MANT_DIG__, __FLT128_DIG__>
{};
#endif
// floating-point traits
template <>
struct value_traits<float> : float_traits<float>
{};
template <>
struct value_traits<double> : float_traits<double>
{};
template <>
struct value_traits<long double> : float_traits<long double>
{};
#if TOML_ENABLE_FLOAT16
template <>
struct value_traits<_Float16> : float_traits<_Float16>
{};
#endif
#ifdef TOML_FLOAT128
template <>
struct value_traits<TOML_FLOAT128> : float_traits<TOML_FLOAT128>
{};
#endif
#ifdef TOML_SMALL_FLOAT_TYPE
template <>
struct value_traits<TOML_SMALL_FLOAT_TYPE> : float_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 specializations - char-based strings
template <typename T>
struct string_traits
{
using native_type = std::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 type = node_type::string;
};
template <>
struct value_traits<std::string> : string_traits<std::string>
{};
template <>
struct value_traits<std::string_view> : string_traits<std::string_view>
{};
template <>
struct value_traits<const char*> : string_traits<const char*>
{};
template <size_t N>
struct value_traits<const char[N]> : string_traits<const char[N]>
{};
template <>
struct value_traits<char*> : string_traits<char*>
{};
template <size_t N>
struct value_traits<char[N]> : string_traits<char[N]>
{};
// string value_traits specializations - char8_t-based strings
#if TOML_HAS_CHAR8
template <>
struct value_traits<std::u8string> : string_traits<std::u8string>
{};
template <>
struct value_traits<std::u8string_view> : string_traits<std::u8string_view>
{};
template <>
struct value_traits<const char8_t*> : string_traits<const char8_t*>
{};
template <size_t N>
struct value_traits<const char8_t[N]> : string_traits<const char8_t[N]>
{};
template <>
struct value_traits<char8_t*> : string_traits<char8_t*>
{};
template <size_t N>
struct value_traits<char8_t[N]> : string_traits<char8_t[N]>
{};
#endif
// string value_traits specializations - wchar_t-based strings on Windows
#if TOML_ENABLE_WINDOWS_COMPAT
template <typename T>
struct wstring_traits
{
using native_type = std::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 type = node_type::string;
};
template <>
struct value_traits<std::wstring> : wstring_traits<std::wstring>
{};
template <>
struct value_traits<std::wstring_view> : wstring_traits<std::wstring_view>
{};
template <>
struct value_traits<const wchar_t*> : wstring_traits<const wchar_t*>
{};
template <size_t N>
struct value_traits<const wchar_t[N]> : wstring_traits<const wchar_t[N]>
{};
template <>
struct value_traits<wchar_t*> : wstring_traits<wchar_t*>
{};
template <size_t N>
struct value_traits<wchar_t[N]> : wstring_traits<wchar_t[N]>
{};
#endif
// other 'native' value_traits specializations
template <typename T, 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 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>
{};
// native value category queries
template <typename T>
using native_type_of = typename value_traits<T>::native_type;
template <typename T>
inline constexpr bool is_native = value_traits<T>::is_native;
template <typename T>
inline constexpr bool can_represent_native = value_traits<T>::can_represent_native;
template <typename T>
inline constexpr bool can_partially_represent_native = value_traits<T>::can_partially_represent_native;
template <typename T>
inline constexpr bool is_losslessly_convertible_to_native = value_traits<T>::is_losslessly_convertible_to_native;
template <typename T, typename... U>
inline constexpr bool is_natively_one_of = is_one_of<native_type_of<T>, U...>;
// native value types => nodes
template <typename T>
struct node_wrapper
{
using type = T;
};
template <typename T>
struct node_wrapper<const T>
{
using type = std::add_const_t<typename node_wrapper<T>::type>;
};
template <typename T>
struct node_wrapper<volatile T>
{
using type = std::add_volatile_t<typename node_wrapper<T>::type>;
};
template <typename T>
struct node_wrapper<const volatile T>
{
using type = std::add_const_t<std::add_volatile_t<typename node_wrapper<T>::type>>;
};
template <>
struct node_wrapper<std::string>
{
using type = value<std::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<std::remove_reference_t<T>>::type;
// nodes => native value types
template <typename T>
struct node_unwrapper
{
using type = T;
};
template <typename T>
struct node_unwrapper<value<T>>
{
using type = T;
};
template <typename T>
struct node_unwrapper<const value<T>>
{
using type = std::add_const_t<T>;
};
template <typename T>
struct node_unwrapper<volatile value<T>>
{
using type = std::add_volatile_t<T>;
};
template <typename T>
struct node_unwrapper<const volatile value<T>>
{
using type = std::add_volatile_t<std::add_const_t<T>>;
};
template <typename T>
using unwrap_node = typename node_unwrapper<std::remove_reference_t<T>>::type;
template <typename T>
struct node_type_getter
{
static constexpr auto value = value_traits<T>::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 <>
struct node_type_getter<void>
{
static constexpr auto value = node_type::none;
};
template <typename T>
inline constexpr node_type node_type_of = node_type_getter<unwrap_node<remove_cvref<T>>>::value;
template <typename T, typename ConvertFrom>
inline constexpr bool is_constructible_or_convertible = std::is_constructible_v<T, ConvertFrom> //
|| std::is_convertible_v<ConvertFrom, T>;
}
TOML_IMPL_NAMESPACE_END;
/// \endcond
TOML_NAMESPACE_START
{
/// \brief Metafunction for determining if a type is, or is a reference to, a toml::table.
template <typename T>
inline constexpr bool is_table = std::is_same_v<impl::remove_cvref<T>, table>;
/// \brief Metafunction for determining if a type is, or is a reference to, a toml::array.
template <typename T>
inline constexpr bool is_array = std::is_same_v<impl::remove_cvref<T>, array>;
/// \brief Metafunction for determining if a type satisfies either toml::is_table or toml::is_array.
template <typename T>
inline constexpr bool is_container = is_table<T> || is_array<T>;
/// \brief Metafunction for determining if a type is, or is a reference to, a std::string or toml::value<std::string>.
template <typename T>
inline constexpr bool is_string = std::is_same_v< //
impl::remove_cvref<impl::wrap_node<impl::remove_cvref<T>>>, //
value<std::string>>;
/// \brief Metafunction for determining if a type is, or is a reference to, a int64_t or toml::value<int64_t>.
template <typename T>
inline constexpr bool is_integer = std::is_same_v< //
impl::remove_cvref<impl::wrap_node<impl::remove_cvref<T>>>, //
value<int64_t>>;
/// \brief Metafunction for determining if a type is, or is a reference to, a double or toml::value<double>.
template <typename T>
inline constexpr bool is_floating_point = std::is_same_v< //
impl::remove_cvref<impl::wrap_node<impl::remove_cvref<T>>>, //
value<double>>;
/// \brief Metafunction for determining if a type satisfies either toml::is_integer or 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, or is a reference to, a bool or toml::value<bool>.
template <typename T>
inline constexpr bool is_boolean = std::is_same_v< //
impl::remove_cvref<impl::wrap_node<impl::remove_cvref<T>>>, //
value<bool>>;
/// \brief Metafunction for determining if a type is, or is a reference to, a toml::date or toml::value<date>.
template <typename T>
inline constexpr bool is_date = std::is_same_v< //
impl::remove_cvref<impl::wrap_node<impl::remove_cvref<T>>>, //
value<date>>;
/// \brief Metafunction for determining if a type is, or is a reference to, a toml::time or toml::value<time>.
template <typename T>
inline constexpr bool is_time = std::is_same_v< //
impl::remove_cvref<impl::wrap_node<impl::remove_cvref<T>>>, //
value<time>>;
/// \brief Metafunction for determining if a type is, or is a reference to, a toml::date_time or toml::value<date_time>.
template <typename T>
inline constexpr bool is_date_time = std::is_same_v< //
impl::remove_cvref<impl::wrap_node<impl::remove_cvref<T>>>, //
value<date_time>>;
/// \brief Metafunction for determining if a type satisfies any of toml::is_date, toml::is_time or toml::is_date_time.
template <typename T>
inline constexpr bool is_chronological = is_date<T> || is_time<T> || is_date_time<T>;
/// \brief Metafunction for determining if a type is, or is a reference to, any of the toml value types. Excludes tables and arrays.
template <typename T>
inline constexpr bool is_value = is_string<T> || is_number<T> || is_boolean<T> || is_chronological<T>;
/// \brief Metafunction for determining if a type is, or is a reference to, a toml::node (or one of its subclasses).
template <typename T>
inline constexpr bool is_node = std::is_same_v<toml::node, impl::remove_cvref<T>> //
|| std::is_base_of_v<toml::node, impl::remove_cvref<T>>;
/// \brief Metafunction for determining if a type is, or is a reference to, a toml::node_view.
template <typename T>
inline constexpr bool is_node_view = impl::is_one_of<impl::remove_cvref<T>, node_view<node>, node_view<const node>>;
}
TOML_NAMESPACE_END;
//#---------------------------------------------------------------------------------------------------------------------
//# INTERNAL HELPERS
//#---------------------------------------------------------------------------------------------------------------------
/// \cond
TOML_IMPL_NAMESPACE_START
{
template <typename T>
TOML_CONST_INLINE_GETTER
constexpr std::underlying_type_t<T> unwrap_enum(T val) noexcept
{
return static_cast<std::underlying_type_t<T>>(val);
}
// Q: "why not use std::fpclassify?"
// A: Because it gets broken by -ffast-math and friends
enum class TOML_CLOSED_ENUM fp_class : unsigned
{
ok,
neg_inf,
pos_inf,
nan
};
TOML_PURE_GETTER
inline fp_class fpclassify(const double& val) noexcept
{
static_assert(sizeof(uint64_t) == sizeof(double));
static constexpr uint64_t sign = 0b1000000000000000000000000000000000000000000000000000000000000000ull;
static constexpr uint64_t exponent = 0b0111111111110000000000000000000000000000000000000000000000000000ull;
static constexpr uint64_t mantissa = 0b0000000000001111111111111111111111111111111111111111111111111111ull;
uint64_t val_bits;
std::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 and std::min?"
// A: Because <algorithm> is _huge_ and these would be the only things I used from it.
// I don't want to impose such a heavy compile-time burden on users.
template <typename Iterator, typename T>
TOML_PURE_GETTER
inline auto find(Iterator start, Iterator end, const T& needle) noexcept //
->decltype(&(*start))
{
for (; start != end; start++)
if (*start == needle)
return &(*start);
return nullptr;
}
template <typename T>
TOML_PURE_INLINE_GETTER
constexpr const T& min(const T& a, const T& b) noexcept //
{
return a < b ? a : b;
}
}
TOML_IMPL_NAMESPACE_END;
/// \endcond
#include "header_end.h"