
RESTinio , nuestro pequeño servidor HTTP incorporado, continĂşa evolucionando . Una de las caracterĂsticas distintivas de RESTinio es que en su implementaciĂłn, las plantillas C ++ de varios pisos se usan activamente (como se mencionĂł anteriormente: 1 , 2 ).
C++ RESTinio , , , RESTinio: , .
( ) , run-time. RESTinio, , . , .
easy_parser_router express-router-
express-router ?
ExpressJS RESTinio , . , , . , , - . , , .
express- . , , :
router->http_get("/api/v1/books/:id",
   [](const auto & req, auto params) {
      const auto book_id = restinio::cast_to<std::uint64_t>(params["Id"]);
      ...
   });
, , "id". "id" ( ), "Id" ( ).
— id. /api/v1/books/:id "id" , , , .
, :
router->http_get(R"(/api/v1/books/:id(\d+))",
   [](const auto & req, auto params) {
      const auto book_id = restinio::cast_to<std::uint64_t>(params["id"]);
      ...
   });
, .
, "id" , 64- .
, , , :
router->http_get(R"(/api/v1/books/:id(\d{1,10}))",
   [](const auto & req, auto params) {
      const auto book_id = restinio::cast_to<std::uint64_t>(params["id"]);
      ...
   });
, run-time. .., , .
, :
class api_v1_handler {
   ...
public:
   auto on_get_book(
         const restinio::request_handle_t & req,
         restinio::router::route_params_t params)
   {
      const auto book_id = restinio::cast_to<std::uint64_t>(params["id"]);
      ...
   }
   auto on_get_book_version(
         const restinio::request_handle_t & req,
         restinio::router::route_params_t params)
   {
      const auto book_id = restinio::cast_to<std::uint64_t>(params["id"]);
      const auto ver_id = restinio::cast_to<std::string>(params["version"]);
      ...
   }
   auto on_get_author_books(
         const restinio::request_handle_t & req,
         restinio::router::route_params_t params)
   {
      const auto author = restinio::cast_to<std::string>(params["author"]);
      ...
   }
   ...
};
- - , .
:
auto handler = std::make_shared<api_v1_handler>(...);
router->http_get(R"(/api/v1/books/:id(\d{1,10}))",
   [handler](const auto & req, auto params) {
      return handler->on_get_book_version(req, params);
   });
router->http_get(R"(/api/v1/books/:id(\d{1,10})/versions/:version)",
   [handler](const auto & req, auto params) {
      return handler->on_get_author_books(req, params);
   });
router->http_get(R"(/api/v1/:author)",
   [handler](const auto & req, auto params) {
      return handler->on_get_book(req, params);
   });
. , , . , .
, api_v1_handler. express- , , :
class api_v1_handler {
   ...
   auto on_get_book_version(
         const restinio::request_handle_t & req,
         std::uint64_t book_id,
         const std::string & ver_id) { ... }
   ...
};
auto handler = std::make_shared<api_v1_handler>(...);
router->http_get(R"(/api/v1/books/:id(\d{1,10})/versions/:version)",
   [handler](const auto & req, auto params) {
      return handler->on_get_book_version(req,
            restinio::cast_to<std::uint64_t>(params["id"]),
            restinio::cast_to<std::string>(params["version"]));
   });
. , . . , , .
easy_parser_router
0.6.6 RESTinio express- easy_parser_router , easy_parser_router .
"id":
namespace epr = restinio::router::easy_parser_router;
router->http_get(
   epr::path_to_params(
      "/api/v1/books/",
      epr::non_negative_decimal_number_p<std::uint64_t>()),
   [](const auto & req, std::uint64_t book_id) {
      ...
   });
, , HTTP GET , api/v1/books/ 64- , . :
- — 
restinio::request_handle_t, ; - — 64- , .
 
