aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--example.cpp20
-rw-r--r--flask.h10
-rw-r--r--http_response.h4
-rw-r--r--routing.h205
-rw-r--r--utility.h155
5 files changed, 358 insertions, 36 deletions
diff --git a/example.cpp b/example.cpp
index c518dc4..9ecf0af 100644
--- a/example.cpp
+++ b/example.cpp
@@ -1,6 +1,6 @@
#include "flask.h"
-#include <iostream>
+#include <sstream>
int main()
{
@@ -13,13 +13,23 @@ int main()
});
app.route("/about")
- ([]{
+ ([](){
return "About Flask example.";
});
- //app.route("/hello/<int>");
- //([]{
- //return "About Flask example.";
+ FLASK_ROUTE(app,"/hello/<int>")
+ ([](int count){
+ if (count > 100)
+ return flask::response(400);
+ std::ostringstream os;
+ os << count << " bottles of beer!";
+ return flask::response(os.str());
+ });
+
+ // Compile error with message "Handler type is mismatched with URL paramters"
+ //FLASK_ROUTE(app,"/another/<int>")
+ //([](int a, int b){
+ //return flask::response(500);
//});
app.port(8080)
diff --git a/flask.h b/flask.h
index 6d222e0..ec14f9a 100644
--- a/flask.h
+++ b/flask.h
@@ -7,11 +7,14 @@
#include <type_traits>
#include "http_server.h"
+#include "utility.h"
#include "routing.h"
// TEST
#include <iostream>
+#define FLASK_ROUTE(app, url) app.route<flask::black_magic::get_parameter_tag(url)>(url)
+
namespace flask
{
class Flask
@@ -26,6 +29,13 @@ namespace flask
return router_.handle(req);
}
+ template <uint64_t Tag>
+ auto route(std::string&& rule)
+ -> typename std::result_of<decltype(&Router::new_rule_tagged<Tag>)(Router, std::string&&)>::type
+ {
+ return router_.new_rule_tagged<Tag>(std::move(rule));
+ }
+
auto route(std::string&& rule)
-> typename std::result_of<decltype(&Router::new_rule)(Router, std::string&&)>::type
{
diff --git a/http_response.h b/http_response.h
index eb7926f..7b862c4 100644
--- a/http_response.h
+++ b/http_response.h
@@ -29,11 +29,11 @@ namespace flask
struct response
{
std::string body;
- int status = 200;
+ int status{200};
std::unordered_map<std::string, std::string> headers;
response() {}
explicit response(int status) : status(status) {}
response(std::string body) : body(std::move(body)) {}
- response(std::string body, int status) : body(std::move(body)), status(status) {}
+ response(int status, std::string body) : body(std::move(body)), status(status) {}
};
}
diff --git a/routing.h b/routing.h
index 79e35fd..8c3cc99 100644
--- a/routing.h
+++ b/routing.h
@@ -5,6 +5,8 @@
#include <string>
#include <tuple>
#include <unordered_map>
+#include <memory>
+#include <boost/lexical_cast.hpp>
#include "http_response.h"
@@ -13,17 +15,56 @@
namespace flask
{
- class Rule
+ class BaseRule
{
public:
- explicit Rule(std::string rule)
+ BaseRule(std::string rule)
: rule_(std::move(rule))
{
}
+
+ virtual ~BaseRule()
+ {
+ }
+
+ BaseRule& name(std::string name)
+ {
+ name_ = std::move(name);
+ return *this;
+ }
+
+ virtual void validate()
+ {
+ }
+ virtual response handle(const request&)
+ {
+ return response(400);
+ }
+
+ protected:
+ std::string rule_;
+ std::string name_;
+ };
+
+ class Rule : public BaseRule
+ {
+ public:
+ Rule(std::string rule)
+ : BaseRule(std::move(rule))
+ {
+ }
+ Rule& name(std::string name)
+ {
+ name_ = std::move(name);
+ return *this;
+ }
+
template <typename Func>
void operator()(Func&& f)
{
+ static_assert(black_magic::CallHelper<Func, black_magic::S<>>::value,
+ "Handler type is mismatched with URL paramters");
handler_ = [f = std::move(f)]{
return response(f());
};
@@ -32,41 +73,75 @@ namespace flask
template <typename Func>
void operator()(std::string name, Func&& f)
{
+ static_assert(black_magic::CallHelper<Func, black_magic::S<>>::value,
+ "Handler type is mismatched with URL paramters");
name_ = std::move(name);
handler_ = [f = std::move(f)]{
return response(f());
};
}
- bool match(const request& req)
+ void validate()
{
- // FIXME need url parsing
- return req.url == rule_;
+ if (!handler_)
+ {
+ throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
+ }
}
- Rule& name(std::string name)
+ response handle(const request&)
+ {
+ return handler_();
+ }
+
+ protected:
+ std::function<response()> handler_;
+ };
+
+ template <typename ... Args>
+ class TaggedRule : public BaseRule
+ {
+ public:
+ TaggedRule(std::string rule)
+ : BaseRule(std::move(rule))
+ {
+ }
+
+ TaggedRule<Args...>& name(std::string name)
{
name_ = std::move(name);
return *this;
}
- void validate()
+ template <typename Func>
+ void operator()(Func&& f)
{
- if (!handler_)
- {
- throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
- }
+ static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
+ "Handler type is mismatched with URL paramters");
+ handler_ = [f = std::move(f)](Args ... args){
+ return response(f(args...));
+ };
+ }
+
+ template <typename Func>
+ void operator()(std::string name, Func&& f)
+ {
+ static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
+ "Handler type is mismatched with URL paramters");
+ name_ = std::move(name);
+ handler_ = [f = std::move(f)](Args ... args){
+ return response(f(args...));
+ };
}
response handle(const request&)
{
- return handler_();
+ //return handler_();
+ return response(500);
}
private:
- std::string rule_;
- std::string name_;
- std::function<response()> handler_;
+ std::function<response(Args...)> handler_;
};
constexpr const size_t INVALID_RULE_INDEX{static_cast<size_t>(-1)};
@@ -77,14 +152,41 @@ namespace flask
{
std::unordered_map<std::string, size_t> children;
size_t rule_index{INVALID_RULE_INDEX};
+ size_t child_for_int{INVALID_RULE_INDEX};
+ size_t child_for_uint{INVALID_RULE_INDEX};
+ size_t child_for_float{INVALID_RULE_INDEX};
+ size_t child_for_str{INVALID_RULE_INDEX};
+ size_t child_for_path{INVALID_RULE_INDEX};
+ bool IsSimpleNode() const
+ {
+ return
+ rule_index == INVALID_RULE_INDEX &&
+ child_for_int == INVALID_RULE_INDEX &&
+ child_for_uint == INVALID_RULE_INDEX &&
+ child_for_float == INVALID_RULE_INDEX &&
+ child_for_str == INVALID_RULE_INDEX &&
+ child_for_path == INVALID_RULE_INDEX;
+ }
};
Trie() : nodes_(1)
{
}
+private:
+ void optimizeNode(Node* node)
+ {
+ for(auto& kv : node->children)
+ {
+ Node* child = &nodes_[kv.second];
+ optimizeNode(child);
+ }
+ }
+
+public:
void optimize()
{
+ optimizeNode(head());
}
size_t find(const request& req, const Node* node = nullptr, size_t pos = 0) const
@@ -116,16 +218,54 @@ namespace flask
void add(const std::string& url, size_t rule_index)
{
- size_t idx = 0;
- for(char c:url)
+ size_t idx{0};
+
+ for(size_t i = 0; i < url.size(); i ++)
{
- std::string piece(&c, 1);
- //std::string piece("/");
- if (!nodes_[idx].children.count(piece))
+ char c = url[i];
+ if (c == '<')
{
- nodes_[idx].children.emplace(piece, new_node());
+ if (url.compare(i, 5, "<int>") == 0)
+ {
+ idx = nodes_[idx].child_for_int = new_node();
+ i += 5;
+ }
+ else if (url.compare(i, 6, "<uint>") == 0)
+ {
+ idx = nodes_[idx].child_for_uint = new_node();
+ i += 6;
+ }
+ else if (url.compare(i, 7, "<float>") == 0 ||
+ url.compare(i, 8, "<double>") == 0)
+ {
+ idx = nodes_[idx].child_for_float = new_node();
+ i += 7;
+ }
+ else if (url.compare(i, 5, "<str>") == 0)
+ {
+ idx = nodes_[idx].child_for_str = new_node();
+ i += 5;
+ }
+ else if (url.compare(i, 6, "<path>") == 0)
+ {
+ idx = nodes_[idx].child_for_path = new_node();
+ i += 6;
+ }
+ else
+ {
+ throw std::runtime_error("Invalid url: " + url + " (" + boost::lexical_cast<std::string>(i) + ")");
+ }
+ i --;
+ }
+ else
+ {
+ std::string piece(&c, 1);
+ if (!nodes_[idx].children.count(piece))
+ {
+ nodes_[idx].children.emplace(piece, new_node());
+ }
+ idx = nodes_[idx].children[piece];
}
- idx = nodes_[idx].children[piece];
}
if (nodes_[idx].rule_index != INVALID_RULE_INDEX)
throw std::runtime_error("handler already exists for " + url);
@@ -154,11 +294,22 @@ namespace flask
class Router
{
public:
+ template <uint64_t N>
+ typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule)
+ {
+ using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
+ auto ruleObject = new RuleT(rule);
+ rules_.emplace_back(ruleObject);
+ trie_.add(rule, rules_.size() - 1);
+ return *ruleObject;
+ }
+
Rule& new_rule(const std::string& rule)
{
- rules_.emplace_back(rule);
+ Rule* r(new Rule(rule));
+ rules_.emplace_back(r);
trie_.add(rule, rules_.size() - 1);
- return rules_.back();
+ return *r;
}
void validate()
@@ -166,7 +317,7 @@ namespace flask
trie_.optimize();
for(auto& rule:rules_)
{
- rule.validate();
+ rule->validate();
}
}
@@ -180,10 +331,10 @@ namespace flask
if (rule_index >= rules_.size())
throw std::runtime_error("Trie internal structure corrupted!");
- return rules_[rule_index].handle(req);
+ return rules_[rule_index]->handle(req);
}
private:
- std::vector<Rule> rules_;
+ std::vector<std::unique_ptr<BaseRule>> rules_;
Trie trie_;
};
}
diff --git a/utility.h b/utility.h
index 8499532..2f3d84a 100644
--- a/utility.h
+++ b/utility.h
@@ -1,5 +1,8 @@
#pragma once
+#include <stdint.h>
+#include <stdexcept>
+
namespace flask
{
namespace black_magic
@@ -13,7 +16,6 @@ namespace flask
return i >= len ? throw OutOfRange(i, len) : i;
}
- // from http://akrzemi1.wordpress.com/2011/05/11/parsing-strings-at-compile-time-part-i/
class const_str
{
const char * const begin_;
@@ -43,9 +45,158 @@ namespace flask
return s[p] == '>' ? p : find_closing_tag(s, p+1);
}
- constexpr int count(const_str s, int i=0)
+ constexpr bool is_valid(const_str s, unsigned i = 0, int f = 0)
+ {
+ return
+ i == s.size()
+ ? f == 0 :
+ f < 0 || f >= 2
+ ? false :
+ s[i] == '<'
+ ? is_valid(s, i+1, f+1) :
+ s[i] == '>'
+ ? is_valid(s, i+1, f-1) :
+ is_valid(s, i+1, f);
+ }
+
+ constexpr int count(const_str s, unsigned i=0)
{
return i == s.size() ? 0 : s[i] == '<' ? 1+count(s,i+1) : count(s,i+1);
}
+
+ constexpr bool is_equ_n(const_str a, unsigned ai, const_str b, unsigned bi, unsigned n)
+ {
+ return
+ ai + n > a.size() || bi + n > b.size()
+ ? false :
+ n == 0
+ ? true :
+ a[ai] != b[bi]
+ ? false :
+ is_equ_n(a,ai+1,b,bi+1,n-1);
+ }
+
+ constexpr bool is_int(const_str s, unsigned i)
+ {
+ return is_equ_n(s, i, "<int>", 0, 5);
+ }
+
+ constexpr bool is_uint(const_str s, unsigned i)
+ {
+ return is_equ_n(s, i, "<uint>", 0, 6);
+ }
+
+ constexpr bool is_float(const_str s, unsigned i)
+ {
+ return is_equ_n(s, i, "<float>", 0, 7) ||
+ is_equ_n(s, i, "<double>", 0, 8);
+ }
+
+ constexpr bool is_str(const_str s, unsigned i)
+ {
+ return is_equ_n(s, i, "<str>", 0, 5);
+ }
+
+ constexpr bool is_path(const_str s, unsigned i)
+ {
+ return is_equ_n(s, i, "<path>", 0, 6);
+ }
+
+ constexpr uint64_t get_parameter_tag(const_str s, unsigned p = 0)
+ {
+ return
+ p == s.size()
+ ? 0 :
+ s[p] == '<' ? (
+ is_int(s, p)
+ ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 1 :
+ is_uint(s, p)
+ ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 2 :
+ is_float(s, p)
+ ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 3 :
+ is_str(s, p)
+ ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 4 :
+ is_path(s, p)
+ ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 5 :
+ throw std::runtime_error("invalid parameter type")
+ ) :
+ get_parameter_tag(s, p+1);
+ }
+
+ template <typename ... T>
+ struct S
+ {
+ template <typename U>
+ using push = S<U, T...>;
+ template <template<typename ... Args> class U>
+ using rebind = U<T...>;
+ };
+template <typename F, typename Set>
+ struct CallHelper;
+ template <typename F, typename ...Args>
+ struct CallHelper<F, S<Args...>>
+ {
+ template <typename F1, typename ...Args1, typename =
+ decltype(std::declval<F1>()(std::declval<Args1>()...))
+ >
+ static char __test(int);
+
+ template <typename ...>
+ static int __test(...);
+
+ static constexpr bool value = sizeof(__test<F, Args...>(0)) == sizeof(char);
+ };
+
+
+ template <int N>
+ struct single_tag_to_type
+ {
+ };
+
+ template <>
+ struct single_tag_to_type<1>
+ {
+ using type = int64_t;
+ };
+
+ template <>
+ struct single_tag_to_type<2>
+ {
+ using type = uint64_t;
+ };
+
+ template <>
+ struct single_tag_to_type<3>
+ {
+ using type = double;
+ };
+
+ template <>
+ struct single_tag_to_type<4>
+ {
+ using type = std::string;
+ };
+
+ template <>
+ struct single_tag_to_type<5>
+ {
+ using type = std::string;
+ };
+
+
+ template <uint64_t Tag>
+ struct arguments
+ {
+ using subarguments = typename arguments<Tag/6>::type;
+ using type =
+ typename subarguments::template push<typename single_tag_to_type<Tag%6>::type>;
+ };
+
+ template <>
+ struct arguments<0>
+ {
+ using type = S<>;
+ };
+
}
}