Skip to content

Commit 2663199

Browse files
committed
refactor: dom::Value tag_invoke
1 parent e919a61 commit 2663199

File tree

7 files changed

+413
-292
lines changed

7 files changed

+413
-292
lines changed

include/mrdocs/Dom/Value.hpp

Lines changed: 232 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -54,36 +54,6 @@ safeString(dom::Value const& str);
5454

5555
namespace dom {
5656

57-
/** Mapping traits to convert types into dom::Object.
58-
59-
This class should be specialized by any type that needs to be converted
60-
to/from a @ref dom::Object. For example:
61-
62-
@code
63-
template<>
64-
struct MappingTraits<MyStruct> {
65-
template <class IO>
66-
static void map(IO &io, MyStruct const& s)
67-
{
68-
io.map("name", s.name);
69-
io.map("size", s.size);
70-
io.map("age", s.age);
71-
}
72-
};
73-
@endcode
74-
*/
75-
template<class T>
76-
struct ToValue {
77-
// Value operator()(T const& o) const;
78-
};
79-
80-
/// Concept to determine if @ref ToValue is defined for a type T
81-
template <class T>
82-
concept HasToValue = requires(T const& o)
83-
{
84-
{ Value(std::declval<ToValue<T>>()(o)) } -> std::same_as<Value>;
85-
};
86-
8757
/** A variant container for any kind of Dom value.
8858
*/
8959
class MRDOCS_DECL
@@ -120,14 +90,12 @@ class MRDOCS_DECL
12090

12191
template<class F>
12292
requires
123-
function_traits_convertible_to_value<F> &&
124-
(!HasToValue<F>)
93+
function_traits_convertible_to_value<F>
12594
Value(F const& f)
12695
: Value(Function(f))
12796
{}
12897

12998
template<std::same_as<bool> Boolean>
130-
requires (!HasToValue<Boolean>)
13199
Value(Boolean const& b) noexcept
132100
: kind_(Kind::Boolean)
133101
, b_(b)
@@ -137,21 +105,18 @@ class MRDOCS_DECL
137105
template <std::integral T>
138106
requires
139107
(!std::same_as<T, bool>) &&
140-
(!std::same_as<T, char>) &&
141-
(!HasToValue<T>)
108+
(!std::same_as<T, char>)
142109
Value(T v) noexcept : Value(std::int64_t(v)) {}
143110

144111
template <std::floating_point T>
145-
requires (!HasToValue<T>)
146112
Value(T v) noexcept : Value(std::int64_t(v)) {}
147113

148114
Value(char c) noexcept : Value(std::string_view(&c, 1)) {}
149115

150116
template<class Enum>
151117
requires
152118
std::is_enum_v<Enum> &&
153-
(!std::same_as<Enum, dom::Kind>) &&
154-
(!HasToValue<Enum>)
119+
(!std::same_as<Enum, dom::Kind>)
155120
Value(Enum v) noexcept
156121
: Value(static_cast<std::underlying_type_t<Enum>>(v))
157122
{}
@@ -169,7 +134,6 @@ class MRDOCS_DECL
169134
}
170135

