summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMikhail Svetkin <mikhail.svetkin@gmail.com>2019-08-30 23:14:31 +0200
committerMikhail Svetkin <mikhail.svetkin@gmail.com>2019-10-24 11:56:21 +0200
commit3eebefcd271cdb2f2bb2affa5e05b3cb04354945 (patch)
treefd717b5790b6c3290cfc4d7f696f684eca1a8ea6 /src
parent52bce52413763ead7759f8c2e374a40bb82f058f (diff)
QHttpServerRouterViewTraits: Make it simpler and testable
Currently QHttpServerRouterViewTraits contains all helpers and tools inside itself. It is hard to read and extend. This patch: - Moves all helper and tools to QtPrivate namespace - Tries to "simplify" template magic - Adds support for unit test Change-Id: I6aa443b286c4c896b8dbfee85fffb638328868ad Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/httpserver/qhttpserver.h8
-rw-r--r--src/httpserver/qhttpserverrouter.h12
-rw-r--r--src/httpserver/qhttpserverrouterviewtraits.h412
3 files changed, 271 insertions, 161 deletions
diff --git a/src/httpserver/qhttpserver.h b/src/httpserver/qhttpserver.h
index 885c150..d5ebdcb 100644
--- a/src/httpserver/qhttpserver.h
+++ b/src/httpserver/qhttpserver.h
@@ -68,7 +68,7 @@ public:
{
using ViewHandler = typename VariadicTypeLast<Args...>::Type;
using ViewTraits = QHttpServerRouterViewTraits<ViewHandler>;
- static_assert(ViewTraits::Arguments::compileCheck(),
+ static_assert(ViewTraits::Arguments::StaticAssert,
"ViewHandler arguments are in the wrong order or not supported");
return routeHelper<Rule, ViewHandler, ViewTraits>(
QtPrivate::makeIndexSequence<sizeof ... (Args) - 1>{},
@@ -102,7 +102,7 @@ private:
}
template<typename ViewTraits, typename T>
- typename std::enable_if<ViewTraits::IsLastArgNonSpecial, void>::type
+ typename std::enable_if<!ViewTraits::Arguments::Last::IsSpecial::Value, void>::type
responseImpl(T &boundViewHandler,
const QHttpServerRequest &request,
QTcpSocket *socket)
@@ -112,7 +112,7 @@ private:
}
template<typename ViewTraits, typename T>
- typename std::enable_if<ViewTraits::IsLastArgRequest, void>::type
+ typename std::enable_if<ViewTraits::Arguments::Last::IsRequest::Value, void>::type
responseImpl(T &boundViewHandler, const QHttpServerRequest &request, QTcpSocket *socket)
{
const QHttpServerResponse response(boundViewHandler(request));
@@ -120,7 +120,7 @@ private:
}
template<typename ViewTraits, typename T>
- typename std::enable_if<ViewTraits::IsLastArgResponder, void>::type
+ typename std::enable_if<ViewTraits::Arguments::Last::IsResponder::Value, void>::type
responseImpl(T &boundViewHandler,
const QHttpServerRequest &request,
QTcpSocket *socket)
diff --git a/src/httpserver/qhttpserverrouter.h b/src/httpserver/qhttpserverrouter.h
index 522562c..a1f4174 100644
--- a/src/httpserver/qhttpserverrouter.h
+++ b/src/httpserver/qhttpserverrouter.h
@@ -97,7 +97,7 @@ public:
{
return addRuleHelper<ViewTraits>(
rule,
- typename QtPrivate::Indexes<ViewTraits::ArgumentCount>::Value{});
+ typename ViewTraits::Arguments::Indexes{});
}
template<typename ViewHandler, typename ViewTraits = QHttpServerRouterViewTraits<ViewHandler>>
@@ -107,8 +107,8 @@ public:
return bindCapturedImpl<ViewHandler, ViewTraits>(
std::forward<ViewHandler>(handler),
match,
- typename QtPrivate::Indexes<ViewTraits::ArgumentCapturableCount>::Value{},
- typename QtPrivate::Indexes<ViewTraits::ArgumentPlaceholdersCount>::Value{});
+ typename ViewTraits::Arguments::CapturableIndexes{},
+ typename ViewTraits::Arguments::PlaceholdersIndexes{});
}
bool handleRequest(const QHttpServerRequest &request,
@@ -119,7 +119,8 @@ private:
bool addRuleHelper(QHttpServerRouterRule *rule,
QtPrivate::IndexesList<Idx...>)
{
- const std::initializer_list<int> types = {ViewTraits::template Arg<Idx>::metaTypeId()...};
+ const std::initializer_list<int> types = {
+ ViewTraits::Arguments::template metaTypeId<Idx>()...};
return addRuleImpl(rule, types);
}
@@ -135,7 +136,8 @@ private:
return std::bind(
std::forward<ViewHandler>(handler),
- QVariant(match.captured(Cx + 1)).value<typename ViewTraits::template Arg<Cx>::Type>()...,
+ QVariant(match.captured(Cx + 1))
+ .value<typename ViewTraits::Arguments::template Arg<Cx>::CleanType>()...,
QtPrivate::QHttpServerRouterPlaceholder<Px>{}...);
}
diff --git a/src/httpserver/qhttpserverrouterviewtraits.h b/src/httpserver/qhttpserverrouterviewtraits.h
index 1987306..b4ce4fb 100644
--- a/src/httpserver/qhttpserverrouterviewtraits.h
+++ b/src/httpserver/qhttpserverrouterviewtraits.h
@@ -44,181 +44,289 @@ QT_BEGIN_NAMESPACE
class QHttpServerRequest;
class QHttpServerResponder;
-template <typename ViewHandler>
-struct QHttpServerRouterViewTraits : QHttpServerRouterViewTraits<decltype(&ViewHandler::operator())> {};
+namespace QtPrivate {
-template <typename ClassType, typename Ret, typename ... Args>
-struct QHttpServerRouterViewTraits<Ret(ClassType::*)(Args...) const>
+template<typename T>
+struct RemoveCVRef
+{
+ using Type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+};
+
+
+template<bool classMember, typename ReturnT, typename ... Args>
+struct FunctionTraitsHelper
{
static constexpr const int ArgumentCount = sizeof ... (Args);
+ static constexpr const int ArgumentIndexMax = ArgumentCount - 1;
+ static constexpr const bool IsClassMember = classMember;
+ using ReturnType = ReturnT;
template <int I>
struct Arg {
- using OriginalType = typename std::tuple_element<I, std::tuple<Args...>>::type;
- using Type = typename QtPrivate::RemoveConstRef<OriginalType>::Type;
-
- static constexpr int metaTypeId() noexcept {
- return qMetaTypeId<
- typename std::conditional<
- !QMetaTypeId2<Type>::Defined,
- void,
- Type>::type>();
- }
- };
-
-private:
- // Tools used to check position of special arguments (QHttpServerResponder, QHttpServerRequest)
- // and unsupported types
- template<bool Last, typename Arg>
- static constexpr bool checkArgument() noexcept
- {
- static_assert(Last || !std::is_same<Arg, const QHttpServerRequest &>::value,
- "ViewHandler arguments error: "
- "QHttpServerRequest can only be the last argument");
- static_assert(Last || !std::is_same<Arg, QHttpServerResponder &&>::value,
- "ViewHandler arguments error: "
- "QHttpServerResponder can only be the last argument");
-
- static_assert(!std::is_same<Arg, QHttpServerRequest &&>::value,
- "ViewHandler arguments error: "
- "QHttpServerRequest can only be passed as a const reference");
- static_assert(!std::is_same<Arg, QHttpServerRequest &>::value,
- "ViewHandler arguments error: "
- "QHttpServerRequest can only be passed as a const reference");
- static_assert(!std::is_same<Arg, const QHttpServerRequest *>::value,
- "ViewHandler arguments error: "
- "QHttpServerRequest can only be passed as a const reference");
- static_assert(!std::is_same<Arg, QHttpServerRequest const*>::value,
- "ViewHandler arguments error: "
- "QHttpServerRequest can only be passed as a const reference");
- static_assert(!std::is_same<Arg, QHttpServerRequest *>::value,
- "ViewHandler arguments error: "
- "QHttpServerRequest can only be passed as a const reference");
-
- static_assert(!std::is_same<Arg, QHttpServerResponder &>::value,
- "ViewHandler arguments error: "
- "QHttpServerResponder can only be passed as a universal reference");
- static_assert(!std::is_same<Arg, const QHttpServerResponder *const>::value,
- "ViewHandler arguments error: "
- "QHttpServerResponder can only be passed as a universal reference");
- static_assert(!std::is_same<Arg, const QHttpServerResponder *>::value,
- "ViewHandler arguments error: "
- "QHttpServerResponder can only be passed as a universal reference");
- static_assert(!std::is_same<Arg, QHttpServerResponder const*>::value,
- "ViewHandler arguments error: "
- "QHttpServerResponder can only be passed as a universal reference");
- static_assert(!std::is_same<Arg, QHttpServerResponder *>::value,
- "ViewHandler arguments error: "
- "QHttpServerResponder can only be passed as a universal reference");
-
- using Type = typename std::remove_cv<typename std::remove_reference<Arg>::type>::type;
-
- static_assert(QMetaTypeId2<Type>::Defined
- || std::is_same<Type, QHttpServerResponder>::value
- || std::is_same<Type, QHttpServerRequest>::value,
- "ViewHandler arguments error: "
- "Type is not registered, please use the Q_DECLARE_METATYPE macro "
- "to make it known to Qt's meta-object system");
-
- return true;
- }
-
-public:
- template<typename Arg, typename ... ArgX>
- struct ArgumentsCheck {
- static constexpr bool compileCheck()
- {
- return checkArgument<false, Arg>() && ArgumentsCheck<ArgX...>::compileCheck();
- }
- };
+ using Type = typename std::tuple_element<I, std::tuple<Args...>>::type;
- template<typename Arg>
- struct ArgumentsCheck<Arg> {
- static constexpr bool compileCheck()
- {
- return checkArgument<true, Arg>();
- }
- };
+ using CleanType = typename QtPrivate::RemoveCVRef<Type>::Type;
- using Arguments = ArgumentsCheck<Args...>;
-
-private:
- // Tools used to compute ArgumentCapturableCount
- template<typename T>
- static constexpr typename std::enable_if<QMetaTypeId2<T>::Defined, int>::type
- capturable()
- { return 1; }
-
- template<typename T>
- static constexpr typename std::enable_if<!QMetaTypeId2<T>::Defined, int>::type
- capturable()
- { return 0; }
-
- static constexpr std::size_t sum() noexcept { return 0; }
-
- template<typename ... N>
- static constexpr std::size_t sum(const std::size_t it, N ... n) noexcept
- { return it + sum(n...); }
-
-public:
- static constexpr const std::size_t ArgumentCapturableCount =
- sum(capturable<typename QtPrivate::RemoveConstRef<Args>::Type>()...);
- static constexpr const std::size_t ArgumentPlaceholdersCount = ArgumentCount
- - ArgumentCapturableCount;
-
-private:
- // Tools used to get BindableType
- template<typename Return, typename ... ArgsX>
- struct BindTypeHelper {
- using Type = std::function<Return(ArgsX...)>;
+ static constexpr bool Defined = QMetaTypeId2<CleanType>::Defined;
};
-
- template<int ... Idx>
- static constexpr typename BindTypeHelper<
- Ret, typename Arg<ArgumentCapturableCount + Idx>::OriginalType...>::Type
- bindTypeHelper(QtPrivate::IndexesList<Idx...>)
- {
- return BindTypeHelper<Ret,
- typename Arg<ArgumentCapturableCount + Idx>::OriginalType...>::Type();
- }
-
-public:
- using BindableType = decltype(bindTypeHelper(typename QtPrivate::Indexes<
- ArgumentPlaceholdersCount>::Value{}));
-
- static constexpr bool IsLastArgRequest = std::is_same<
- typename Arg<ArgumentCount - 1>::Type, QHttpServerRequest>::value;
-
- static constexpr bool IsLastArgResponder = std::is_same<
- typename Arg<ArgumentCount - 1>::Type, QHttpServerResponder&&>::value;
-
- static constexpr bool IsLastArgNonSpecial = !(IsLastArgRequest || IsLastArgResponder);
};
-template <typename ClassType, typename Ret>
-struct QHttpServerRouterViewTraits<Ret(ClassType::*)() const>
+template<bool classMember, typename ReturnT>
+struct FunctionTraitsHelper<classMember, ReturnT>
{
static constexpr const int ArgumentCount = 0;
+ static constexpr const int ArgumentIndexMax = -1;
+ static constexpr const bool IsClassMember = classMember;
+ using ReturnType = ReturnT;
template <int I>
struct Arg {
- using Type = void;
+ using Type = std::false_type;
+ using CleanType = Type;
+ static constexpr bool Defined = QMetaTypeId2<CleanType>::Defined;
};
+};
- static constexpr const std::size_t ArgumentCapturableCount = 0u;
- static constexpr const std::size_t ArgumentPlaceholdersCount = 0u;
+template<typename T>
+struct FunctionTraits;
- using BindableType = decltype(std::function<Ret()>{});
+template<typename T>
+struct FunctionTraits : public FunctionTraits<decltype(&T::operator())>{};
- static constexpr bool IsLastArgRequest = false;
- static constexpr bool IsLastArgResponder = false;
- static constexpr bool IsLastArgNonSpecial = true;
+template<typename ReturnT, typename ... Args>
+struct FunctionTraits<ReturnT (*)(Args...)>
+ : public FunctionTraitsHelper<false, ReturnT, Args...>
+{
+};
- struct ArgumentsCheck {
- static constexpr bool compileCheck() { return true; }
+template<class ReturnT, class ClassT, class ...Args>
+struct FunctionTraits<ReturnT (ClassT::*)(Args...) const>
+ : public FunctionTraitsHelper<true, ReturnT, Args...>
+{
+ using classType = ClassT;
+};
+
+template<typename ViewHandler, bool DisableStaticAssert>
+struct ViewTraitsHelper {
+ using FunctionTraits = typename QtPrivate::FunctionTraits<ViewHandler>;
+ using ArgumentIndexes = typename QtPrivate::Indexes<FunctionTraits::ArgumentCount>::Value;
+
+ struct StaticMath {
+ template <template<typename> class Predicate, bool defaultValue>
+ struct Loop {
+ static constexpr bool eval() noexcept {
+ return defaultValue;
+ }
+
+ template<typename T, typename ... N>
+ static constexpr T eval(const T it, N ...n) noexcept {
+ return Predicate<T>::eval(it, eval(n...));
+ }
+ };
+
+ template<typename T>
+ struct SumPredicate {
+ static constexpr T eval(const T rs, const T ls) noexcept
+ {
+ return rs + ls;
+ }
+ };
+
+ template<typename T>
+ struct AndPredicate {
+ static constexpr T eval(const T rs, const T ls) noexcept
+ {
+ return rs && ls;
+ }
+ };
+
+ using Sum = Loop<SumPredicate, false>;
+ using And = Loop<AndPredicate, true>;
+ using Or = Sum;
};
- using Arguments = ArgumentsCheck;
+ struct Arguments {
+ template<int I>
+ struct StaticCheck {
+ using Arg = typename FunctionTraits::template Arg<I>;
+ using CleanType = typename Arg::CleanType;
+
+ template<typename T, bool Clean = false>
+ static constexpr bool isType() noexcept
+ {
+ using SelectedType =
+ typename std::conditional<
+ Clean,
+ CleanType,
+ typename Arg::Type
+ >::type;
+ return std::is_same<SelectedType, T>::value;
+ }
+
+ template<typename T>
+ struct SpecialHelper {
+ using CleanTypeT = typename QtPrivate::RemoveCVRef<T>::Type;
+
+ static constexpr bool TypeMatched = isType<CleanTypeT, true>();
+ static constexpr bool TypeCVRefMatched = isType<T>();
+ static constexpr bool ValidPosition =
+ (I == FunctionTraits::ArgumentIndexMax);
+ static constexpr bool ValidAll = TypeCVRefMatched && ValidPosition;
+
+ static constexpr bool assertCondition =
+ DisableStaticAssert || !TypeMatched || TypeCVRefMatched;
+
+ static constexpr bool assertConditionOrder =
+ DisableStaticAssert || !TypeMatched || ValidPosition;
+
+ static constexpr bool staticAssert() noexcept
+ {
+ static_assert(assertConditionOrder,
+ "ViewHandler arguments error: "
+ "QHttpServerRequest or QHttpServerResponder"
+ " can only be the last argument");
+ return true;
+ }
+ };
+
+ template<typename ... T>
+ struct CheckAny {
+ static constexpr bool Value = StaticMath::Or::eval(T::Value...);
+ static constexpr bool Valid = StaticMath::Or::eval(T::Valid...);
+ static constexpr bool staticAssert() noexcept
+ {
+ return StaticMath::Or::eval(T::staticAssert()...);
+ }
+ };
+
+ struct IsRequest {
+ using Helper = SpecialHelper<const QHttpServerRequest &>;
+ static constexpr bool Value = Helper::TypeMatched;
+ static constexpr bool Valid = Helper::ValidAll;
+
+ static constexpr bool staticAssert() noexcept
+ {
+ static_assert(Helper::assertCondition,
+ "ViewHandler arguments error: "
+ "QHttpServerRequest can only be passed as a const reference");
+ return Helper::staticAssert();
+ }
+ };
+
+ struct IsResponder {
+ using Helper = SpecialHelper<QHttpServerResponder &&>;
+ static constexpr bool Value = Helper::TypeMatched;
+ static constexpr bool Valid = Helper::ValidAll;
+
+ static constexpr bool staticAssert() noexcept
+ {
+ static_assert(Helper::assertCondition,
+ "ViewHandler arguments error: "
+ "QHttpServerResponder can only be passed as a universal reference");
+ return Helper::staticAssert();
+ }
+ };
+
+ using IsSpecial = CheckAny<IsRequest, IsResponder>;
+
+ struct IsSimple {
+ static constexpr bool Value = !IsSpecial::Value &&
+ I < FunctionTraits::ArgumentCount &&
+ FunctionTraits::ArgumentIndexMax != -1;
+ static constexpr bool Valid = Arg::Defined;
+
+ static constexpr bool assertCondition =
+ DisableStaticAssert || !Value || Valid;
+
+ static constexpr bool staticAssert() noexcept
+ {
+ static_assert(assertCondition,
+ "ViewHandler arguments error: "
+ "Type is not registered, please use the Q_DECLARE_METATYPE macro "
+ "to make it known to Qt's meta-object system");
+ return true;
+ }
+ };
+
+ using CheckOk = CheckAny<IsSimple, IsSpecial>;
+
+ static constexpr bool Valid = CheckOk::Valid;
+ static constexpr bool StaticAssert = CheckOk::staticAssert();
+ };
+
+ template<int ... I>
+ struct ArgumentsReturn {
+ template<int Idx>
+ using Arg = StaticCheck<Idx>;
+
+ template<int Idx>
+ static constexpr int metaTypeId() noexcept
+ {
+ using Type = typename FunctionTraits::template Arg<Idx>::CleanType;
+
+ return qMetaTypeId<
+ typename std::conditional<
+ QMetaTypeId2<Type>::Defined,
+ Type,
+ void>::type>();
+ }
+
+ static constexpr std::size_t Count = FunctionTraits::ArgumentCount;
+ static constexpr std::size_t CapturableCount =
+ StaticMath::Sum::eval(
+ static_cast<std::size_t>(FunctionTraits::template Arg<I>::Defined)...);
+ static constexpr std::size_t PlaceholdersCount = Count - CapturableCount;
+
+ static constexpr bool Valid = StaticMath::And::eval(StaticCheck<I>::Valid...);
+ static constexpr bool StaticAssert =
+ StaticMath::And::eval(StaticCheck<I>::StaticAssert...);
+
+ using Indexes = typename QtPrivate::IndexesList<I...>;
+
+ using CapturableIndexes =
+ typename QtPrivate::Indexes<CapturableCount>::Value;
+
+ using PlaceholdersIndexes =
+ typename QtPrivate::Indexes<PlaceholdersCount>::Value;
+
+ using Last = Arg<FunctionTraits::ArgumentIndexMax>;
+ };
+
+ template<int ... I>
+ static constexpr ArgumentsReturn<I...> eval(QtPrivate::IndexesList<I...>) noexcept
+ {
+ return ArgumentsReturn<I...>{};
+ }
+ };
+
+ template<int CaptureOffset>
+ struct BindType {
+ template<typename ... Args>
+ struct FunctionWrapper {
+ using Type = std::function<typename FunctionTraits::ReturnType (Args...)>;
+ };
+
+ template<int ... Idx>
+ static constexpr typename FunctionWrapper<
+ typename FunctionTraits::template Arg<CaptureOffset + Idx>::Type...>::Type
+ eval(QtPrivate::IndexesList<Idx...>) noexcept
+ {
+ return FunctionWrapper<
+ typename FunctionTraits::template Arg<CaptureOffset + Idx>::Type...>::Type();
+ }
+ };
+};
+
+} // namespace QtPrivate
+
+template <typename ViewHandler, bool DisableStaticAssert = false>
+struct QHttpServerRouterViewTraits
+{
+ using Helpers = typename QtPrivate::ViewTraitsHelper<ViewHandler, DisableStaticAssert>;
+ using Arguments = decltype(Helpers::Arguments::eval(typename Helpers::ArgumentIndexes{}));
+ using BindableType = decltype(
+ Helpers::template BindType<Arguments::CapturableCount>::eval(
+ typename Arguments::PlaceholdersIndexes{}));
};
QT_END_NAMESPACE