~ne02ptzero/pistache

a345fcc6ed0eafed487cd3877bee842e5f4607be — Andrey Garaev 1 year, 2 months ago f2f5a50
Implement router static checks (#808)

* feat router: Implement static checks

* Actualize checks
1 files changed, 72 insertions(+), 28 deletions(-)

M include/pistache/router.h
M include/pistache/router.h => include/pistache/router.h +72 -28
@@ 306,29 306,72 @@ void Head(Router &router, const std::string &resource, Route::Handler handler);
void NotFound(Router &router, Route::Handler handler);

namespace details {
template <class... Args> struct TypeList {
  template <size_t N> struct At {
template <typename... Args>
struct TypeList {
  template <size_t N>
  struct At {
    static_assert(N < sizeof...(Args), "Invalid index");
    typedef typename std::tuple_element<N, std::tuple<Args...>>::type Type;

    using Type = typename std::tuple_element<N, std::tuple<Args...>>::type;
  };
};

template <typename... Args> void static_checks() {
template <typename Request, typename Response>
struct BindChecks {
  constexpr static bool request_check =
      std::is_const<typename std::remove_reference<Request>::type>::value &&
      std::is_lvalue_reference<typename std::remove_cv<Request>::type>::value &&
      std::is_same<typename std::decay<Request>::type, Rest::Request>::value;

  constexpr static bool response_check =
      !std::is_const<typename std::remove_reference<Response>::type>::value &&
      std::is_same<typename std::remove_reference<Response>::type,
                   Response>::value &&
      std::is_same<typename std::decay<Response>::type,
                   Http::ResponseWriter>::value;

  static_assert(
      request_check && response_check,
      "Function should accept (const Rest::Request&, HttpResponseWriter)");
};

template <typename Request, typename Response>
struct MiddlewareChecks {
  constexpr static bool request_check =
      !std::is_const<typename std::remove_reference<Request>::type>::value &&
      std::is_lvalue_reference<typename std::remove_cv<Request>::type>::value &&
      std::is_same<typename std::decay<Request>::type, Http::Request>::value;

  constexpr static bool response_check =
      !std::is_const<typename std::remove_reference<Response>::type>::value &&
      std::is_lvalue_reference<
          typename std::remove_cv<Response>::type>::value &&
      std::is_same<typename std::decay<Response>::type,
                   Http::ResponseWriter>::value;

  static_assert(request_check && response_check,
                "Function should accept (Http::Request&, HttpResponseWriter&)");
};

template <template <typename, typename> class Checks, typename... Args>
constexpr void static_checks() {
  static_assert(sizeof...(Args) == 2, "Function should take 2 parameters");
//            typedef details::TypeList<Args...> Arguments;
// Disabled now as it
// 1/ does not compile
// 2/ might not be relevant
#if 0
            static_assert(std::is_same<Arguments::At<0>::Type, const Rest::Request&>::value, "First argument should be a const Rest::Request&");
            static_assert(std::is_same<typename Arguments::At<0>::Type, Http::Response>::value, "Second argument should be a Http::Response");
#endif

  using Arguments = details::TypeList<Args...>;

  using Request = typename Arguments::template At<0>::Type;
  using Response = typename Arguments::template At<1>::Type;

  // instantiate template this way
  constexpr Checks<Request, Response> checks;

  UNUSED(checks);
}
} // namespace details

template <typename Result, typename Cls, typename... Args, typename Obj>
Route::Handler bind(Result (Cls::*func)(Args...), Obj obj) {
  details::static_checks<Args...>();
  details::static_checks<details::BindChecks, Args...>();

  return [=](const Rest::Request &request, Http::ResponseWriter response) {
    (obj->*func)(request, std::move(response));


@@ 339,7 382,7 @@ Route::Handler bind(Result (Cls::*func)(Args...), Obj obj) {

template <typename Result, typename Cls, typename... Args, typename Obj>
Route::Handler bind(Result (Cls::*func)(Args...), std::shared_ptr<Obj> objPtr) {
  details::static_checks<Args...>();
  details::static_checks<details::BindChecks, Args...>();

  return [=](const Rest::Request &request, Http::ResponseWriter response) {
    (objPtr.get()->*func)(request, std::move(response));


@@ 350,7 393,7 @@ Route::Handler bind(Result (Cls::*func)(Args...), std::shared_ptr<Obj> objPtr) {

template <typename Result, typename... Args>
Route::Handler bind(Result (*func)(Args...)) {
  details::static_checks<Args...>();
  details::static_checks<details::BindChecks, Args...>();

  return [=](const Rest::Request &request, Http::ResponseWriter response) {
    func(request, std::move(response));


@@ 361,29 404,30 @@ Route::Handler bind(Result (*func)(Args...)) {

template <typename Cls, typename... Args, typename Obj>
Route::Middleware middleware(bool (Cls::*func)(Args...), Obj obj) {
	details::static_checks<Args...>();
  details::static_checks<details::MiddlewareChecks, Args...>();

	return [=](Http::Request &request, Http::ResponseWriter &response) {
		return (obj->*func)(request, response);
	};
  return [=](Http::Request &request, Http::ResponseWriter &response) {
    return (obj->*func)(request, response);
  };
}

template <typename Cls, typename... Args, typename Obj>
Route::Middleware middleware(bool (Cls::*func)(Args...), std::shared_ptr<Obj> objPtr) {
	details::static_checks<Args...>();
Route::Middleware middleware(bool (Cls::*func)(Args...),
                             std::shared_ptr<Obj> objPtr) {
  details::static_checks<details::MiddlewareChecks, Args...>();

	return [=](Http::Request &request, Http::ResponseWriter &response) {
		return (objPtr.get()->*func)(request, response);
	};
  return [=](Http::Request &request, Http::ResponseWriter &response) {
    return (objPtr.get()->*func)(request, response);
  };
}

template <typename... Args>
Route::Middleware middleware(bool (*func)(Args...)) {
	details::static_checks<Args...>();
  details::static_checks<details::MiddlewareChecks, Args...>();

	return [=](Http::Request &request, Http::ResponseWriter &response) {
		return func(request, response);
	};
  return [=](Http::Request &request, Http::ResponseWriter &response) {
    return func(request, response);
  };
}

} // namespace Routes