171136
template <std::convertible_to<String> StringLike>
172-
requires (!HasToValue<StringLike>)
173137
Value(StringLike const& s)
174138
: Value(String(s))
175139
{
@@ -194,12 +158,6 @@ class MRDOCS_DECL
194158
{
195159
}
196160

197-
template <HasToValue T>
198-
Value(T const& t)
199-
: Value(ToValue<T>{}(t))
200-
{
201-
}
202-
203161
Value& operator=(Value const& other);
204162
Value& operator=(Value&& other) noexcept;
205163

@@ -607,13 +565,241 @@ stringOrNull(
607565
{
608566
if(!s.empty())
609567
{
610-
return s;
568+
return {s};
611569
}
612570
return nullptr;
613571
}
614572

615573
//------------------------------------------------
616574

575+
/** Customization point tag.
576+
577+
This tag type is used by the function
578+
@ref dom::ValueFrom to select overloads
579+
of `tag_invoke`.
580+
581+
@note This type is empty; it has no members.
582+
583+
@see @ref dom::ValueFrom, @ref dom::ValueTo, @ref dom::ValueToTag,
584+
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf">
585+
tag_invoke: A general pattern for supporting customisable functions</a>
586+
*/
587+
struct ValueFromTag { };
588+
589+
/** Concept to determine if a type can be converted to a @ref dom::Value
590+
with a user-provided conversion.
591+
592+
This concept determines if the user-provided conversion is
593+
defined as:
594+
595+
@code
596+
void tag_invoke( ValueFromTag, dom::Value&, T );
597+
@endcode
598+
*/
599+
template<class T>
600+
concept HasValueFromWithoutContext = requires(
601+
Value& v,
602+
T const& t)
603+
{
604+
tag_invoke(ValueFromTag{}, v, t);
605+
};
606+
607+
/** Concept to determine if a type can be converted to a @ref dom::Value
608+
with a user-provided conversion.
609+
610+
This concept determines if the user-provided conversion is
611+
defined as:
612+
613+
@code
614+
void tag_invoke( ValueFromTag, dom::Value&, T, Context const& );
615+
@endcode
616+
*/
617+
template<class T, class Context>
618+
concept HasValueFromWithContext = requires(
619+
Value& v,
620+
T const& t,
621+
Context const& ctx)
622+
{
623+
tag_invoke(ValueFromTag{}, v, t, ctx);
624+
};
625+
626+
/** Determine if `T` can be converted to @ref dom::Value.
627+
628+
If `T` can be converted to @ref dom::Value via a
629+
call to @ref dom::ValueFrom, the static data member `value`
630+
is defined as `true`. Otherwise, `value` is
631+
defined as `false`.
632+
633+
@see @ref dom::ValueFrom
634+
*/
635+
template <class T, class Context>
636+
concept HasValueFrom =
637+
HasValueFromWithContext<T, Context> ||
638+
HasValueFromWithoutContext<T> ||
639+
std::constructible_from<Value, T>;
640+
641+
/** Determine if ` T` can be converted to @ref dom::Value
642+
without a context.
643+
644+
This concept determines if there is a user-provided
645+
conversion to @ref dom::Value that does not require
646+
a context or if @ref dom::Value has a constructor
647+
that can be used to convert `T` to a @ref dom::Value.
648+
*/
649+
template <class T>
650+
concept HasStandaloneValueFrom =
651+
HasValueFromWithoutContext<T> ||
652+
std::constructible_from<Value, T>;
653+
654+
/** Convert an object of type `T` to @ref dom::Value.
655+
656+
This function attempts to convert an object
657+
of type `T` to @ref dom::Value using
658+
659+
@li a user-provided overload of `tag_invoke`.
660+
661+
@li one of @ref dom::Value's constructors,
662+
663+
Conversion of user-provided types is done by calling an overload of
664+
`tag_invoke` found by argument-dependent lookup. Its signature should
665+
be similar to:
666+
667+
@code
668+
void tag_invoke( ValueFromTag, dom::Value&, T, Context const& );
669+
@endcode
670+
671+
or
672+
673+
@code
674+
void tag_invoke( ValueFromTag, dom::Value&, T );
675+
@endcode
676+
677+
The overloads are checked for existence in that order and the first that
678+
matches will be selected.
679+
680+
The `ctx` argument can be used either as a tag type to provide conversions
681+
for third-party types, or to pass extra data to the conversion function.
682+
683+
@par Exception Safety
684+
Strong guarantee.
685+
686+
@tparam T The type of the object to convert.
687+
688+
@tparam Context The type of context passed to the conversion function.
689+
690+
@param t The object to convert.
691+
692+
@param ctx Context passed to the conversion function.
693+
694+
@param jv @ref dom::Value out parameter.
695+
696+
@see @ref dom::ValueFromTag, @ref dom::ValueTo,
697+
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf">
698+
tag_invoke: A general pattern for supporting customisable functions</a>
699+
*/
700+
template <class Context, HasValueFrom<Context> T>
701+
void
702+
ValueFrom(
703+
T&& t,
704+
Context const& ctx,
705+
Value& v)
706+
{
707+
using BT = std::remove_cvref_t<T>;
708+
if constexpr (HasValueFromWithContext<BT, Context>)
709+
{
710+
tag_invoke(ValueFromTag{}, v, static_cast<T&&>(t), ctx);
711+
}
712+
else {
713+
ValueFrom(static_cast<T&&>(t), v);
714+
}
715+
}
716+
717+
/** Convert an object of type `T` to @ref dom::Value.
718+
719+
This function attempts to convert an object
720+
of type `T` to @ref dom::Value using
721+
722+
@li a user-provided overload of `tag_invoke`.
723+
724+
@li one of @ref dom::Value's constructors,
725+
726+
Conversion of other types is done by calling an overload of `tag_invoke`
727+
found by argument-dependent lookup. Its signature should be similar to:
728+
729+
@code
730+
void tag_invoke( ValueFromTag, dom::Value&, T );
731+
@endcode
732+
733+
@par Exception Safety
734+
Strong guarantee.
735+
736+
@tparam T The type of the object to convert.
737+
738+
@param t The object to convert.
739+
740+
@param jv @ref dom::Value out parameter.
741+
742+
@see @ref dom::ValueFromTag, @ref dom::ValueTo,
743+
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf">
744+
tag_invoke: A general pattern for supporting customisable functions</a>
745+
*/
746+
template<class T>
747+
requires HasStandaloneValueFrom<T>
748+
void
749+
ValueFrom(
750+
T&& t,
751+
Value& v)
752+
{
753+
using BT = std::remove_cvref_t<T>;
754+
if constexpr (HasValueFromWithoutContext<BT>)
755+
{
756+
tag_invoke(ValueFromTag{}, v, static_cast<T&&>(t));
757+
}
758+
else /* if constexpr (std::constructible_from<Value, T>) */
759+
{
760+
v = Value(static_cast<T&&>(t));
761+
}
762+
}
763+
764+
/** Convert an object of type `T` to @ref dom::Value.
765+
766+
This function attempts to convert an object
767+
of type `T` to @ref dom::Value using
768+
769+
@li a user-provided overload of `tag_invoke`.
770+
771+
@li one of @ref dom::Value's constructors,
772+
773+
Conversion of other types is done by calling an overload of `tag_invoke`
774+
found by argument-dependent lookup. Its signature should be similar to:
775+
776+
@code
777+
void tag_invoke( ValueFromTag, dom::Value&, T );
778+
@endcode
779+
780+
@par Exception Safety
781+
Strong guarantee.
782+
783+
@tparam T The type of the object to convert.
784+
785+
@param t The object to convert.
786+
787+
@return @ref dom::Value out parameter.
788+
789+
@see @ref dom::ValueFromTag, @ref dom::ValueTo,
790+
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf">
791+
tag_invoke: A general pattern for supporting customisable functions</a>
792+
*/
793+
template<class T>
794+
requires HasStandaloneValueFrom<T>
795+
Value
796+
ValueFrom(T&& t)
797+
{
798+
dom::Value v;
799+
ValueFrom(static_cast<T&&>(t), v);
800+
return v;
801+
}
802+
617803
} // dom
618804

619805
template <std::convertible_to<std::string_view> SV>

0 commit comments

Comments
 (0)