, URL book_id , uint64_t, " " .
api_v1_handler . api_v1_handler:
class api_v1_handler {
   ...
public:
   using book_id_type = std::uint64_t;
   auto on_get_book(
         const restinio::request_handle_t & req,
         book_id_type book_id) { ... }
   auto on_get_book_version(
         const restinio::request_handle_t & req,
         book_id_type book_id,
         const std::string & ver_id) { ... }
   auto on_get_author_books(
         const restinio::request_handle_t & req,
         const std::string & author) { ... }
   ...
};
- . -.
:
namespace epr = restinio::router::easy_parser_router;
auto book_id_p = epr::non_negative_decimal_number_p<api_v1_handler::book_id_type>();
auto ver_id_p = epr::path_fragment_p();
auto author_p = epr::path_fragment_p();
auto handler = std::make_shared<api_v1_handler>(...);
router->http_get(
   epr::path_to_params("/api/v1/books/", book_id_p),
   [handler](const auto & req, auto book_id) {
      return handler->on_get_book(req, book_id);
   });
router->http_get(
   epr::path_to_params("/api/v1/books/", book_id_p, "/versions/", ver_id_p),
   [handler](const auto & req, auto book_id, const auto & ver_id) {
      return handler->on_get_book_version(req, book_id, ver_id);
   });
router->http_get(
   epr::path_to_params("/api/v1/", author_p),
   [handler](const auto & req, const auto & author) {
      return handler->on_get_author_books(req, author);
   });
on_get_book , on_get_author_books.
path_to_params. path_to_tuple
, easy_parser_router-, path_to_params, . variadic template , , .. producer-.
path_to_params -. , path URL, HTTP-.
URL path . , path_to_params -producer . , path_to_params producer, . producer-, . , . .. path_to_params producer-, , , :
router->http_get(epr::path_to_params("/"), [](const auto & req) {...});
.
, , path_to_params — URL HTTP- .
path_to_params — . .., path_to_params producer-, :
router->http_get(
   epr::path_to_params("/api/v1/books/", book_id_p, "/versions/", ver_id_p),
   [handler](const auto & req, auto book_id, const auto & ver_id) {
      return handler->on_get_book_version(req, book_id, ver_id);
   });
, producer-, . - : — restinio::request_handle_t, — producer-.
, - , path_to_params path_to_tuple. path_to_tuple path_to_params , path_to_tuple producer- . :
router->http_get(
   epr::path_to_tuple("/api/v1/books/", book_id_p, "/versions/", ver_id_p),
   [handler](const auto & req, std::tuple<std::uint64_t, std::string> params) {
      return handler->on_get_book_version(req, std::get<0>(params), std::get<1>(params));
   });
easy_parser easy_parser_router?
, RESTinio HTTP-, RESTinio easy_parser. Parsing Expression Grammar (PEG), PEG- C++ DSL. , , :
limit = "limit" [SPACE] ":" [SPACE] NUMBER SPACE "bytes"
"limit:4096 bytes" "limit: 4096 bytes", easy_parser- :
using namespace restinio::easy_parser;
auto parser = produce<unsigned int>(
   exact("limit"),
   maybe(space()),
   symbol(':'),
   maybe(space()),
   non_negative_decimal_number_p<unsigned int>(),
   space(),
   exact("bytes"));
, , , unsigned int.
easy_parser_router RESTinio-0.6.6 easy_parser-. easy_parser_router easy_parser- - URL, easy_parser-.
, easy_parser, , easy_parser_router- - long_output .
: GET- URL /, /<size> /<size>/<count>, <size> <count> . , URL /512k/1024 , 1024 512KiB . URL /1200/500 , 500 1200 .
express- URL :
std::size_t extract_chunk_size(const restinio::router::route_params_t & params) {
   const auto multiplier = [](const auto sv) noexcept -> std::size_t {
      if(sv.empty() || "B" == sv || "b" == sv) return 1u;
      else if("K" == sv || "k" == sv) return 1024u;
      else return 1024u*1024u;
   };
   return restinio::cast_to<std::size_t>(params["value"]) *
         multiplier(params["multiplier"]);
}
...
auto router = std::make_unique<router_t>();
router->http_get("/", [&ctx](auto req, auto) {...});
router->http_get(
         R"(/:value(\d+):multiplier([MmKkBb]?))",
         [&ctx](auto req, auto params) {
      const auto chunk_size = extract_chunk_size(params);
      ...
   });
