
RESTinio , unser kleiner eingebetteter HTTP-Server, entwickelt sich weiter . Eines der charakteristischen Merkmale von RESTinio ist, dass bei seiner Implementierung mehrstöckige C ++ - Vorlagen aktiv verwendet werden (wie bereits erwähnt: 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.