A modern, header-only request router for C++
This library is designed to route requests to an associated callback. Requests are matched using a pattern, which may contain embedded regular expressions. The data matched with the regular expression is then parsed and provided to the callback.
The router matches incoming requests against list of URL patterns. A URL pattern consists
of a combination of literal data and slugs, matched using a regular expression. Slugs are
opened with a {
and closed with a }
.
The following example pattern
/test/{\d+}/{\w+}
would match targets like /test/123/hello
and /test/999/bye
, but not /test/xyz/abc
(because
\d only matches digits).
The library does not impose a particular callback signature on the user, instead letting the user choose the signature that suits their needs.
The list of patterns and callbacks is stored in a routing table, which is available in the
router::table
class. This class is templated on the signature of the used callback functions.
Say, your callback functions follow the int(std::string&& body)
signature, you would create
an instance of router::table<int(std::string&&)>
. On this instance you can register your URL
patterns and their associated callbacks using the add()
member function:
router.add<&callback>("pattern");
router.add<&class::callback>("pattern", instance_pointer);
The first variant is used for registering a free function, while the second variant is used for registering a member function as the callback. Callbacks may be registered on any class, as long as they have the correct signature.
If the URL patterns contain slugs, you are probably interested in the data they hold. There are three ways to get this data.
- Accept the data as separate parameters to the callback
- Accept a tuple with the slug data in the callback
- Accept a custom data transfer object in the callback
The first two options are the easiest. Let's assume our previous examples of a callback definition
of int(std::string&&)
and a pattern of /test/{\d+}/{\w+}
. From the pattern, it's clear that the
first slug should contain numeric data, and the second slug contains a string. We could therefore
define and register our callback like this:
int callback(std::string&& body, int numeric, std::string&& word);
int callback(std::string&& body, std::tuple<int, std::string>&& slugs);
When a matching request is routed, the slugs are automatically parsed and converted to the right type before the callback is invoked.
It is also possible to make a dedicated struct, a so-called data transfer object, to receive the slug data.
The data transfer object needs to be annotated so that cpprouter knows how to parse the slug
data, this is done by defining a using
alias on the class.
class slug_dto {
int numeric;
std::string word;
using dto = router::dto<slug_dto>
::bind<&slug_dto::numeric>
::bind<&slug_dto::word>;
};
Assuming the same callback signature as before, the router and callback could be declared as follows:
int callback(std::string&& body, slug_dto&& slugs);
See the examples (TODO)