router->http_get(
         R"(/:value(\d+):multiplier([MmKkBb]?)/:count(\d+))",
         [&ctx](auto req, auto params) {
      const auto chunk_size = extract_chunk_size(params);
      const auto count = restinio::cast_to<std::size_t>(params["count"]);
      ...
   });
easy_parser_router- :
using namespace restinio::router::easy_parser_router;
auto router = std::make_unique<router_t>();
struct distribution_params
{
   std::size_t chunk_size_{100u*1024u};
   std::size_t count_{10000u};
};
struct chunk_size { std::uint32_t c_{1u}, m_{1u}; };
router->http_get(
   path_to_params(
      produce<distribution_params>(
         exact("/"),
         maybe(
            produce<chunk_size>(
               non_negative_decimal_number_p<std::uint32_t>()
                  >> &chunk_size::c_,
               maybe(
                  produce<std::uint32_t>(
                     alternatives(
                        caseless_symbol_p('b') >> just_result(1u),
                        caseless_symbol_p('k') >> just_result(1024u),
                        caseless_symbol_p('m') >> just_result(1024u * 1024u)
                     )
                  ) >> &chunk_size::m_
               )
            ) >> convert(
                  [](auto cs) { return std::size_t{cs.c_} * cs.m_; })
               >> &distribution_params::chunk_size_,
            maybe(
               exact("/"),
               non_negative_decimal_number_p<std::size_t>()
                  >> &distribution_params::count_
            )
         )
      )
   ),
   [&ctx](const auto & req, const auto & params ) { ... });
.. URL URL. URL PEG-:
path = "/" [NUMBER [((B|b) | (K|k) | (M|m))] ["/" NUMBER]]
C++ , PEG- distribution_params, : . C++ , b, k m.
easy_parser , , , . ...
-, . , ( , ). , PEG- " , ".
-, easy_parser, easy_parser_router- . — , . , , don't shoot the pianist… , - , DSL C++14, , .
, produce, maybe, alternatives, just_result, convert . . . - , . easy_parser- , . , .
easy_parser- easy_parser_router-
easy_parser easy_parser_router, " C++ ". , - C++, , , , .
result_value_wrapper result_wrapper_for
easy_parser- , PEG- - . DSL easy_parser- — produce:
template<typename Target_Type, typename... Clauses>
auto produce(Clauses &&... clauses);
Target_Type , . Clauses .
, - :
struct KV{ int key; int value; };
produce<KV>(
   decimal_number_p<int>() >> &KV::key,
   exact("->"),
   decimal_number_p<int>() >> &KV::value);
:
expected<KV, parsing_error_t>
try_produce_KV_(impl::input_t & from) {
   KV result_value;
   {
      impl::decimal_number_producer_t<int> p;
      const auto r = p.try_produce(from);
      if(!r) return make_unexpected(r.error());
      impl::field_setter_consumer_t<&KV::key> consumer;
      consumer.consume(result_value, *r); 
   }
   {
      impl::exact_clause_t c{"->"};
      const auto r = c.try_process(from, result_value);
      if(r) return make_unexpected(*r);
   }
   {
      impl::decimal_number_producer_t<int> p;
      const auto r = p.try_produce(from);
      if(!r) return make_unexpected(r.error());
      impl::field_setter_consumer_t<&KV::value> consumer;
      consumer.consume(result_value, *r); 
   }
   return result_value;
}
.. , produce<KV>(clauses...), KV, clauses. clauses , produce<KV>.
, produce<T> T — - int long, - . T . , - :
struct KV{ int key; int value; };
produce<std::vector<KV>>(
   repeat(1, N,
      produce<KV>(
         decimal_number_p<int>() >> &KV::key,
         exact("->"),
         decimal_number_p<int>() >> &KV::value
      ) >> to_container(),
      maybe(exact(","))
   ));
