Last active
March 29, 2018 23:57
-
-
Save zxmarcos/e28c57a6ef0c560953235e084f9626e8 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> | |
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 { | |
#if 1 | |
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; | |
} | |
}; | |
#else | |
struct url_matcher { | |
string url; | |
url_matcher(const string &surl) : url(surl) { | |
} | |
bool matches(const string &url) { | |
false; | |
} | |
vector<string> extract(const string &url) const { | |
vector<string> values; | |
return values; | |
} | |
}; | |
#endif | |
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; | |
} | |
} | |
struct xpto_router { | |
function<void(const string &url)> cb; | |
vector<pair<detail::url_matcher, function<void(const string &)>>> 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 ArgList = typename traits::func_traits<Callable>::args_type; | |
auto url_matcher = detail::url_matcher(url); | |
routes.emplace_back(make_pair(url_matcher, [=](const string &url) { | |
invoke_helper(f, | |
parse_arguments<ArgList>(url_matcher.extract(url)), | |
make_index_sequence<tuple_size<ArgList>::value>{}); | |
})); | |
} | |
void dispatch(const string url) { | |
for (auto &it : routes) { | |
if (it.first.matches(url)) { | |
it.second(url); | |
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 router; | |
router.bind("/<id>", [](int a) { cout << a << endl; }); | |
router.dispatch("/123"); | |
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