Skip to content

Commit

Permalink
Merge pull request #247 from wravery/input-copy-constructors
Browse files Browse the repository at this point in the history
Generate input type copy constructors
  • Loading branch information
wravery committed May 8, 2022
2 parents 31af73f + b07ad02 commit 681c08d
Show file tree
Hide file tree
Showing 25 changed files with 1,895 additions and 114 deletions.
61 changes: 54 additions & 7 deletions include/graphqlservice/GraphQLClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,17 @@ template <typename Type>
return false;
}

// Special-case an innermost nullable INPUT_OBJECT type.
template <TypeModifier... Other>
[[nodiscard]] constexpr bool onlyNoneModifiers() noexcept
{
return (... && (Other == TypeModifier::None));
}

// Serialize variable input values with chained type modifiers which add nullable or list wrappers.
template <typename Type>
struct ModifiedVariable
{
// Special-case an innermost nullable INPUT_OBJECT type.
template <TypeModifier... Other>
[[nodiscard]] static constexpr bool onlyNoneModifiers() noexcept
{
return (... && (Other == TypeModifier::None));
}

// Peel off modifiers until we get to the underlying type.
template <typename U, TypeModifier Modifier = TypeModifier::None, TypeModifier... Other>
struct VariableTraits
Expand Down Expand Up @@ -152,6 +152,53 @@ struct ModifiedVariable

return result;
}

// Peel off the none modifier. If it's included, it should always be last in the list.
template <TypeModifier Modifier = TypeModifier::None, TypeModifier... Other>
[[nodiscard]] static
typename std::enable_if_t<TypeModifier::None == Modifier && sizeof...(Other) == 0, Type>
duplicate(const Type& value)
{
// Just copy the value.
return Type { value };
}

// Peel off nullable modifiers.
template <TypeModifier Modifier, TypeModifier... Other>
[[nodiscard]] static typename std::enable_if_t<TypeModifier::Nullable == Modifier,
typename VariableTraits<Type, Modifier, Other...>::type>
duplicate(const typename VariableTraits<Type, Modifier, Other...>::type& nullableValue)
{
typename VariableTraits<Type, Modifier, Other...>::type result {};

if (nullableValue)
{
if constexpr (isInputType<Type>() && onlyNoneModifiers<Other...>())
{
// Special case duplicating the std::unique_ptr.
result = std::make_unique<Type>(Type { *nullableValue });
}
else
{
result = duplicate<Other...>(*nullableValue);
}
}

return result;
}

// Peel off list modifiers.
template <TypeModifier Modifier, TypeModifier... Other>
[[nodiscard]] static typename std::enable_if_t<TypeModifier::List == Modifier,
typename VariableTraits<Type, Modifier, Other...>::type>
duplicate(const typename VariableTraits<Type, Modifier, Other...>::type& listValue)
{
typename VariableTraits<Type, Modifier, Other...>::type result(listValue.size());

std::transform(listValue.cbegin(), listValue.cend(), result.begin(), duplicate<Other...>);

return result;
}
};

// Convenient type aliases for testing, generated code won't actually use these. These are also
Expand Down
3 changes: 3 additions & 0 deletions include/graphqlservice/GraphQLResponse.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ template <>
GRAPHQLRESPONSE_EXPORT IdType::IdType(
typename ByteData::const_pointer begin, typename ByteData::const_pointer end);
template <>
GRAPHQLRESPONSE_EXPORT IdType::IdType(
typename ByteData::pointer begin, typename ByteData::pointer end);
template <>
GRAPHQLRESPONSE_EXPORT const IdType::ByteData& IdType::get<IdType::ByteData>() const;
template <>
GRAPHQLRESPONSE_EXPORT const IdType::OpaqueString& IdType::get<IdType::OpaqueString>() const;
Expand Down
61 changes: 54 additions & 7 deletions include/graphqlservice/GraphQLService.h
Original file line number Diff line number Diff line change
Expand Up @@ -599,20 +599,20 @@ template <typename Type>
return false;
}

// Special-case an innermost nullable INPUT_OBJECT type.
template <TypeModifier... Other>
[[nodiscard]] constexpr bool onlyNoneModifiers() noexcept
{
return (... && (Other == TypeModifier::None));
}

// Extract individual arguments with chained type modifiers which add nullable or list wrappers.
// If the argument is not optional, use require and let it throw a schema_exception when the
// argument is missing or not the correct type. If it's optional, use find and check the second
// element in the pair to see if it was found or if you just got the default value for that type.
template <typename Type>
struct ModifiedArgument
{
// Special-case an innermost nullable INPUT_OBJECT type.
template <TypeModifier... Other>
[[nodiscard]] static constexpr bool onlyNoneModifiers() noexcept
{
return (... && (Other == TypeModifier::None));
}

// Peel off modifiers until we get to the underlying type.
template <typename U, TypeModifier Modifier = TypeModifier::None, TypeModifier... Other>
struct ArgumentTraits
Expand Down Expand Up @@ -747,6 +747,53 @@ struct ModifiedArgument
return { typename ArgumentTraits<Type, Modifier, Other...>::type {}, false };
}
}