.. KV, std::vector<KV>.
DSL- easy_parser_router- .
() , , , , " ", " " . , , , std::vector std::string.
std::array, , . - :
produce<std::array<char, 8>>(
   repeat(8, 8, hexdigit_p() >> to_container()));
: , std::array<char, 8>?
produce<std::array<char, 8>> std::array<char, 8>, .
, std::array , - . std::vector std::string .
result_value_wrapper easy_parser , produce<T>(clauses...) , produce clauses. .. produce<T>(...) - :
expected_t<T, parsing_error_t>
try_produce_T_(impl::input_t & from) {
   typename result_value_wrapper<T>::wrapped_type result_value;
   ...
   return result_value_wrapper<T>::unwrap_value(result_value);
}
easy_parser- result_value_wrapper . , std::vector:
template< typename T, typename... Args >
struct result_value_wrapper< std::vector< T, Args... > >
{
   using result_type = std::vector< T, Args... >;
   using value_type = typename result_type::value_type;
   using wrapped_type = result_type;
   static void
   as_result( wrapped_type & to, result_type && what )
   {
      to = std::move(what);
   }
   static void
   to_container( wrapped_type & to, value_type && what )
   {
      to.push_back( std::move(what) );
   }
   RESTINIO_NODISCARD
   static result_type &&
   unwrap_value( wrapped_type & v )
   {
      return std::move(v);
   }
};
, , std::array:
namespace impl
{
template< typename T, std::size_t S >
struct std_array_wrapper
{
   std::array< T, S > m_array;
   std::size_t m_index{ 0u };
};
} 
template< typename T, std::size_t S >
struct result_value_wrapper< std::array< T, S > >
{
   using result_type = std::array< T, S >;
   using value_type = typename result_type::value_type;
   using wrapped_type = impl::std_array_wrapper< T, S >;
   static void
   as_result( wrapped_type & to, result_type && what )
   {
      to.m_array = std::move(what);
      to.m_index = 0u;
   }
   static void
   to_container( wrapped_type & to, value_type && what )
   {
      if( to.m_index >= S ) throw exception_t(...);
      to.m_array[ to.m_index ] = std::move(what);
      ++to.m_index;
   }
   RESTINIO_NODISCARD
   static result_type &&
   unwrap_value( wrapped_type & v )
   {
      return std::move(v.m_array);
   }
};
produce<std::vector<T>>(...) std::vector<T>, produce<std::array<T, 10>>(...) impl::std_array_wrapper<T, 10>.
, result_value_wrapper . produce:
expected<KV, parsing_error_t>
try_produce_KV_(impl::input_t & from) {
   typename result_value_wrapper<KV>::wrapped_type result_value;
   {
      impl::decimal_number_producer_t<int> p;
      const auto r = p.try_produce(from);
      if(!r) return make_unexpected(r.error());
      impl::field_setter_consumer_t<&KV::key> consumer;
      consumer.consume(result_value, *r); 
   }
   ...
}
(1) consume result_value_wrapper<KV>::wrapped_type. consume , . KV, - KV. consume .
"" result_wrapper_for, wrapped_type result_value_wrapper. consume :
template< typename Target_Type, typename Value >
void
consume( Target_Type & dest, Value && src ) const
{
   using W = typename result_wrapper_for<Target_Type>::type;
   W::as_result( dest, std::forward<Value>(src) );
}
, - result_value_wrapper wrapped_type, result_type, result_wrapper_for:
template< typename T, std::size_t S >
struct result_wrapper_for< impl::std_array_wrapper<T, S> >
{
   using type = result_value_wrapper< std::array< T, S > >;
};
, consume<Target_Type, Value>() Target_Type impl::std_array_wrapper<T, S> ( wrapped_type std::array), consume , result_value_wrapper<std::array<T, S>> Target_Type.
easy_parser , :
- producer - ;
 - transformer producer- . transformer- , , 
