added relops for values, arrays and tables

also:
 - fixed stream operator for node_type
 - implemented node::visit() noexcept propagation
 - removed some unnecessary overloads of string-based accessors
This commit is contained in:
Mark Gillard 2020-01-22 23:29:46 +02:00
parent abdd4f9993
commit c4f30c2ffa
13 changed files with 969 additions and 503 deletions

View File

@ -53,7 +53,7 @@ EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 0
AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = NO
BUILTIN_STL_SUPPORT = YES
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = NO

2
extern/Catch2 vendored

@ -1 +1 @@
Subproject commit d10b9bd02e098476670f5eb0527d2c7281476e8a
Subproject commit bff44f8b0116e0ba1fa24fb5249d43c2639fbb64

View File

@ -8,9 +8,9 @@
#include "toml_date_time.h"
#include "toml_print_to_stream.h"
#include "toml_node.h"
#include "toml_table.h"
#include "toml_array.h"
#include "toml_value.h"
#include "toml_array.h"
#include "toml_table.h"
#include "toml_node_view.h"
#include "toml_utf8.h"
#include "toml_parser.h"
@ -35,7 +35,7 @@
#undef TOML_EMPTY_BASES
#undef TOML_CPP_VERSION
#undef TOML_CPP
#undef TOML_CONDITIONAL_NOEXCEPT
#undef TOML_MAY_THROW_UNLESS
#undef TOML_MAY_THROW
#undef TOML_NO_DEFAULT_CASE
#undef TOML_CONSTEVAL

View File

@ -1,5 +1,5 @@
#pragma once
#include "toml_node.h"
#include "toml_value.h"
namespace toml::impl
{
@ -79,9 +79,12 @@ namespace toml::impl
namespace toml
{
[[nodiscard]] bool operator == (const table& lhs, const table& rhs) noexcept;
[[nodiscard]] bool operator != (const table& lhs, const table& rhs) noexcept;
/// \brief A TOML array.
/// \detail The interface of this type is modeled after std::vector so things
/// mostly work as you'd expect them to with a vector: \cpp
/// mostly work as you'd expect them to: \cpp
///
/// auto table = toml::parse("arr = [1, 2, 3, 4, 'five']"sv);
///
@ -263,6 +266,47 @@ namespace toml
return values[index]->as<T>();
}
/// \brief Equality operator.
///
/// \param lhs The LHS array.
/// \param rhs The RHS array.
///
/// \returns True if the arrays contained the same values.
[[nodiscard]] friend bool operator == (const array& lhs, const array& rhs) noexcept
{
if (&lhs == &rhs)
return true;
if (lhs.values.size() != rhs.values.size())
return false;
for (size_t i = 0, e = lhs.values.size(); i < e; i++)
{
const auto lhs_type = lhs.values[i]->type();
const node& rhs_ = *rhs.values[i];
const auto rhs_type = rhs_.type();
if (lhs_type != rhs_type)
return false;
const bool equal = lhs.values[i]->visit([&](const auto& lhs_) noexcept
{
return lhs_ == *reinterpret_cast<std::remove_reference_t<decltype(lhs_)>*>(&rhs_);
});
if (!equal)
return false;
}
return true;
}
/// \brief Inequality operator.
///
/// \param lhs The LHS array.
/// \param rhs The RHS array.
///
/// \returns True if the arrays did not contain the same values.
[[nodiscard]] friend bool operator != (const array& lhs, const array& rhs) noexcept
{
return !(lhs == rhs);
}
template <typename CHAR>
friend inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>&, const array&) TOML_MAY_THROW;
};

View File