// Peel off the none modifier. If it's included, it should always be last in the list.
template <TypeModifier Modifier = TypeModifier::None, TypeModifier... Other>
[[nodiscard]] static
typename std::enable_if_t<TypeModifier::None == Modifier && sizeof...(Other) == 0, Type>
duplicate(const Type& value)
{
// Just copy the value.
return Type { value };
}

// Peel off nullable modifiers.
template <TypeModifier Modifier, TypeModifier... Other>
[[nodiscard]] static typename std::enable_if_t<TypeModifier::Nullable == Modifier,
typename ArgumentTraits<Type, Modifier, Other...>::type>
duplicate(const typename ArgumentTraits<Type, Modifier, Other...>::type& nullableValue)
{
typename ArgumentTraits<Type, Modifier, Other...>::type result {};

if (nullableValue)
{
if constexpr (isInputType<Type>() && onlyNoneModifiers<Other...>())
{
// Special case duplicating the std::unique_ptr.
result = std::make_unique<Type>(Type { *nullableValue });
}
else
{
result = duplicate<Other...>(*nullableValue);
}
}

return result;
}

// Peel off list modifiers.
template <TypeModifier Modifier, TypeModifier... Other>
[[nodiscard]] static typename std::enable_if_t<TypeModifier::List == Modifier,
typename ArgumentTraits<Type, Modifier, Other...>::type>
duplicate(const typename ArgumentTraits<Type, Modifier, Other...>::type& listValue)
{
typename ArgumentTraits<Type, Modifier, Other...>::type result(listValue.size());

std::transform(listValue.cbegin(), listValue.cend(), result.begin(), duplicate<Other...>);

return result;
}
};

// Convenient type aliases for testing, generated code won't actually use these. These are also
Expand Down
1 change: 1 addition & 0 deletions samples/client/benchmark/BenchmarkClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <sstream>
#include <stdexcept>
#include <string_view>
#include <utility>

using namespace std::literals;

Expand Down
54 changes: 48 additions & 6 deletions samples/client/multiple/MultipleQueriesClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <sstream>
#include <stdexcept>
#include <string_view>
#include <utility>

using namespace std::literals;

Expand Down Expand Up @@ -125,6 +126,53 @@ static const std::array<std::string_view, 4> s_namesTaskState = {
"Unassigned"sv,
};

CompleteTaskInput::CompleteTaskInput(
response::IdType idArg,
std::optional<TaskState> testTaskStateArg,
std::optional<bool> isCompleteArg,
std::optional<std::string> clientMutationIdArg) noexcept
: id { std::move(idArg) }
, testTaskState { std::move(testTaskStateArg) }
, isComplete { std::move(isCompleteArg) }
, clientMutationId { std::move(clientMutationIdArg) }
{
}

CompleteTaskInput::CompleteTaskInput(const CompleteTaskInput& other)
: id { ModifiedVariable<response::IdType>::duplicate(other.id) }
, testTaskState { ModifiedVariable<TaskState>::duplicate<TypeModifier::Nullable>(other.testTaskState) }
, isComplete { ModifiedVariable<bool>::duplicate<TypeModifier::Nullable>(other.isComplete) }
, clientMutationId { ModifiedVariable<std::string>::duplicate<TypeModifier::Nullable>(other.clientMutationId) }
{
}

CompleteTaskInput::CompleteTaskInput(CompleteTaskInput&& other) noexcept
: id { std::move(other.id) }
, testTaskState { std::move(other.testTaskState) }
, isComplete { std::move(other.isComplete) }
, clientMutationId { std::move(other.clientMutationId) }
{
}

CompleteTaskInput& CompleteTaskInput::operator=(const CompleteTaskInput& other)
{
CompleteTaskInput value { other };

std::swap(*this, value);

return *this;
}

CompleteTaskInput& CompleteTaskInput::operator=(CompleteTaskInput&& other) noexcept
{
id = std::move(other.id);
testTaskState = std::move(other.testTaskState);
isComplete = std::move(other.isComplete);
clientMutationId = std::move(other.clientMutationId);

return *this;
}

} // namespace multiple

using namespace multiple;
Expand Down Expand Up @@ -585,12 +633,6 @@ Response parseResponse(response::Value&& response)

} // namespace query::Miscellaneous

template <>
constexpr bool isInputType<CompleteTaskInput>() noexcept
{
return true;
}

