Last active
March 30, 2018 02:49
-
-
Save zxmarcos/78c71309967766853d34c7c573842984 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
#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