diff options
-rw-r--r-- | routing.h | 110 | ||||
-rw-r--r-- | unittest.cpp | 23 | ||||
-rw-r--r-- | utility.h | 15 |
3 files changed, 126 insertions, 22 deletions
@@ -1,6 +1,9 @@ #pragma once #include <cstdint> +#include <utility> +#include <string> +#include <tuple> #include "utility.h" @@ -8,32 +11,121 @@ namespace flask { namespace black_magic { - constexpr bool is_equ_n(StrWrap a, int ai, StrWrap b, int bi, int n) + constexpr bool is_equ_n(const_str a, int ai, const_str b, int bi, int n) { - return n == 0 ? true : a[ai] != b[bi] ? false : is_equ_n(a,ai+1,b,bi+1,n-1); + 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(StrWrap s, int i) + constexpr bool is_int(const_str s, int i) { return is_equ_n(s, i, "<int>", 0, 5); } - constexpr bool is_str(StrWrap s, int i) + constexpr bool is_float(const_str s, int 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, int i) { return is_equ_n(s, i, "<str>", 0, 5); } - //constexpr ? parse_route(StrWrap s) - //{ - //return - //} + constexpr bool is_path(const_str s, int i) + { + return is_equ_n(s, i, "<path>", 0, 6); + } + + template <typename ...Args> + struct Caller + { + template <typename F> + void operator()(F f, Args... args) + { + f(args...); + } + }; + + + template <int N, typename ... Args> struct S; + template <int N, typename Arg, typename ... Args> struct S<N, Arg, Args...> { + static_assert(N <= 4+1, "too many routing arguments (maximum 5)"); + template <typename T> + using push = typename std::conditional<(N>4), S<N, Arg, Args...>, S<N+1, Arg, Args..., T>>::type; + using pop = S<N-1, Args...>; + }; + template <> struct S<0> + { + template <typename T> + using push = S<1, T>; + }; + + template <typename F, typename Set> + struct CallHelper; + template <typename F, int N, typename ...Args> + struct CallHelper<F, S<N, 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); + }; + + static_assert(CallHelper<void(), S<0>>::value, ""); + static_assert(CallHelper<void(int), S<1, int>>::value, ""); + static_assert(!CallHelper<void(int), S<0>>::value, ""); + static_assert(!CallHelper<void(int), S<2, int, int>>::value, ""); + + template <typename F, + typename Set = S<0>> + constexpr bool validate_helper(const_str rule, unsigned i=0) + { + return + i == rule.size() + ? CallHelper<F, Set>::value : + is_int(rule, i) + ? validate_helper<F, typename Set::template push<int>>(rule, find_closing_tag(rule, i+1)+1) : + is_float(rule, i) + ? validate_helper<F, typename Set::template push<double>>(rule, find_closing_tag(rule, i+1)+1) : + is_str(rule, i) + ? validate_helper<F, typename Set::template push<std::string>>(rule, find_closing_tag(rule, i+1)+1) : + is_path(rule, i) + ? validate_helper<F, typename Set::template push<std::string>>(rule, find_closing_tag(rule, i+1)+1) : + validate_helper<F, Set>(rule, i+1) + ; + } + + static_assert(validate_helper<void()>("/"),""); + static_assert(validate_helper<void(int)>("/<int>"),""); + static_assert(!validate_helper<void()>("/<int>"),""); } class Router { public: - constexpr Router(black_magic::StrWrap s) + constexpr Router(black_magic::const_str rule) : rule(rule) + { + } + + template <typename F> + constexpr bool validate() const { + return black_magic::validate_helper<F>(rule); } + private: + black_magic::const_str rule; }; } diff --git a/unittest.cpp b/unittest.cpp index 0cea0c7..66c58a0 100644 --- a/unittest.cpp +++ b/unittest.cpp @@ -5,23 +5,36 @@ using namespace flask; using namespace flask::black_magic; -template <int N> struct T{}; +template <int N> struct ThrowTest{}; int main() { try { - throw T<is_int("1<int>22",0)>(); + throw ThrowTest<is_int("1<int>22",0)>(); } - catch(T<0>) + catch(ThrowTest<0>) { } try { - throw T<is_int("1<int>22",1)>(); + throw ThrowTest<is_int("1<int>22",1)>(); } - catch(T<1>) + catch(ThrowTest<1>) { } + + { + constexpr Router r = Router("/"); + static_assert(r.validate<void()>(), "Good handler"); + static_assert(!r.validate<void(int)>(), "Bad handler - no int argument"); + } + { + constexpr Router r = Router("/blog/<int>"); + static_assert(!r.validate<void()>(), "Bad handler - need argument"); + static_assert(r.validate<void(int)>(), "Good handler"); + static_assert(!r.validate<void(std::string)>(), "Bad handler - int is not convertible to std::string"); + static_assert(r.validate<void(double)>(), "Acceptable handler - int will be converted to double"); + } } @@ -14,37 +14,36 @@ namespace flask } // from http://akrzemi1.wordpress.com/2011/05/11/parsing-strings-at-compile-time-part-i/ - class StrWrap + class const_str { const char * const begin_; unsigned size_; public: template< unsigned N > - constexpr StrWrap( const char(&arr)[N] ) : begin_(arr), size_(N - 1) { + constexpr const_str( const char(&arr)[N] ) : begin_(arr), size_(N - 1) { static_assert( N >= 1, "not a string literal"); } - - constexpr char operator[]( unsigned i ) { + constexpr char operator[]( unsigned i ) const { return requires_in_range(i, size_), begin_[i]; } - constexpr operator const char *() { + constexpr operator const char *() const { return begin_; } - constexpr unsigned size() { + constexpr unsigned size() const { return size_; } }; - constexpr int find_closing_tag(StrWrap s, std::size_t p) + constexpr unsigned find_closing_tag(const_str s, unsigned p) { return s[p] == '>' ? p : find_closing_tag(s, p+1); } - constexpr int count(StrWrap s, int i=0) + constexpr int count(const_str s, int i=0) { return i == s.size() ? 0 : s[i] == '<' ? 1+count(s,i+1) : count(s,i+1); } |