Skip to content

Commit

Permalink
Don't derive nlohmann::optional from std::optional
Browse files Browse the repository at this point in the history
  • Loading branch information
karzhenkov committed Jan 7, 2023
1 parent 57f0f50 commit 3854934
Show file tree
Hide file tree
Showing 5 changed files with 372 additions and 52 deletions.
2 changes: 1 addition & 1 deletion include/nlohmann/detail/conversions/from_json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ void from_json(const BasicJsonType& j, std::optional<T>& opt)
}

template<typename BasicJsonType, typename T>
void from_json(const BasicJsonType& j, nlohmann::optional<T>& opt)
void from_json(const BasicJsonType& j, optional<T>& opt)
{
if (j.is_null())
{
Expand Down
7 changes: 7 additions & 0 deletions include/nlohmann/detail/conversions/to_json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,13 @@ void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept
j = nullptr;
}
}

template<typename BasicJsonType, typename T,
enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>
void to_json(BasicJsonType& j, const optional<T>& opt) noexcept
{
to_json(j, opt.base());
}
#endif

template<typename BasicJsonType, typename T,
Expand Down
201 changes: 177 additions & 24 deletions include/nlohmann/optional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
#include <optional>
#include <utility>

namespace nlohmann
{
NLOHMANN_JSON_NAMESPACE_BEGIN

template <typename T>
class optional : public std::optional<T>
class optional
{
// *INDENT-OFF*

using base_type = std::optional<T>;
using value_type = T;

template <typename U, typename = optional>
struct has_conversion_operator : std::false_type { };
Expand Down Expand Up @@ -67,17 +67,29 @@ class optional : public std::optional<T>

struct noexcept_fix_t {};

base_type base_value;

public:

const base_type& base() const
base_type& base() &
{
return base_value;
}

const base_type& base() const &
{
return *this;
return base_value;
}

base_type&& base() &&
{
return std::move(base_value);
}

constexpr optional() noexcept(noexcept(std::optional<noexcept_fix_t>())) = default;

constexpr optional(std::nullopt_t /* unused */) noexcept
: base_type(std::nullopt)
: base_value(std::nullopt)
{
}

Expand All @@ -86,7 +98,7 @@ class optional : public std::optional<T>
noexcept(noexcept(
base_type(std::forward<U>(value).operator optional())
)) :
base_type(std::forward<U>(value).operator optional())
base_value(std::forward<U>(value).operator optional())
{
}

Expand All @@ -95,7 +107,7 @@ class optional : public std::optional<T>
noexcept(noexcept(
base_type(std::forward<U>(value))
)) :
base_type(std::forward<U>(value))
base_value(std::forward<U>(value))
{
}

Expand All @@ -105,7 +117,7 @@ class optional : public std::optional<T>
noexcept(noexcept(
base_type(std::forward<U>(value))
)) :
base_type(std::forward<U>(value))
base_value(std::forward<U>(value))
{
}

Expand All @@ -115,7 +127,7 @@ class optional : public std::optional<T>
noexcept(noexcept(
base_type(std::in_place, std::forward<U>(u), std::forward<Args>(args)...)
)) :
base_type(std::in_place, std::forward<U>(u), std::forward<Args>(args)...)
base_value(std::in_place, std::forward<U>(u), std::forward<Args>(args)...)
{
}

Expand All @@ -125,49 +137,190 @@ class optional : public std::optional<T>
noexcept(noexcept(
base_type(std::in_place, u, std::forward<Args>(args)...)
)) :
base_type(std::in_place, u, std::forward<Args>(args)...)
base_value(std::in_place, u, std::forward<Args>(args)...)
{
}

constexpr T& operator *() & noexcept { return *base_value; }
constexpr const T& operator *() const& noexcept { return *base_value; }

constexpr T&& operator *() && noexcept { return static_cast<T&&>(*base_value); }
constexpr const T&& operator *() const&& noexcept { return static_cast<const T&&>(*base_value); }

constexpr T* operator ->() noexcept { return base_value.operator ->(); }
constexpr const T* operator ->() const noexcept { return base_value.operator ->(); }

operator base_type& () & { return base_value; }
operator base_type&& () && { return std::move(base_value); }

// *INDENT-ON*
};

namespace detail::opt
{

template <typename T> const T& cmp_val(const T& v)
{
return v;
}
template <typename T> const std::optional<T>& cmp_val(const optional<T>& v)
{
return v.base();
}
template <typename T> void cmp_val(const std::optional<T>& v) = delete;

} // namespace detail::opt

#ifdef JSON_HAS_CPP_20

template <typename T, typename U>
auto operator == (const optional<T>& lhs, const U& rhs) ->
decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs))
{
return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs);
}

// *INDENT-OFF*

template <typename T, typename U>
auto operator <=> (const optional<T>& lhs, const U& rhs) ->
decltype(detail::opt::cmp_val(lhs) <=> detail::opt::cmp_val(rhs))
{
return detail::opt::cmp_val(lhs) <=> detail::opt::cmp_val(rhs);
}

// *INDENT-ON*

#else // JSON_HAS_CPP_20

template<class T, class U>
constexpr bool operator == (const optional<T>& lhs, const optional<U>& rhs)
constexpr auto operator == (const optional<T>& lhs, const optional<U>& rhs) ->
decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs))
{
return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs);
}

template <class T, class U>
constexpr auto operator == (const optional<T>& lhs, const U& rhs) ->
decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs))
{
return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs);
}

template <class T, class U>
constexpr auto operator == (const T& lhs, const optional<U>& rhs) ->
decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs))
{
return lhs.base() == rhs.base();
return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs);
}

template<class T, class U>
constexpr bool operator != (const optional<T>& lhs, const optional<U>& rhs)
constexpr auto operator != (const optional<T>& lhs, const optional<U>& rhs) ->
decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs))
{
return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs);
}

template <class T, class U>
constexpr auto operator != (const optional<T>& lhs, const U& rhs) ->
decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs))
{
return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs);
}

template <class T, class U>
constexpr auto operator != (const T& lhs, const optional<U>& rhs) ->
decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs))
{
return lhs.base() != rhs.base();
return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs);
}

template<class T, class U>
constexpr bool operator < (const optional<T>& lhs, const optional<U>& rhs)
constexpr auto operator < (const optional<T>& lhs, const optional<U>& rhs) ->
decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs))
{
return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs);
}

template <class T, class U>
constexpr auto operator < (const optional<T>& lhs, const U& rhs) ->
decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs))
{
return lhs.base() < rhs.base();
return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs);
}

template <class T, class U>
constexpr auto operator < (const T& lhs, const optional<U>& rhs) ->
decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs))
{
return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs);
}

template<class T, class U>
constexpr bool operator <= (const optional<T>& lhs, const optional<U>& rhs)
constexpr auto operator <= (const optional<T>& lhs, const optional<U>& rhs) ->
decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs))
{
return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs);
}

template <class T, class U>
constexpr auto operator <= (const optional<T>& lhs, const U& rhs) ->
decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs))
{
return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs);
}

template <class T, class U>
constexpr auto operator <= (const T& lhs, const optional<U>& rhs) ->
decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs))
{
return lhs.base() <= rhs.base();
return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs);
}

template<class T, class U>
constexpr bool operator > (const optional<T>& lhs, const optional<U>& rhs)
constexpr auto operator > (const optional<T>& lhs, const optional<U>& rhs) ->
decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs))
{
return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs);
}

template <class T, class U>
constexpr auto operator > (const optional<T>& lhs, const U& rhs) ->
decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs))
{
return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs);
}

template <class T, class U>
constexpr auto operator > (const T& lhs, const optional<U>& rhs) ->
decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs))
{
return lhs.base() > rhs.base();
return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs);
}

template<class T, class U>
constexpr bool operator >= (const optional<T>& lhs, const optional<U>& rhs)
constexpr auto operator >= (const optional<T>& lhs, const optional<U>& rhs) ->
decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs))
{
return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs);
}

template <class T, class U>
constexpr auto operator >= (const optional<T>& lhs, const U& rhs) ->
decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs))
{
return lhs.base() >= rhs.base();
return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs);
}

} // namespace nlohmann
template <class T, class U>
constexpr auto operator >= (const T& lhs, const optional<U>& rhs) ->
decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs))
{
return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs);
}

#endif // JSON_HAS_CPP_20

NLOHMANN_JSON_NAMESPACE_END

#endif // JSON_HAS_CPP_17
Loading

0 comments on commit 3854934

Please sign in to comment.