@ -140,11 +140,11 @@
#define TOML_DOXYGEN 0
#endif
#if TOML_EXCEPTIONS
#define TOML_CONDITIONAL_NOEXCEPT(...) noexcept(__VA_ARGS__)
#define TOML_MAY_THROW
#define TOML_MAY_THROW_UNLESS(...) noexcept(__VA_ARGS__)
#else
#define TOML_CONDITIONAL_NOEXCEPT(...) noexcept
#define TOML_MAY_THROW noexcept
#define TOML_MAY_THROW noexcept
#define TOML_MAY_THROW_UNLESS(...) noexcept
#endif
#ifndef TOML_DISABLE_INIT_WARNINGS
@ -328,15 +328,15 @@ namespace toml
/// \brief TOML node type identifiers.
enum class node_type : uint8_t
{
table,
array,
string,
integer,
floating_point,
boolean,
date,
time,
date_time
table, ///< The node is a toml::table.
array, ///< The node is a toml::array.
string, ///< The node is a toml::value<toml::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<toml::date>.
time, ///< The node is a toml::value<toml::time>.
date_time ///< The node is a toml::value<toml::date_time>.
};
#if TOML_LARGE_FILES
@ -534,6 +534,20 @@ namespace toml::impl
#endif
template <typename T, typename... U>
struct is_one_of_ : std::integral_constant<bool,
(false || ... || std::is_same_v<T, U>)
> {};
template <typename T, typename... U>
inline constexpr bool is_one_of = is_one_of_<T, U...>::value;
template <typename T, typename... U>
using enable_if_one_of = std::enable_if_t<is_one_of<T, U...>>;
template <typename T, typename... U>
using enable_if_not_one_of = std::enable_if_t<!is_one_of<T, U...>>;
template <typename T>
[[nodiscard]] TOML_ALWAYS_INLINE
constexpr std::underlying_type_t<T> unwrap_enum(T val) noexcept
@ -615,6 +629,20 @@ namespace toml::impl
#endif
template <typename T> using promoted = typename impl::value_promoter<T>::type;
template <typename T> struct node_type_of_;
template <> struct node_type_of_<table> { static constexpr auto value = node_type::table; };
template <> struct node_type_of_<array> { static constexpr auto value = node_type::array; };
template <> struct node_type_of_<string> { static constexpr auto value = node_type::string; };
template <> struct node_type_of_<int64_t> { static constexpr auto value = node_type::integer; };
template <> struct node_type_of_<double> { static constexpr auto value = node_type::floating_point; };
template <> struct node_type_of_<bool> { static constexpr auto value = node_type::boolean; };
template <> struct node_type_of_<date> { static constexpr auto value = node_type::date; };
template <> struct node_type_of_<time> { static constexpr auto value = node_type::time; };
template <> struct node_type_of_<date_time> { static constexpr auto value = node_type::date_time; };
template <typename T>
inline constexpr auto node_type_of = node_type_of_<promoted<typename node_unwrapper<T>::type>>::value;
inline constexpr toml::string_view low_character_escape_table[] =
{
TOML_STRING_PREFIX("\\u0000"sv),
@ -730,14 +758,15 @@ namespace toml
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, node_type rhs) TOML_MAY_THROW
{
using underlying_t = std::underlying_type_t<node_type>;
const auto str = impl::node_type_friendly_names[static_cast<underlying_t>(rhs)];
if constexpr (std::is_same_v<CHAR, char>)
return lhs << impl::node_type_friendly_names[static_cast<underlying_t>(rhs)];
else if constexpr (sizeof(CHAR) == 1)
{
const auto str = impl::node_type_friendly_names[static_cast<underlying_t>(rhs)];
return lhs << std::basic_string_view<CHAR>{ reinterpret_cast<const CHAR*>(str.data()), str.length() };
}
return lhs << str;
else
return lhs << lhs.data();
{
if constexpr (sizeof(CHAR) == 1)
return lhs << std::basic_string_view<CHAR>{ reinterpret_cast<const CHAR*>(str.data()), str.length() };
else
return lhs << str.data();
}
}
}

View File

