Deferred caller, delegator implementation with variadic template
Recently I started playing with a small toy project – tiny web server with C++. The first requirement I wanted to implement was the ‘static type routing handler’ as shown from the crow project.
Which means, users should be able to register its routing handlers as the following manner:
In order to do that, two things come up to the list:
- constexpr string parser to determine the parameter type (e.g <int>, <string>…)
- being able to register lambda which can have variadic template arguments
Regarding #2 issue, people would agree it should have been called ‘delegator’ or ‘deferred caller’. Its concept is basically same. Accepts a functor, saves it and calls it later. Then, how can I save functor which can have variadic number and types without using variant?
I got a hint from the Codeproject article, and implemented as follows. The routing_delegator structure is a container owns a pointer to the routing_record interface pointer.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct routing_delegator | |
{ | |
routing_delegator() {} // default | |
routing_delegator(std::vector<std::string> r) : ruleset(r) {} | |
// register functor handler | |
template <typename F> | |
void to(F&& f) | |
{ | |
routing_data = std::make_unique<typed_routing_recored<decltype(&F::operator())>>(f); | |
} | |
// invoke handler | |
template <typename… Args> | |
reply handle(Args&&… args) | |
{ | |
auto tuple = std::make_tuple<Args…>(args…); | |
return routing_data->handle(&tuple); | |
} | |
std::unique_ptr<routing_record> routing_data = nullptr; | |
std::vector<std::string> ruleset; | |
}; |
‘to(F&& f)’ is lambda subscribing function. If user tries to pass a lambda to the function, you can figure out the lambda’s parameters by specifying operator() functor signature to a specialized template below. (refer to this SO link)
The typed_routing_recored is the template class, and lambda signature will be deduced, and the typed figured out all will be saved to the std::function.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct routing_record | |
{ | |
virtual reply handle(void*) abstract; | |
}; | |
template <typename T> | |
struct typed_routing_recored; | |
template <typename RetType, typename classTy, typename… ArgType> | |
struct typed_routing_recored<RetType(classTy::*)(ArgType…) const> : public routing_record | |
{ | |
std::function<RetType(ArgType…)> stdfn; | |
typed_routing_recored(RetType(*pfn)(ArgType…)) | |
: stdfn(pfn) {} | |
virtual reply handle(void* args) override | |
{ | |
static const std::size_t typesize = sizeof…(ArgType); | |
auto tuple = static_cast<std::tuple<typename std::decay<ArgType>::type…>*> (args); | |
return _handle(*tuple, std::index_sequence_for<ArgType…>()); | |
} | |
template <std::size_t… Is> | |
RetType _handle(const std::tuple<ArgType…>& tuple, std::index_sequence<Is…>) | |
{ | |
return (stdfn)(std::get<Is>(tuple)…); | |
} | |
}; |
How about calling the saved functors? The routing_record is an interface and declares one abstract function – which is ‘handle(void*)’. As you might guess from its name, void* is the key point to enable accept variadic sized, typed arguments.
If user calls ‘handle’ with parameters, routing_delegator::handle(Args&&… args) wconverterts those parameters into tuple and passes it to the routing_record::handle(void*) with its address
And finally child class’s handle(void*) implementation will casts into tuple again and delivers it to the actual std::function functor.
You can check out the all code here, https://github.com/heejune/tinyweb-cppserver/blob/develop/router.h
Please be warned, it’s still in development and all experimental code. Thanks!
Heejune.