@@ -295,19 +295,25 @@ struct FieldParams : SelectionSetParams
295
295
// Field accessors may return either a result of T, an awaitable of T, or a std::future<T>, so at
296
296
// runtime the implementer may choose to return by value or defer/parallelize expensive operations
297
297
// by returning an async future or an awaitable coroutine.
298
+ //
299
+ // If the overhead of conversion to response::Value is too expensive, scalar type field accessors
300
+ // can store and return a std::shared_ptr<const response::Value> directly. However, be careful using
301
+ // this mechanism, because there's no validation that the response::Value you return matches the
302
+ // GraphQL type of this field. It will be inserted directly into the response document without
303
+ // modification.
298
304
template <typename T>
299
- class FieldResult
305
+ class AwaitableScalar
300
306
{
301
307
public:
302
308
template <typename U>
303
- FieldResult (U&& value)
309
+ AwaitableScalar (U&& value)
304
310
: _value { std::forward<U>(value) }
305
311
{
306
312
}
307
313
308
314
struct promise_type
309
315
{
310
- FieldResult <T> get_return_object () noexcept
316
+ AwaitableScalar <T> get_return_object () noexcept
311
317
{
312
318
return { _promise.get_future () };
313
319
}
@@ -421,6 +427,108 @@ class FieldResult
421
427
std::variant<T, std::future<T>, std::shared_ptr<const response::Value>> _value;
422
428
};
423
429
430
+ // Field accessors may return either a result of T, an awaitable of T, or a std::future<T>, so at
431
+ // runtime the implementer may choose to return by value or defer/parallelize expensive operations
432
+ // by returning an async future or an awaitable coroutine.
433
+ template <typename T>
434
+ class AwaitableObject
435
+ {
436
+ public:
437
+ template <typename U>
438
+ AwaitableObject (U&& value)
439
+ : _value { std::forward<U>(value) }
440
+ {
441
+ }
442
+
443
+ struct promise_type
444
+ {
445
+ AwaitableObject<T> get_return_object () noexcept
446
+ {
447
+ return { _promise.get_future () };
448
+ }
449
+
450
+ coro::suspend_never initial_suspend () const noexcept
451
+ {
452
+ return {};
453
+ }
454
+
455
+ coro::suspend_never final_suspend () const noexcept
456
+ {
457
+ return {};
458
+ }
459
+
460
+ void return_value (const T& value) noexcept (std::is_nothrow_copy_constructible_v<T>)
461
+ {
462
+ _promise.set_value (value);
463
+ }
464
+
465
+ void return_value (T&& value) noexcept (std::is_nothrow_move_constructible_v<T>)
466
+ {
467
+ _promise.set_value (std::move (value));
468
+ }
469
+
470
+ void unhandled_exception () noexcept
471
+ {
472
+ _promise.set_exception (std::current_exception ());
473
+ }
474
+
475
+ private:
476
+ std::promise<T> _promise;
477
+ };
478
+
479
+ bool await_ready () const noexcept
480
+ {
481
+ return std::visit (
482
+ [](const auto & value) noexcept {
483
+ using value_type = std::decay_t <decltype (value)>;
484
+
485
+ if constexpr (std::is_same_v<value_type, T>)
486
+ {
487
+ return true ;
488
+ }
489
+ else if constexpr (std::is_same_v<value_type, std::future<T>>)
490
+ {
491
+ using namespace std ::literals;
492
+
493
+ return value.wait_for (0s) != std::future_status::timeout;
494
+ }
495
+ },
496
+ _value);
497
+ }
498
+
499
+ void await_suspend (coro::coroutine_handle<> h) const
500
+ {
501
+ std::thread (
502
+ [this ](coro::coroutine_handle<> h) noexcept {
503
+ std::get<std::future<T>>(_value).wait ();
504
+ h.resume ();
505
+ },
506
+ std::move (h))
507
+ .detach ();
508
+ }
509
+
510
+ T await_resume ()
511
+ {
512
+ return std::visit (
513
+ [](auto && value) -> T {
514
+ using value_type = std::decay_t <decltype (value)>;
515
+
516
+ if constexpr (std::is_same_v<value_type, T>)
517
+ {
518
+ return T { std::move (value) };
519
+ }
520
+ else if constexpr (std::is_same_v<value_type, std::future<T>>)
521
+ {
522
+ return value.get ();
523
+ }
524
+ },
525
+ std::move (_value));
526
+ }
527
+
528
+ private:
529
+ std::variant<T, std::future<T>> _value;
530
+ };
531
+
424
532
// Fragments are referenced by name and have a single type condition (except for inline
425
533
// fragments, where the type condition is common but optional). They contain a set of fields
426
534
// (with optional aliases and sub-selections) and potentially references to other fragments.
@@ -708,7 +816,8 @@ struct ModifiedResult
708
816
std::vector<typename ResultTraits<U, Other...>::type>,
709
817
typename std::conditional_t <std::is_base_of_v<Object, U>, std::shared_ptr<U>, U>>>;
710
818
711
- using future_type = FieldResult<type>;
819
+ using future_type = typename std::conditional_t <std::is_base_of_v<Object, U>,
820
+ AwaitableObject<type>, AwaitableScalar<type>>;
712
821
};
713
822
714
823
template <typename U>
@@ -718,7 +827,7 @@ struct ModifiedResult
718
827
typename std::conditional_t <std::is_base_of_v<Object, U>, std::shared_ptr<U>, U>;
719
828
720
829
using future_type = typename std::conditional_t <std::is_base_of_v<Object, U>,
721
- FieldResult <std::shared_ptr<Object>>, FieldResult <type>>;
830
+ AwaitableObject <std::shared_ptr<Object>>, AwaitableScalar <type>>;
722
831
};
723
832
724
833
// Convert a single value of the specified type to JSON.
@@ -730,20 +839,13 @@ struct ModifiedResult
730
839
static typename std::enable_if_t <TypeModifier::None == Modifier && sizeof ...(Other) == 0
731
840
&& !std::is_same_v<Object, Type> && std::is_base_of_v<Object, Type>,
732
841
AwaitableResolver>
733
- convert (FieldResult <typename ResultTraits<Type>::type> result, ResolverParams params)
842
+ convert (AwaitableObject <typename ResultTraits<Type>::type> result, ResolverParams params)
734
843
{
735
844
// Call through to the Object specialization with a static_pointer_cast for subclasses of
736
845
// Object.
737
846
static_assert (std::is_same_v<std::shared_ptr<Type>, typename ResultTraits<Type>::type>,
738
847
" this is the derived object type" );
739
848
740
- auto value = result.get_value ();
741
-
742
- if (value)
743
- {
744
- co_return ResolverResult { response::Value { std::shared_ptr { std::move (value) } } };
745
- }
746
-
747
849
co_await params.launch ;
748
850
749
851
auto awaitedResult = co_await ModifiedResult<Object>::convert (
@@ -772,13 +874,6 @@ struct ModifiedResult
772
874
convert (
773
875
typename ResultTraits<Type, Modifier, Other...>::future_type result, ResolverParams params)
774
876
{
775
- auto value = result.get_value ();
776
-
777
- if (value)
778
- {
779
- co_return ResolverResult { response::Value { std::shared_ptr { std::move (value) } } };
780
- }
781
-
782
877
co_await params.launch ;
783
878
784
879
auto awaitedResult = co_await std::move (result);
@@ -806,11 +901,15 @@ struct ModifiedResult
806
901
typename ResultTraits<Type, Modifier, Other...>::type>,
807
902
" this is the optional version" );
808
903
809
- auto value = result.get_value ();
810
-
811
- if (value)
904
+ if constexpr (!std::is_base_of_v<Object, Type>)
812
905
{
813
- co_return ResolverResult { response::Value { std::shared_ptr { std::move (value) } } };
906
+ auto value = result.get_value ();
907
+
908
+ if (value)
909
+ {
910
+ co_return ResolverResult { response::Value {
911
+ std::shared_ptr { std::move (value) } } };
912
+ }
814
913
}
815
914
816
915
co_await params.launch ;
@@ -833,11 +932,15 @@ struct ModifiedResult
833
932
static typename std::enable_if_t <TypeModifier::List == Modifier, AwaitableResolver> convert (
834
933
typename ResultTraits<Type, Modifier, Other...>::future_type result, ResolverParams params)
835
934
{
836
- auto value = result.get_value ();
837
-
838
- if (value)
935
+ if constexpr (!std::is_base_of_v<Object, Type>)
839
936
{
840
- co_return ResolverResult { response::Value { std::shared_ptr { std::move (value) } } };
937
+ auto value = result.get_value ();
938
+
939
+ if (value)
940
+ {
941
+ co_return ResolverResult { response::Value {
942
+ std::shared_ptr { std::move (value) } } };
943
+ }
841
944
}
842
945
843
946
std::vector<AwaitableResolver> children;
@@ -988,25 +1091,25 @@ using ObjectResult = ModifiedResult<Object>;
988
1091
// Export all of the built-in converters
989
1092
template <>
990
1093
GRAPHQLSERVICE_EXPORT AwaitableResolver ModifiedResult<int >::convert(
991
- FieldResult <int > result, ResolverParams params);
1094
+ AwaitableScalar <int > result, ResolverParams params);
992
1095
template <>
993
1096
GRAPHQLSERVICE_EXPORT AwaitableResolver ModifiedResult<double >::convert(
994
- FieldResult <double > result, ResolverParams params);
1097
+ AwaitableScalar <double > result, ResolverParams params);
995
1098
template <>
996
1099
GRAPHQLSERVICE_EXPORT AwaitableResolver ModifiedResult<std::string>::convert(
997
- FieldResult <std::string> result, ResolverParams params);
1100
+ AwaitableScalar <std::string> result, ResolverParams params);
998
1101
template <>
999
1102
GRAPHQLSERVICE_EXPORT AwaitableResolver ModifiedResult<bool >::convert(
1000
- FieldResult <bool > result, ResolverParams params);
1103
+ AwaitableScalar <bool > result, ResolverParams params);
1001
1104
template <>
1002
1105
GRAPHQLSERVICE_EXPORT AwaitableResolver ModifiedResult<response::IdType>::convert(
1003
- FieldResult <response::IdType> result, ResolverParams params);
1106
+ AwaitableScalar <response::IdType> result, ResolverParams params);
1004
1107
template <>
1005
1108
GRAPHQLSERVICE_EXPORT AwaitableResolver ModifiedResult<response::Value>::convert(
1006
- FieldResult <response::Value> result, ResolverParams params);
1109
+ AwaitableScalar <response::Value> result, ResolverParams params);
1007
1110
template <>
1008
1111
GRAPHQLSERVICE_EXPORT AwaitableResolver ModifiedResult<Object>::convert(
1009
- FieldResult <std::shared_ptr<Object>> result, ResolverParams params);
1112
+ AwaitableObject <std::shared_ptr<Object>> result, ResolverParams params);
1010
1113
#endif // GRAPHQL_DLLEXPORTS
1011
1114
1012
1115
// Subscription callbacks receive the response::Value representing the result of evaluating the
0 commit comments