producer() >> transformer_one() >> transformer_two() >> transformer_three(). .. transformer >>, producer, transformer. transformer, consumer; - consumer "" producer- . , -. consumer 
>> producer, transformer, . .. consumer >>; - clause. 
producer() >> ... >> consumer(), clause, , . 
. , , producer- transformer-, result_type.
easy_parser- ( to_lower ) result_type , , .
convert ( map): (), . convert :
produce<chunk_size>(
   non_negative_decimal_number_p<std::uint32_t>()
      >> &chunk_size::c_,
   maybe(
      produce<std::uint32_t>(
         alternatives(
            caseless_symbol_p('b') >> just_result(1u),
            caseless_symbol_p('k') >> just_result(1024u),
            caseless_symbol_p('m') >> just_result(1024u * 1024u)
         )
      ) >> &chunk_size::m_
   )
) >> convert( 
      [](auto cs) { return std::size_t{cs.c_} * cs.m_; })
   >> &distribution_params::chunk_size_,
(1) convert , chunk_size, std::size_t.
, . .. .
.. convert :
template<typename Callable>
SomeTransformerType convert(Callable && f) {...}
transformer-.
, SomeTransformerType result_type. convert, . convert producer-.
easy_parser : transformer_proxy.
Transformer_proxy transfomer-, , transformer- , transformer_proxy producer-.
transformer_proxy operator>> :
template<
   typename P,
   typename T,
   typename S = std::enable_if_t<
         is_producer_v<P> & is_transformer_proxy_v<T>,
         void > >
RESTINIO_NODISCARD
auto
operator>>(P producer, T transformer_proxy )
{
   auto real_transformer = transformer_proxy.template make_transformer< 
         typename P::result_type >();
   using transformator_type = std::decay_t< decltype(real_transformer) >;
   using producer_type = transformed_value_producer_t< P, transformator_type >;
   return producer_type{ std::move(producer), std::move(real_transformer) };
};
convert, .. transformer_proxy, - . transformer_proxy operator>>, - make_transformer transformer- result_type :
template< typename Converter >
class convert_transformer_proxy_t : public transformer_proxy_tag
{
   Converter m_converter;
public :
   ...
   template< typename Input_Type >
   RESTINIO_NODISCARD
   auto
   make_transformer() const &
   {
      using output_type = std::decay_t<
            decltype(m_converter(std::declval<Input_Type&&>())) >;
      return convert_transformer_t< output_type, Converter >{ m_converter };
   }
   ...
};
output_type make_transformer — result_type convert_transformer_t.
path_to_params/path_to_tuple
easy_parser_router easy_parser, easy_parser_router DSL path_to_params path_to_tuple. , :
router->http_get(
   path_to_params("/"),
   [](const auto & req) {...});
router->http_get(
   path_to_params("/api/v1/books/", non_negative_decimal_number_p<int>()),
   [](const auto & req, int book_id) {...});
, - :
router->http_get(
   route_to_params(produce<std::tuple<>>(
      exact_p("/") >> just_result(std::tuple<>{}))),
   [](const auto & req) {});
router->http_get(
   route_to_params(produce<std::tuple<int>>(
      exact("/api/v1/books/"),
      non_negative_decimal_number_p<int>() >> to_tuple<0>())),
  [](const auto & req, int book_id) {...});
, - DSL path_to_params (path_to_tuple) :
std::tuple, URL . result_type producer-, path_to_params (path_to_tuple);- clauses URL.
 
, :
path_to_params("/")
std::tuple<>. clauses - type_list<exact_fragment_clause_t>.
:
path_to_params(
   "/api/v1/books/",
   non_negative_decimal_number_p<int>(),
   "/versions/",
   path_fragment_p())