template <>
response::Value ModifiedVariable<TaskState>::serialize(TaskState&& value)
{
Expand Down
17 changes: 17 additions & 0 deletions samples/client/multiple/MultipleQueriesClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,17 @@ enum class [[nodiscard]] TaskState

struct [[nodiscard]] CompleteTaskInput
{
explicit CompleteTaskInput(
response::IdType idArg = response::IdType {},
std::optional<TaskState> testTaskStateArg = std::optional<TaskState> {},
std::optional<bool> isCompleteArg = std::optional<bool> {},
std::optional<std::string> clientMutationIdArg = std::optional<std::string> {}) noexcept;
CompleteTaskInput(const CompleteTaskInput& other);
CompleteTaskInput(CompleteTaskInput&& other) noexcept;

CompleteTaskInput& operator=(const CompleteTaskInput& other);
CompleteTaskInput& operator=(CompleteTaskInput&& other) noexcept;

response::IdType id {};
std::optional<TaskState> testTaskState {};
std::optional<bool> isComplete {};
Expand All @@ -135,6 +146,12 @@ struct [[nodiscard]] CompleteTaskInput

} // namespace multiple

template <>
constexpr bool isInputType<multiple::CompleteTaskInput>() noexcept
{
return true;
}

namespace query::Appointments {

using multiple::GetRequestText;
Expand Down
52 changes: 47 additions & 5 deletions samples/client/mutate/MutateClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <sstream>
#include <stdexcept>
#include <string_view>
#include <utility>

using namespace std::literals;

Expand Down Expand Up @@ -58,16 +59,57 @@ static const std::array<std::string_view, 4> s_namesTaskState = {
"Unassigned"sv,
};

} // namespace mutate
CompleteTaskInput::CompleteTaskInput(
response::IdType idArg,
std::optional<TaskState> testTaskStateArg,
std::optional<bool> isCompleteArg,
std::optional<std::string> clientMutationIdArg) noexcept
: id { std::move(idArg) }
, testTaskState { std::move(testTaskStateArg) }
, isComplete { std::move(isCompleteArg) }
, clientMutationId { std::move(clientMutationIdArg) }
{
}

using namespace mutate;
CompleteTaskInput::CompleteTaskInput(const CompleteTaskInput& other)
: id { ModifiedVariable<response::IdType>::duplicate(other.id) }
, testTaskState { ModifiedVariable<TaskState>::duplicate<TypeModifier::Nullable>(other.testTaskState) }
, isComplete { ModifiedVariable<bool>::duplicate<TypeModifier::Nullable>(other.isComplete) }
, clientMutationId { ModifiedVariable<std::string>::duplicate<TypeModifier::Nullable>(other.clientMutationId) }
{
}

template <>
constexpr bool isInputType<CompleteTaskInput>() noexcept
CompleteTaskInput::CompleteTaskInput(CompleteTaskInput&& other) noexcept
: id { std::move(other.id) }
, testTaskState { std::move(other.testTaskState) }
, isComplete { std::move(other.isComplete) }
, clientMutationId { std::move(other.clientMutationId) }
{
}

CompleteTaskInput& CompleteTaskInput::operator=(const CompleteTaskInput& other)
{
return true;
CompleteTaskInput value { other };

std::swap(*this, value);

return *this;
}

CompleteTaskInput& CompleteTaskInput::operator=(CompleteTaskInput&& other) noexcept
{
id = std::move(other.id);
testTaskState = std::move(other.testTaskState);
isComplete = std::move(other.isComplete);
clientMutationId = std::move(other.clientMutationId);

return *this;
}

} // namespace mutate

using namespace mutate;

template <>
response::Value ModifiedVariable<TaskState>::serialize(TaskState&& value)
{
Expand Down
17 changes: 17 additions & 0 deletions samples/client/mutate/MutateClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ enum class [[nodiscard]] TaskState

struct [[nodiscard]] CompleteTaskInput
{
explicit CompleteTaskInput(
response::IdType idArg = response::IdType {},
std::optional<TaskState> testTaskStateArg = std::optional<TaskState> {},
std::optional<bool> isCompleteArg = std::optional<bool> {},
std::optional<std::string> clientMutationIdArg = std::optional<std::string> {}) noexcept;
CompleteTaskInput(const CompleteTaskInput& other);
CompleteTaskInput(CompleteTaskInput&& other) noexcept;

CompleteTaskInput& operator=(const CompleteTaskInput& other);
CompleteTaskInput& operator=(CompleteTaskInput&& other) noexcept;

response::IdType id {};
std::optional<TaskState> testTaskState {};
std::optional<bool> isComplete {};
Expand All @@ -68,6 +79,12 @@ struct [[nodiscard]] CompleteTaskInput

} // namespace mutate

template <>
constexpr bool isInputType<mutate::CompleteTaskInput>() noexcept
{
return true;
}

namespace mutation::CompleteTaskMutation {

using mutate::GetRequestText;
Expand Down

0 comments on commit 681c08d

Please sign in to comment.