@ -202,7 +202,55 @@ namespace toml
private:
template <typename FUNC, typename N, typename T, bool = std::is_invocable_v<FUNC, ref_cast_type<N, T>>>
template <typename FUNC, typename N, typename T>
static constexpr bool can_visit = std::is_invocable_v<FUNC, ref_cast_type<N, T>>;
template <typename FUNC, typename N>
static constexpr bool can_visit_any =
can_visit<FUNC, N, table>
|| can_visit<FUNC, N, array>
|| can_visit<FUNC, N, string>
|| can_visit<FUNC, N, int64_t>
|| can_visit<FUNC, N, double>
|| can_visit<FUNC, N, bool>
|| can_visit<FUNC, N, date>
|| can_visit<FUNC, N, time>
|| can_visit<FUNC, N, date_time>;
template <typename FUNC, typename N>
static constexpr bool can_visit_all =
can_visit<FUNC, N, table>
&& can_visit<FUNC, N, array>
&& can_visit<FUNC, N, string>
&& can_visit<FUNC, N, int64_t>
&& can_visit<FUNC, N, double>
&& can_visit<FUNC, N, bool>
&& can_visit<FUNC, N, date>
&& can_visit<FUNC, N, time>
&& can_visit<FUNC, N, date_time>;
#if TOML_EXCEPTIONS
template <typename FUNC, typename N, typename T>
static constexpr bool visit_is_nothrow_one =
!can_visit<FUNC, N, T>
|| std::is_nothrow_invocable_v<FUNC, ref_cast_type<N, T>>;
template <typename FUNC, typename N>
static constexpr bool visit_is_nothrow =
visit_is_nothrow_one<FUNC, N, table>
&& visit_is_nothrow_one<FUNC, N, array>
&& visit_is_nothrow_one<FUNC, N, string>
&& visit_is_nothrow_one<FUNC, N, int64_t>
&& visit_is_nothrow_one<FUNC, N, double>
&& visit_is_nothrow_one<FUNC, N, bool>
&& visit_is_nothrow_one<FUNC, N, date>
&& visit_is_nothrow_one<FUNC, N, time>
&& visit_is_nothrow_one<FUNC, N, date_time>;
#endif
template <typename FUNC, typename N, typename T, bool = can_visit<FUNC, N, T>>
struct visit_return_type final
{
using type = decltype(std::declval<FUNC>()(std::declval<ref_cast_type<N, T>>()));
@ -216,78 +264,60 @@ namespace toml
template <typename A, typename B>
using nonvoid = std::conditional_t<std::is_void_v<A>, B, A>;
// this is done using a static helper to preserve const and ref categories
// (otherwise I'd have to implement this function thrice)
// ((propagation in C++: a modern horror story))
//# this is done using a static helper to preserve const and ref categories
//# (otherwise I'd have to implement this function thrice)
//# ((propagation in C++: a modern horror story))
template <typename N, typename FUNC>
static decltype(auto) do_visit(N&& node, FUNC&& visitor) TOML_MAY_THROW
static decltype(auto) do_visit(N&& node, FUNC&& visitor)
TOML_MAY_THROW_UNLESS(visit_is_nothrow<FUNC&&, N&&>)
{
static_assert(
std::is_invocable_v<FUNC&&, ref_cast_type<N&&, table>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, array>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, string>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, int64_t>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, double>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, bool>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, time>>
|| std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date_time>>,
can_visit_any<FUNC&&, N&&>,
"Visitors must be invocable for at least one of the toml::node specializations"
);
switch (node.type())
{
case node_type::table:
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, table>>)
if constexpr (can_visit<FUNC&&, N&&, table>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<table>());
break;
case node_type::array:
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, array>>)
if constexpr (can_visit<FUNC&&, N&&, array>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<array>());
break;
case node_type::string:
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, string>>)
if constexpr (can_visit<FUNC&&, N&&, string>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<string>());
break;
case node_type::integer:
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, int64_t>>)
if constexpr (can_visit<FUNC&&, N&&, int64_t>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<int64_t>());
break;
case node_type::floating_point:
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, double>>)
if constexpr (can_visit<FUNC&&, N&&, double>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<double>());
break;
case node_type::boolean:
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, bool>>)
if constexpr (can_visit<FUNC&&, N&&, bool>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<bool>());
break;
case node_type::date:
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date>>)
if constexpr (can_visit<FUNC&&, N&&, date>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<date>());
break;
case node_type::time:
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, time>>)
if constexpr (can_visit<FUNC&&, N&&, time>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<time>());
break;
case node_type::date_time:
if constexpr (std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date_time>>)
if constexpr (can_visit<FUNC&&, N&&, date_time>)
return std::forward<FUNC>(visitor)(std::forward<N>(node).template ref_cast<date_time>());
break;
TOML_NO_DEFAULT_CASE;
}
static constexpr auto is_exhaustive =
std::is_invocable_v<FUNC&&, ref_cast_type<N&&, table>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, array>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, string>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, int64_t>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, double>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, bool>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, time>>
&& std::is_invocable_v<FUNC&&, ref_cast_type<N&&, date_time>>;
if constexpr (is_exhaustive)
if constexpr (can_visit_all<FUNC&&, N&&>)
TOML_UNREACHABLE;
else
{
@ -300,8 +330,8 @@ namespace toml
nonvoid<typename visit_return_type<FUNC&&, N&&, bool>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, date>::type,
nonvoid<typename visit_return_type<FUNC&&, N&&, time>::type,
typename visit_return_type<FUNC&&, N&&, date_time>::type
>>>>>>>>;
typename visit_return_type<FUNC&&, N&&, date_time>::type
>>>>>>>>;
if constexpr (!std::is_void_v<return_type>)
{
@ -326,9 +356,9 @@ namespace toml
/// node.visit([](auto&& n)
/// {
/// if constexpr (toml::is_string<decltype(n)>)
/// do_something_with_a_string(n.get());
/// else if constexpr (toml::is_floating_point<decltype(n)>)
/// do_something_with_a_float(n.get());
/// do_something_with_a_string(*n)); //n is a toml::value<toml::string>
/// else if constexpr (toml::is_integer<decltype(n)>)
/// do_something_with_an_int(*n); //n is a toml::value<int64_t>
/// else
/// throw std::exception("Expected string or integer")
/// });
@ -341,25 +371,25 @@ namespace toml
/// \param visitor The visitor object.
///
/// \returns The return value of the visitor.
/// Must be default-constructible if non-void and your visitor is not exhaustive.
/// Can be void. Non-exhaustive visitors must return a default-constructible type.
///
/// \see https://en.wikipedia.org/wiki/Visitor_pattern
template <typename FUNC>
decltype(auto) visit(FUNC&& visitor) & TOML_MAY_THROW
decltype(auto) visit(FUNC&& visitor) & TOML_MAY_THROW_UNLESS(visit_is_nothrow<FUNC&&, node&>)
{
return do_visit(*this, std::forward<FUNC>(visitor));
}
/// \brief Invokes a visitor on the node based on the node's concrete type (rvalue overload).
template <typename FUNC>
decltype(auto) visit(FUNC&& visitor) && TOML_MAY_THROW
decltype(auto) visit(FUNC&& visitor) && TOML_MAY_THROW_UNLESS(visit_is_nothrow<FUNC&&, node&&>)
{
return do_visit(std::move(*this), std::forward<FUNC>(visitor));
}
/// \brief Invokes a visitor on the node based on the node's concrete type (const overload).
template <typename FUNC>
decltype(auto) visit(FUNC&& visitor) const& TOML_MAY_THROW
decltype(auto) visit(FUNC&& visitor) const& TOML_MAY_THROW_UNLESS(visit_is_nothrow<FUNC&&, const node&>)
{
return do_visit(*this, std::forward<FUNC>(visitor));
}

View File

@ -232,7 +232,6 @@ namespace toml
public:
[[nodiscard]] bool operator == (const string& rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (string_view rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (int64_t rhs) const noexcept { return value_equality(*this, rhs); }
[[nodiscard]] bool operator == (int32_t rhs) const noexcept { return value_equality(*this, rhs); }

View File

@ -1,5 +1,5 @@
#pragma once
#include "toml_node.h"
#include "toml_array.h"
namespace toml::impl
{
@ -155,10 +155,6 @@ namespace toml
{
return { values.erase(first.raw_, last.raw_) };
}
bool erase(const string& key) noexcept
{
return values.erase(key) > 0_sz;
}
bool erase(string_view key) noexcept
{
if (auto it = values.find(key); it != values.end())
@ -206,25 +202,63 @@ namespace toml
public:
[[nodiscard]] node* get(string_view key) noexcept { return do_get(values, key); }
[[nodiscard]] node* get(const string& key) noexcept { return do_get(values, key); }
[[nodiscard]] const node* get(string_view key) const noexcept { return do_get(values, key); }
[[nodiscard]] const node* get(const string& key) const noexcept { return do_get(values, key); }
template <typename T>
[[nodiscard]] node_of<T>* get_as(string_view key) noexcept { return do_get_as<T>(values, key); }
template <typename T>
[[nodiscard]] node_of<T>* get_as(const string& key) noexcept { return do_get_as<T>(values, key); }
template <typename T>
[[nodiscard]] const node_of<T>* get_as(string_view key) const noexcept { return do_get_as<T>(values, key); }
template <typename T>
[[nodiscard]] const node_of<T>* get_as(const string& key) const noexcept { return do_get_as<T>(values, key); }
[[nodiscard]] bool contains(const string& key) const noexcept { return do_contains(values, key); }
[[nodiscard]] bool contains(string_view key) const noexcept { return do_contains(values, key); }
[[nodiscard]] inline node_view<table> operator[] (string_view) noexcept;
[[nodiscard]] inline node_view<const table> operator[] (string_view) const noexcept;
/// \brief Equality operator.
///
/// \param lhs The LHS table.
/// \param rhs The RHS table.
///
/// \returns True if the tables contained the same keys and values.
[[nodiscard]] friend bool operator == (const table& lhs, const table& rhs) noexcept
{
if (&lhs == &rhs)
return true;
if (lhs.values.size() != rhs.values.size())
return false;
for (auto l = lhs.values.begin(), r = rhs.values.begin(), e = lhs.values.end(); l != e; l++, r++)
{
if (l->first != r->first)
return false;
const auto lhs_type = l->second->type();
const node& rhs_ = *r->second;
const auto rhs_type = rhs_.type();
if (lhs_type != rhs_type)
return false;
const bool equal = l->second->visit([&](const auto& lhs_) noexcept
{
return lhs_ == *reinterpret_cast<std::remove_reference_t<decltype(lhs_)>*>(&rhs_);
});
if (!equal)
return false;
}
return true;
}
/// \brief Inequality operator.
///
/// \param lhs The LHS table.
/// \param rhs The RHS table.
///
/// \returns True if the tables did not contain the same keys and values.
[[nodiscard]] friend bool operator != (const table& lhs, const table& rhs) noexcept
{
return !(lhs == rhs);
}
template <typename CHAR>
friend inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>&, const table&) TOML_MAY_THROW;
};

View File

@ -375,7 +375,7 @@ namespace toml::impl
template <typename U, typename STR = std::string_view>
explicit utf8_reader(U && source, STR&& source_path = {})
TOML_CONDITIONAL_NOEXCEPT(std::is_nothrow_constructible_v<utf8_byte_stream<T>, U&&>)
TOML_MAY_THROW_UNLESS(std::is_nothrow_constructible_v<utf8_byte_stream<T>, U&&>)
: stream{ std::forward<U>(source) }
{
current.position = { 1, 1 };

View File

@ -4,9 +4,20 @@
namespace toml
{
/// \brief A TOML value.
///
/// \extends ::toml::node
///
/// \tparam T The value's data type. Can be one of:
/// - toml::string
/// - int64_t
/// - double
/// - bool
/// - toml::date
/// - toml::time
/// - toml::date_time
template <typename T>
class value final
: public node
class value final : public node
{
static_assert(
impl::is_value<T>,
@ -30,18 +41,24 @@ namespace toml
public:
/// \brief Constructs a toml value.
///
/// \tparam U Constructor argument types.
/// \param args Variadic list of arguments that are forwarded to the internal value's constructor.
template <typename... U>
TOML_NODISCARD_CTOR
explicit value(U&&... args) TOML_CONDITIONAL_NOEXCEPT(std::is_nothrow_constructible_v<T, U &&...>)
explicit value(U&&... args) TOML_MAY_THROW_UNLESS(std::is_nothrow_constructible_v<T, U &&...>)
: val_{ std::forward<U>(args)... }
{}
/// \brief Move constructor.
TOML_NODISCARD_CTOR
value(value&& other) noexcept
: node{ std::move(other) },
val_{ std::move(other.val_) }
{}
/// \brief Move-assignment operator.
value& operator= (value&& rhs) noexcept
{
node::operator=(std::move(rhs));
@ -49,19 +66,23 @@ namespace toml
return *this;
}
[[nodiscard]] node_type type() const noexcept override
{
if constexpr (std::is_same_v<T, string>) return node_type::string;
else if constexpr (std::is_same_v<T, int64_t>) return node_type::integer;
else if constexpr (std::is_same_v<T, double>) return node_type::floating_point;
else if constexpr (std::is_same_v<T, bool>) return node_type::boolean;
else if constexpr (std::is_same_v<T, date>) return node_type::date;
else if constexpr (std::is_same_v<T, time>) return node_type::time;
else if constexpr (std::is_same_v<T, date_time>) return node_type::date_time;
}
/// \brief Returns the value's node type identifier.
///
/// \returns One of:
/// - node_type::string
/// - node_type::integer
/// - node_type::floating_point
/// - node_type::boolean
/// - node_type::date
/// - node_type::time
/// - node_type::date_time
[[nodiscard]] node_type type() const noexcept override { return impl::node_type_of<T>; }
/// \brief Always returns `false` for value nodes.
[[nodiscard]] bool is_table() const noexcept override { return false; }
/// \brief Always returns `false` for value nodes.
[[nodiscard]] bool is_array() const noexcept override { return false; }
/// \brief Always returns `true` for value nodes.
[[nodiscard]] bool is_value() const noexcept override { return true; }
[[nodiscard]] bool is_string() const noexcept override { return std::is_same_v<T, string>; }
@ -88,16 +109,25 @@ namespace toml
[[nodiscard]] const value<time>* as_time() const noexcept override { return as_value<time>(this); }
[[nodiscard]] const value<date_time>* as_date_time() const noexcept override { return as_value<date_time>(this); }
/// \brief Returns a reference to the underlying value.
[[nodiscard]] T& get() & noexcept { return val_; }
/// \brief Returns a reference to the underlying value (rvalue overload).
[[nodiscard]] T&& get() && noexcept { return std::move(val_); }
/// \brief Returns a reference to the underlying value (const overload).
[[nodiscard]] const T& get() const & noexcept { return val_; }
/// \brief Returns a reference to the underlying value.
[[nodiscard]] T& operator* () & noexcept { return val_; }
/// \brief Returns a reference to the underlying value (rvalue overload).
[[nodiscard]] T&& operator* () && noexcept { return std::move(val_); }
/// \brief Returns a reference to the underlying value (const overload).
[[nodiscard]] const T& operator* () const& noexcept { return val_; }
/// \brief Returns a reference to the underlying value.
[[nodiscard]] operator T& () & noexcept { return val_; }
/// \brief Returns a reference to the underlying value (rvalue overload).
[[nodiscard]] operator T&& () && noexcept { return std::move(val_); }
/// \brief Returns a reference to the underlying value (const overload).
[[nodiscard]] operator const T& () const& noexcept { return val_; }
template <typename CHAR>
@ -117,6 +147,134 @@ namespace toml
return lhs;
}
using value_arg_t = std::conditional_t<
std::is_same_v<T, string>,
string_view,
std::conditional_t<impl::is_one_of<T, double, int64_t, bool>, T, const T&>
>;
/// \brief Value equality operator.
[[nodiscard]] friend bool operator == (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ == rhs; }
/// \brief Value equality operator.
[[nodiscard]] friend bool operator == (value_arg_t lhs, const value& rhs) noexcept { return lhs == rhs.val_; }
/// \brief Value inequality operator.
[[nodiscard]] friend bool operator != (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ != rhs; }
/// \brief Value inequality operator.
[[nodiscard]] friend bool operator != (value_arg_t lhs, const value& rhs) noexcept { return lhs != rhs.val_; }
/// \brief Value less-than operator.
[[nodiscard]] friend bool operator < (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ < rhs; }
/// \brief Value less-than operator.
[[nodiscard]] friend bool operator < (value_arg_t lhs, const value& rhs) noexcept { return lhs < rhs.val_; }
/// \brief Value less-than-or-equal-to operator.
[[nodiscard]] friend bool operator <= (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ <= rhs; }
/// \brief Value less-than-or-equal-to operator.
[[nodiscard]] friend bool operator <= (value_arg_t lhs, const value& rhs) noexcept { return lhs <= rhs.val_; }
/// \brief Value greater-than operator.
[[nodiscard]] friend bool operator > (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ > rhs; }
/// \brief Value greater-than operator.
[[nodiscard]] friend bool operator > (value_arg_t lhs, const value& rhs) noexcept { return lhs > rhs.val_; }
/// \brief Value greater-than-or-equal-to operator.
[[nodiscard]] friend bool operator >= (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ >= rhs; }
/// \brief Value greater-than-or-equal-to operator.
[[nodiscard]] friend bool operator >= (value_arg_t lhs, const value& rhs) noexcept { return lhs >= rhs.val_; }
/// \brief Equality operator.
///
/// \param lhs The LHS value.
/// \param rhs The RHS value.
///
/// \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
{
if constexpr (std::is_same_v<T, U>)
return lhs.val_ == rhs.val_;
else
return false;
}
/// \brief Inequality operator.
///
/// \param lhs The LHS value.
/// \param rhs The RHS value.
///
/// \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
{
if constexpr (std::is_same_v<T, U>)
return lhs.val_ != rhs.val_;
else
return true;
}
/// \brief Less-than operator.
///
/// \param lhs The LHS toml::value.
/// \param rhs The RHS toml::value.
///
/// \returns Values of the same data type: `lhs.get() < rhs.get()` <br>
/// Values of different types: `lhs.type() < rhs.type()`
template <typename U>
[[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_;
else
return impl::node_type_of<T> < impl::node_type_of<U>;
}
/// \brief Less-than-or-equal-to operator.
///
/// \param lhs The LHS toml::value.
/// \param rhs The RHS toml::value.
///
/// \returns Values of the same data type: `lhs.get() <= rhs.get()` <br>
/// Values of different types: `lhs.type() <= rhs.type()`
template <typename U>
[[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_;
else
return impl::node_type_of<T> <= impl::node_type_of<U>;
}
/// \brief Greater-than operator.
///
/// \param lhs The LHS toml::value.
/// \param rhs The RHS toml::value.
///
/// \returns Values of the same data type: `lhs.get() > rhs.get()` <br>
/// Values of different types: `lhs.type() > rhs.type()`
template <typename U>
[[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_;
else
return impl::node_type_of<T> > impl::node_type_of<U>;
}
/// \brief Greater-than-or-equal-to operator.
///
/// \param lhs The LHS toml::value.
/// \param rhs The RHS toml::value.
///
/// \returns Values of the same data type: `lhs.get() >= rhs.get()` <br>
/// Values of different types: `lhs.type() >= rhs.type()`
template <typename U>
[[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_;
else
return impl::node_type_of<T> >= impl::node_type_of<U>;
}
};
value(const string_char*) -> value<string>;

View File

@ -78,6 +78,7 @@ def main():
source_text = Preprocessor()('toml.h')
source_text = re.sub('\r\n', '\n', source_text, 0, re.I | re.M) # convert windows newlines
source_text = re.sub('(?:\n[ \t]*//[/#!<]+[^\n]*)+\n', '\n', source_text, 0, re.I | re.M) # remove 'magic' comment blocks
source_text = re.sub('(?:///[<].*?)\n', '\n', source_text, 0, re.I | re.M) # remove inline doxy briefs
source_text = re.sub('\n(?:[ \t]*\n[ \t]*)+\n', '\n\n', source_text, 0, re.I | re.M) # remove double newlines
source_text = re.sub('([^ \t])[ \t]+\n', '\\1\n', source_text, 0, re.I | re.M) # remove trailing whitespace
for i in range(0, 5): # remove blank lines between simple one-liner definitions

View File

@ -202,6 +202,7 @@ void parse_expected_value(std::string_view value_str, const T& expected) noexcep
{
CHECK(tbl.size() == 1);
REQUIRE(tbl[S("val"sv)].as<impl::promoted<T>>());
REQUIRE(tbl[S("val"sv)].get()->type() == impl::node_type_of<T>);
CHECK(tbl[S("val"sv)].as<impl::promoted<T>>()->get() == expected);
CHECK(tbl[S("val"sv)].get()->source().begin == begin);
CHECK(tbl[S("val"sv)].get()->source().end == end);

982
toml.hpp

File diff suppressed because it is too large Load Diff