std::tuple<int, std::string>, clauses - : type_list<exact_fragment_clause_t, tuple_item_consumer_t<0, non_negative_decimal_number_producer_t<int>>, exact_fragment_clause_t, tuple_item_consumer_t<1, path_fragment_producer_t>>.
easy_parser_router ( ):
template< typename... Args >
struct dsl_processor
{
   static_assert( 0u != sizeof...(Args), "Args can't be an empty list" );
   using arg_types = meta::transform_t<
         dsl_details::special_decay, meta::type_list<Args...> >;
   using result_tuple = dsl_details::detect_result_tuple_t< arg_types >;
   using clauses_tuple = dsl_details::make_clauses_types_t< arg_types >;
};
, : arg_types result_tuple.
arg_types — , , const/volatile . , Args dsl_processor const T&, arg_types T.
arg_types - transform, . ( dsl_details::special_decay) . , .
meta::transform_t , C++14 ( Boost-).
result_tuple .
, result_tuple path_to_params (path_to_tuple) , producer-, result_type. :
template< typename Args_Type_List >
struct detect_result_tuple
{
   using type = meta::rename_t<
         typename result_tuple_detector<
               Args_Type_List,
               meta::type_list<> >::type,
         std::tuple >;
};
template< typename Args_Type_List >
using detect_result_tuple_t = typename detect_result_tuple<Args_Type_List>::type;
detect_result_tuple result_tuple_detector, . result_tuple_detector type_list<T...>, std::tuple<T...>. detect_result_tuple type_list<T...> std::tuple<T...> rename.
, result_tuple_detector. :
template< typename From, typename To >
struct result_tuple_detector;
template<
   template<class...> class From,
   typename... Sources,
   template<class...> class To,
   typename... Results >
struct result_tuple_detector< From<Sources...>, To<Results...> >
{
   using type = typename result_tuple_detector<
         meta::tail_of_t< Sources... >,
         typename add_type_if_necessary<
               meta::head_of_t< Sources... >,
               To< Results... > >::type
      >::type;
};
template<
   template<class...> class From,
   template<class...> class To,
   typename... Results >
struct result_tuple_detector< From<>, To<Results...> >
{
   using type = To<Results...>;
};
, C++ ", ..." , result_tuple_detector. , , :
template<
   template<class...> class From,
   template<class...> class To,
   typename... Results >
struct result_tuple_detector< From<>, To<Results...> >
{
   using type = To<Results...>;
};
. , . , — result_tuple_detector.
, , :
template<
   template<class...> class From,
   typename... Sources,
   template<class...> class To,
   typename... Results >
struct result_tuple_detector< From<Sources...>, To<Results...> >
{
   using type = typename result_tuple_detector<
         meta::tail_of_t< Sources... >,
         typename add_type_if_necessary<
               meta::head_of_t< Sources... >,
               To< Results... > >::type
      >::type;
};
: , "" , , add_type_if_necessary<H, R_List>. . add_type_if_necessary<H, R_List> (R_List), H producer-. , R_List H::result_type, H producer-.
add_type_if_necessary :
template< typename H, typename R, bool Is_Producer >
struct add_type_if_necessary_impl;
template<
   typename H,
   template<class...> class To,
   typename... Results >
struct add_type_if_necessary_impl< H, To<Results...>, false >
{
   using type = To<Results...>;
};
template<
   typename H,
   template<class...> class To,
   typename... Results >
struct add_type_if_necessary_impl< H, To<Results...>, true >
{
   using type = To<Results..., typename H::result_type>;
};
template< typename H, typename R >
struct add_type_if_necessary
   : add_type_if_necessary_impl< H, R, ep::impl::is_producer_v<H> >
{};
is_producer_v — - easy_parser, , producer- .
easy_parser_router — ...
… . , , C++ compile-time, .
, HTTP- C++ — - , . - RESTinio.
, .. -. easy_parser — . , .
, .
, , . , . .
, easy_parser easy_parser_router RESTinio . , , .
, , C++. , .
, . . .
, , - . C++ - , .
- RESTinio easy_parser_router , .
, .
- , RESTinio, easy_parser / easy_parser_router, .
, RESTinio.