Skip to content

Instantly share code, notes, and snippets.

@zxmarcos
Last active March 30, 2018 02:49
Show Gist options
  • Save zxmarcos/78c71309967766853d34c7c573842984 to your computer and use it in GitHub Desktop.
Save zxmarcos/78c71309967766853d34c7c573842984 to your computer and use it in GitHub Desktop.
#include <iostream>
#include <string>
#include <functional>
#include <tuple>
#include <utility>
#include <regex>
#include <vector>
#include <tuple-utils/tuple_subset.h>
using namespace std;
namespace traits {
template <typename T>
struct func_traits;
template<typename T>
struct func_traits : public func_traits<decltype(&T::operator())> {};
template<typename R, typename C, typename ...Args>
struct func_traits<R(C::*)(Args...)const> {
using return_type = R;
using args_type = tuple<Args...>;
};
template <typename R, typename ...Args>
struct func_traits<function<R(Args...)>> {
using return_type = R;
using args_type = tuple<Args...>;
};
template <typename R, typename ...Args>
struct func_traits<R(*)(Args...)> {
using return_type = R;
using args_type = tuple<Args...>;
};
}
namespace detail {
struct url_matcher {
string url;
regex pattern;
url_matcher(const string &surl) : url(surl) {
regex parameters("(<[^>]+>)", regex_constants::ECMAScript);
string str_pattern = string("^") + regex_replace(surl, parameters, "([^/]+)") + string("$");
pattern = regex(str_pattern, regex_constants::ECMAScript);
cout << str_pattern << endl;
}
bool matches(const string &url) {
return regex_search(url, pattern);
}
vector<string> extract(const string &url) const {
vector<string> values;
smatch matcher;
if (regex_match(url, matcher, pattern)) {
for (size_t i = 1; i < matcher.size(); i++) {
values.push_back(matcher[i].str());
}
}
return values;
}
};
template <typename T>
void reader(T &result, const string &value)
{
// static_assert(false, "XPTO router: URL reader for type not implemented!");
}
template <>
void reader(int &result, const string &value)
{
result = stoi(value);
}
template<>
void reader(string &result, const string &value)
{
result = value;
}
}
template <typename ...Context>
struct xpto_router {
using ContextT = tuple<Context...>;
using ContextSize = tuple_size<ContextT>;
vector<pair<detail::url_matcher, function<void(const string &, ContextT &)>>> routes;
template<typename F, typename T, size_t...index>
static void invoke_helper(F&& f, T&& args, index_sequence<index...>)
{
f(get<index>(forward<T>(args))...);
}
template<size_t index, typename ArgList>
int parse_value_impl(ArgList &args, const vector<string> &values)
{
if (index < values.size()) {
detail::reader<typename tuple_element<index, ArgList>::type>(get<index>(args), values[index]);
}
return 0;
}
template<typename ArgList, size_t...index>
auto parse(ArgList& args, const vector<string> &values, index_sequence<index...>)
{
auto x = { parse_value_impl<index>(args, values)... };
(void ) x;
}
template <typename ArgList>
ArgList parse_arguments(const vector<string> &values)
{
using Size = integral_constant<size_t, tuple_size<ArgList>::value>;
if (values.size() != Size::value) {
throw runtime_error("Arguments mistmatch!");
}
ArgList args;
parse<ArgList>(args, values, make_index_sequence<Size::value>{});
return args;
}
template <typename Callable>
void bind(const string url, Callable f) {
using FArgList = typename traits::func_traits<Callable>::args_type;
using ArgList = decltype(tupleutils::tuple_subset<
ContextSize::value,
tuple_size<FArgList>::value - ContextSize::value>(FArgList{}));
auto url_matcher = detail::url_matcher(url);
routes.emplace_back(make_pair(url_matcher, [=](const string &url, ContextT &ctx) {
invoke_helper(f,
tuple_cat(ctx, parse_arguments<ArgList>(url_matcher.extract(url))),
make_index_sequence<tuple_size<FArgList>::value>{});
}));
}
void dispatch(const string &url, Context... args) {
for (auto &it : routes) {
if (it.first.matches(url)) {
auto context = make_tuple(args...);
it.second(url, context);
break;
}
}
}
};
void foo(int a) {
cout << a << endl;
}
void bar(int a, string b) {
cout << a << b << endl;
}
void test(int a, int c) {
cout << "test" << endl;
}
int main(int, char **)
{
xpto_router<string> router;
router.bind("/<id>", [](string ctx, int id) { cout << ctx << ' ' << id << endl; });
router.dispatch("/123", "teste");
// router.bind("/<id>", foo);
// router.bind("/test/<x>", test);
// router.bind("/<id>/<name>", bar);
// router.dispatch("/42");
// router.dispatch("/42/jose");
// try {
// router.dispatch("/test/1");
// } catch (const std::exception &e) {
// cout << e.what() << endl;
// }
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment