Advertisement
Advertisement

More Related Content

Advertisement

Recently uploaded(20)

Advertisement

20150212 c++11 features used in crow

  1. C++11 features used in Crow Crow를 통해 살펴보는 C++11의 새 기능 하재승 @ipkn ipknhama@gmail.com
  2. Crow Framework을 작성하기 위해 사용한 기법들을 소개 (TMP 중심) auto, lambda등 일반적인 C++ 개발에서 접하기 쉬운 기법들에 대한 설명은 생략 목표
  3. 목표
  4. Crow C++ Framework for writing Web server http://github.com/ipkn/crow GitHub Star 1063개 Hacker News vote 194 https://news.ycombinator.com/item?id=8002604
  5. Web server 개념 브라우저로 접속해서 요청을 보내면 해당 요청에 맞는 응답을 보내줌 GET / HTTP/1.1 Host: www.example.com HTTP/1.1 200 OK Content-Length: 12 ... Hello World!
  6. 간단한 Crow 예제 #include “crow_all.h” int main() { crow::SimpleApp app; CROW_ROUTE(app, “/”) ([]{ return “Hello world!”; }); app.port(8080).run(); return 0; }
  7. … as Flask (from Quickstart) from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run()
  8. 목차 Routing Compile time Run time Middleware 서버 구조
  9. 소개될 C++11 기능 template metaprogramming constexpr variadic template SFINAE decltype, declval std::chrono, operator ””, enum class
  10. 왜 굳이 C++로? https://github.com/ipkn/crow-benchmark
  11. C++? 빠른 속도 멀티 패러다임 언어 절차형 + OOP + Generic (template)
  12. template compile time code generation vector<T> → vector<int>, vector<string> T는 컴파일 타임에 결정되어야 한다.
  13. Crow 클래스 구조 App Server Connection Router Rule (TaggedRule) Trie
  14. Routing
  15. Routing - CROW_ROUTE 어떤 요청에 어떤 응답을 줄지 지정 CROW_ROUTE(app, “/”) CROW_ROUTE(app, “/post/all”) CROW_ROUTE(app, “/post/<int>”) CROW_ROUTE(app, “/near/<double>/<double>”)
  16. CROW_ROUTE 목표 쓰기 편하게 실수하기 어렵게 잘못된 핸들러를 주면 에러나게 빠르게 실제 함수 호출 시 오버헤드를 최소화
  17. CROW_ROUTE 기능 (1) 여러 타입의 인자 지원 <int> <uint> <double> <str> <path>
  18. CROW_ROUTE 기능 (2) 컴파일 시간에 핸들러 타입 체크 CROW_ROUTE(app, “/post/<int>”) ([](string a) … ([](int a, int b) … ([](int a) ...
  19. CROW_ROUTE 기능 (3) 여러 종류의 핸들러 지원 [](…) [](const request& req, …) [](const request& req, response& res, …) * 어떻게 여러 종류를 지원하는지에 관해선 나중에 설명 예정
  20. 실제 CROW_ROUTE 구현 #define CROW_ROUTE(app, url) app.route< crow::black_magic::get_parameter_tag(url) >(url)
  21. black_magic ???!!! 이름을 말할 수 없는 그분
  22. app.route<get_parameter_tag(url)>(url) 핸들러 형태 체크 → 컴파일 타임 → 템플릿 인자 실행 중에 URL 체크 → 런타임 → 함수 인자 실제 CROW_ROUTE 구현 (2)
  23. app.route<get_parameter_tag(url)>(url) 핸들러 형태 체크 → 컴파일 타임 → 템플릿 인자 실행 중에 URL 체크 → 런타임 → 함수 인자 실제 CROW_ROUTE 구현 (2)
  24. app.route<get_parameter_tag(url)>(url) 핸들러 형태 체크 → 컴파일 타임 → 템플릿 인자 실행 중에 URL 체크 → 런타임 → 함수 인자 실제 CROW_ROUTE 구현 (2)
  25. app.route<get_parameter_tag(url)>(url) 스트링 값(“abc” 등)은 템플릿 인자로 사용할 수 없음 url로 부터 인자에 대한 정보를 얻어 템플릿 인자로 넣을 수 있는 값으로 변환 템플릿 인자부터 ..
  26. constexpr function constexpr int fact(int n) { return (n <= 1) ? 1 : fact(n-1)*n; } char arr[fact(5)];
  27. constexpr 컴파일 타임에 계산할 수도 있는 함수 또는 값 cin >> n; cout << fact(n); // 런타임 fact(5) == 120, 컴파일 타임에 계산 가능 → 배열의 크기, 템플릿 인자 등으로 사용 가능
  28. get_parameter_tag URL 을 받아 인자 정보를 가지는 숫자로 변환 인자가 없는 경우 0 URL 인자는 5종류: <int>=1 <uint>=2 … 6진법으로 표현 (자세한 구현은 생략)
  29. get_parameter_tag “/” “/post” “/post/<int>” “/near/<int>/<int>” “/put_score/<double>” “/wiki/<path>” 0 0 1 11 3 5
  30. 숫자에서 다시 타입 리스트 복원 0 → <> 1 → <int> 11 → <int, int> 13 → <double, int> 4 → <string> 5 → <string> * path도 타입은 string
  31. black_magic::S variadic template으로 타입 리스트를 구현 S<int, double, string> 이 가능 S<a,b>::push<c> === S<c, a, b> S<a>::push_back<b> === S<a, b> S<a,b,c>::rebind<Rule> === Rule<a,b,c>
  32. black_magic::S<T…> 구현 (1) template <typename ... T> struct S { template <typename U> using push = S<U, T...>; S<a,b>::push<c> === S<c, a, b> * using A = B; === typedef B A;
  33. black_magic::S<T…> 구현 (2) template <template <typename ...> class U> using rebind = U<T...>; S<a,b,c>::rebind<Rule> == Rule<a,b,c>
  34. black_magic::arguments<uint64_t> get_parameter_tag 값을 타입 리스트로 arguments<0>::type == S<> arguments<3>::type == S<double> arguments<11>::type == S<int, int>
  35. arguments 구현 template <uint64_t Tag> struct arguments { using subarg = typename arguments<Tag/6>::type; using type = typename subargs::template push< typename single_tag_to_type<Tag%6>::type >; };
  36. 실제로 일어나는 일 argument<31> subarg = S<double> type = S<int, double> arg<3> subarg = S<> type = S<double>
  37. partial template specialization template <int N> struct single_tag_to_type {}; template <> struct single_tag_to_type<1> { using type = int64_t; }; // C++11은 아니지만..
  38. arguments 구현 template <> struct arguments<0> { using type = S<>; };
  39. URL이 라우팅 규칙이 되기까지 get_parameter_tag(“/<int>/<int>”) → arguments<11> → S<int, int> → TaggedRule<int, int> Router vector<unique_ptr<BaseRule>> rules_;
  40. Q&A for Routing 1 get_parameter_tag(“/<int>/<int>”) → arguments<11> → S<int, int> → TaggedRule<int, int>
  41. 실제 핸들러 호출하기 Routing 2
  42. 남은 문제 /v<uint>/<string>/<path> “/v2/wiki/example/document” TaggedRule<uint, string, string> handler [](uint32_t ver, string func, string path)
  43. Rule, TaggedRule 최종적으로 호출될 핸들러를 가지고 있음 app.route 의 리턴 값 void handle( const request& req, response& res, const routing_params& params)
  44. Trie based routing 여러 rule 들의 URL을 모아서 요청이 들어왔을 때 어느 Rule에서 처리할 지 판단 트리 구조로 성능 향상
  45. Trie example GET statuses/home_timeline GET statuses/retweets/:id GET statuses/retweets_of_me POST statuses/retweet/:id (from Twitter REST API)
  46. statuses/ retweethome_timeline s/ _of_meuint uint /
  47. Trie add( string url, rule ) find( string url ) -> rule, routing_params http://en.wikipedia.org/wiki/Trie routing.h
  48. routing_params vector<int> int_params; vector<uint> uint_params; vector<double> double_params; vector<string> string_params;
  49. Trie example (1) /near/<double>/<double> “/near/37.1/10.1” routing_params.double_params == {37.1, 10.1}
  50. Trie example (2) /v<uint>/<string>/<path> “/v2/wiki/example/document” routing_params .uint_params == {2} .string_params == {“wiki”, “example/document”}
  51. 핸들러 호출하기 /v<uint>/<string>/<path> [](unsigned int, string, string) handler(rp.uint_params[0], rp.string_params[0], rp.string_params[1]) compile time
  52. 호출부 구현 template <typename CP, int NInt, int NUint, int NDouble, int NString, typename S1, typename S2> struct call; S1 - URL로 부터 정의된 핸들러 인자 타입의 리스트 S2 - 호출을 위한 타입+인덱스 정보의 리스트 N?? - 인자 처리를 위한 인덱스 값들, CP - 호출 파라미터
  53. TaggedRule<Args…>::handle void handle(req, res, routing_params) { call<CallParams, 0, 0, 0, 0, S<Args…>, S<>>( call_params); } call_params req + res + routing_params + handler
  54. call 호출 과정 - /v<uint>/<string>/<path> 0 0 [uint, str, str] [] 1 0 [str, str] [(uint, 0)] 1 1 [str] [(uint, 0) (str, 0)] 1 2 [] [(uint, 0) (str, 0) (str, 1)] call을 재귀호출 NUint NStr S1 S2
  55. call 호출 과정 - /v<uint>/<string>/<path> 0 0 [uint, str, str] [] NUint NStr S1 S2
  56. call 호출 과정 - /v<uint>/<string>/<path> 0 0 [uint, str, str] [] → 1 0 [str, str] [<uint, 0>] NUint NStr S1 S2
  57. struct call<CP, NInt, NUint, NDouble, NString, S< int64_t, Args1...>, S<Args2...>> { void operator()(CP cp) { using pushed = typename S<Args2...>:: template push_back<call_pair<int64_t, NInt>>; call<CP, NInt+1, NUint, NDouble, NString, S<Args1...>, pushed>()(cp); } };
  58. call 호출 과정 - /v<uint>/<string>/<path> 0 0 [uint, str, str] [] 1 0 [str, str] [<uint, 0>] → 1 1 [str] [<uint, 0> <str, 0>] NUint NStr S1 S2
  59. call 호출 과정 - /v<uint>/<string>/<path> 0 0 [uint, str, str] [] 1 0 [str, str] [<uint, 0>] 1 1 [str] [<uint, 0> <str, 0>] → 1 2 [] [<uint, 0>, <str, 0>, <str, 1>] NUint NStr S1 S2
  60. struct call<CP, NInt, NUint, NDouble, NString, S<>, S<Args2...>> { void operator()(CP cp) { // 일부 생략 cp.res = cp.handler( cp.routing_params.template get <typename Args2::type>(Args2::pos) ... ); cparams.res.end(); return;
  61. T routing_params::get<T>(index) template<> string routing_params::get<string>(index) const { return string_params[index]; }
  62. 핸들러 호출하기 handler(rp.uint_params[0], rp.string_params[0], rp.string_params[1]); handler(rp.get<uint>(0), rp.get<string>(0), rp.get<string>(1)); handler( rp.get<Args2::type>(Args2::pos)... )
  63. 핸들러 호출하기 Args… = <uint, 0>, <str, 0>, <str, 1> handler( rp.get<Args::type>(Args::pos)... ) <uint,0>을 대입한 경우를 보면 = rp.get<uint>(0) ...을 통해서 반복되므로 rp.get<uint>(0), rp.get<str>(1), rp.get<str>(2)
  64. struct call<CP, NInt, NUint, NDouble, NString, S<>, S<Args2...>> { void operator()(CP cp) { // 일부 생략 cp.res = cp.handler( cp.routing_params.template get <typename Args2::type>(Args2::pos) ... ); cparams.res.end(); return;
  65. Variadic template from example void print(A a, B b, C c, …) 모든 인자를 출력해주는 함수 print() print(1) - 1 print(2, “hello”, 40) - 2hello40
  66. print 구현 void print() {} template <typename A, typename ... Args> void print(A a, Args... args) { cout << a; print(args...); }
  67. print(2, “hello”, 40) 실행 A = int Args… = const char*, int args… = “hello”, 40 cout << 2; print(“hello”, 40);
  68. print 실행 print<int, const char*, int>(2, “hello”, 40) - 2 print<const char*, int>(“hello”, 40) - hello print<int>(40) - 40 print() 2hello40
  69. Variadic template 대입의 예 args… = 1, 5, 10 (args+1)… = 1+1, 5+1, 10+1 func(args)… = func(1), func(5), func(10) func(args…) = func(1, 5, 10)
  70. Q&A for Routing 2 Trie Routing parameter Variadic template
  71. Middleware
  72. Middleware? C 요청 -> S 응답 -> C 받음 요청 --[MW]--> 응답 --[MW]--> 받음 before_handle(req, res) after_handle(req, res)
  73. Example Middleware Static page serving (cache control) Cookie parsing, Session support JSON parsing Profiling, Logging
  74. Middleware of express.js (Node.js) app.use(function (req, res, next) { req.cookies = parse_cookies(req); next(); }); req에 임의로 변수추가 가능
  75. Middleware 구현의 제약들 근데 우린 C++이잖아… 변수는 다 선언되어야 unordered_map<string, any> ??? + express의 문제점: Dependency 세션은 사용하는데 쿠키파서가 없는 경우
  76. 이 문제를 어떻게 해결하면 좋을까? 각 MW별로 저장해야할 값들이 존재 어떤 MW는 다른 MW의 값을 참조해야 한다 (C++이니까) 필요한 MW가 빠진 경우 컴파일 에러가 발생해 야함
  77. struct MW::context MW가 필요한 값을 저장하는 struct 내용에 특별한 제한이 없음 App<A, B, C> app; A, B, C 세 MW를 사용하는 app SimpleApp == App<> Crow에서의 해결 방식
  78. ctx_A : public A::context ctx_AB : public ctx_A, public B::context ctx_ABC : public ctx_AB, public C::context context : public ctx_ABC 실제로 ctx_AB === partial_context<A, B> context 체인 상속 - App<A, B, C>
  79. Crow에서의 해결 방식 template <typename AllContext> before_handle(req, res, context& ctx, AllContext& all_ctx) from MW - all_ctx.get<A>() from handler - app.get_context<A>(req)
  80. Crow에서의 해결 방식 - 전체 호출 과정 A::before_handle(req, res, ctx_A) B::before_handle(req, res, ctx_AB) C::before_handle(req, res, ctx_ABC) app.handle(req, res) C::after_handle(req, res, ctx_ABC) B::after_handle(req, res, ctx_AB) A::after_handle(req, res, ctx_A)
  81. Dependency 해결 (1) Session MW는 Cookie MW에 의존한다면 app<Cookie, Session> - OK app<Session> - error app<Session, Cookie> - error 의존: all_ctx.get<Cookie>를 사용한다.
  82. Dependency 해결 (2) App<A, B, C> app; A::before_handle(req, res, ctx_A) B::before_handle(req, res, ctx_AB) C::before_handle(req, res, ctx_ABC) app.handle(req, res) C::after_handle(req, res, ctx_ABC) B::after_handle(req, res, ctx_AB) A::after_handle(req, res, ctx_A)
  83. Dependency 해결 (3) ctx_A :: get<A> - OK ctx_A :: get<B> - error ctx_AB :: get<A> - OK ctx_AB :: get<B> - OK
  84. ctx_AB::get template <typename T> typename T::context& get() { return static_cast<typename T::context&>(*this); } 상속한 MW의 context 타입으로만 캐스팅 가능
  85. 다른 MW과 연관이 없는 MW라면? template <typename AllContext> before_handle(req, res, context& ctx, AllContext& all_ctx) 대부분의 경우 before_handle(req, res, context& ctx)
  86. SFINAE Substitution failure is not an error. 템플릿 인자를 대입하는 과정에서 발생하는 에 러는 에러로 간주하지 않는다. (모두 실패하는 경우에는 에러) 또는 이를 이용한 프로그래밍 기법을 지칭함
  87. SFINAE 예제 template <typename T> true_type has_iterator_checker(typename T::iterator *); template <typename T> false_type has_iterator_checker(...); decltype(has_iterator_checker<int>()) === false_type decltype(has_iterator_checker<string>()) === true_type
  88. after_call_helper template <typename MW, typename Context> void after_call_helper(MW& mw, …, decltype( declval<MW>().before_handle( declval<request&>(), declval<response&>(), declval<typename MW::context&>(), declval<Context&>() ))* dummy = 0)
  89. decltype, declval<T> decltype(X) X의 선언형 declval<T>() 컴파일 타임에만 사용할 수 있는 T의 객체형
  90. template <typename A, typename B> auto add(A a, B b) -> decltype(a+b) { return a+b; } template <typename A, typename B> decltype(declval<A>() + declval<B>()) add(A a, B b) C++14: return type deduction
  91. after_call_helper template <typename MW, typename Context> void after_call_helper(MW& mw, …, decltype( declval<MW>().before_handle( declval<request&>(), declval<response&>(), declval<typename MW::context&>(), declval<Context&>() ))* dummy = 0)
  92. after_call_helper template <typename MW, typename AllContext> void after_call_helper(MW& mw, …, decltype( mw.before_handle( declval<request&>(), declval<response&>(), declval<typename MW::context&>(), declval<AllContext&>() ))* dummy = 0)
  93. after_call_helper template <typename MW, typename Context> void after_call_helper(MW& mw, …, decltype( mw.after_handle(req, res, ctx, all_ctx) ) * dummy = 0)
  94. after_call_helper template <typename MW, typename Context> void after_call_helper(MW& mw, …, 다음 함수를 호출할 수 있는가? mw.before_handle(req, res, ctx, all_ctx) )
  95. after_call_helper template <typename MW, typename AllContext> void after_call_helper(MW& mw, …, decltype( declval<MW>().after_handle( declval<request&>(), declval<response&>(), declval<typename MW::context&>(), declval<AllContext&>() ))* dummy = 0)
  96. std::enable_if SFINAE를 이용하기 편하게 만들어둔 도구 조건부로 구현을 선택 template <typename T> enable_if<is_integral<T>::value> func(T x) … handler 형태를 고를 때 사용 (생략) http://en.cppreference.com/w/cpp/types/enable_if
  97. CookieParser - Example Middleware CookieParser::context uo_map<string, string> jar, cookies_to_add; get_cookie(key) set_cookie(key, value) before - 파싱 + jar에 저장 after - Set-Cookie 헤더 추가
  98. Q&A for Middleware App<A, B, C> app; context inheritance chain Middleware call chain CookieParser SFINAE
  99. Crow 서버 구조
  100. 고려해야 할 점 멀티 쓰레드 작업 배분 Connection 객체의 생성, 소멸 관리 HTTP 타임 아웃 (Keep-Alive)
  101. 초기 버전 boost::asio + joyent/http-parser asio::io_service 하나 concurrency 개수 만큼 io_service.run() shared_ptr<Connection> 으로 수명 관리 asio::deadline_timer로 타임아웃 처리
  102. 초당 처리 횟수가 100,000 이상.. shared_ptr의 atomic 연산이 성능 부하 deadline_timer 내부 자료구조 업데이트 부하
  103. 여러 개의 싱글 쓰레드 서버의 결합 N 개의 작업용 쓰레드 (asio::io_service ✕ N) 요청이 들어올 때마다 어느 쓰레드가 처리할지 RR로 분배 해당 요청에 대한 모든 처리는 담당한 쓰레드 안에서만 이루어짐
  104. deadline_timer 성능 저하 문제 각 쓰레드 별로 thread_local한 타이머를 구현 관련 코드: dumb_timer_queue.h
  105. shared_ptr 제거 Connection 객체를 하나의 쓰레드가 담당 atomic 연산 불필요 변경 후 성능 4배 향상 쓰레드간 통신 전혀 없음 * shared_ptr을 쓰지말란 얘기는 아닙니다.
  106. 그 외 사용된 C++11 기능들 소개
  107. std::chrono std::chrono:: system_clock steady_clock high_resolution_clock (거의) 같은 인터페이스 제공
  108. std::chrono 사용법 auto t1 = clock::now(); do_something(); auto t2 = clock::now(); if (t2 - t1 > seconds(10)) … duration_cast<seconds>(t2 - t1).count()
  109. system_clock to_time_t, from_time_t 제공 steady_clock 무조건 증가함이 보장됨 high_resolution_clock 제일 높은 해상도를 제공 (가능한 짧은 시간 간격)
  110. operator “” C++11 std::chrono::seconds(10) std::chrono::minutes(20) std::string(“hello”) C++14 10s 20min “hello”s
  111. POST request CROW_ROUTE(app, “/update_score”) .methods(“POST”_method) ([](const crow::request& req){ auto json = crow::json::load(req.body); ... });
  112. operator “” _method constexpr crow::HTTPMethod operator "" _method(const char* str, size_t len) { return crow::black_magic::is_equ_p(str, "GET", 3) ? crow::HTTPMethod::GET : … };
  113. enum class enum class HTTPMethod { GET, POST, ... }; int m = HTTPMethod::GET; // error!
  114. 사용된 boost 모듈들 asio lexical_cast string iequals operators
  115. <끝>(내용 발표는)
  116. 미리 나온 질문들
  117. 현업에서 어디까지 최신 기술을 쓰는게 좋을까요?
  118. 현업에서 어디까지 최신 기술...? C++11 is not bleeding edge technology. (개인적인 의견) 2011년? 트랜스포머3, 테라, 스카이림, 포탈2 node.js 0.3.3/0.6.6 → 0.12.0, RoR 3.1 → 4.2, iPhone4 / iOS 5.0, 갤럭시 S2
  119. 현업에서 어디까지 최신 기술...? 팀에서 TDD를 사용하기 위해 고려해야할 점? http://agile.egloos.com/5827377 사회적 자본이 더 중요한 경우가 많다.
  120. C++14 - minor patch to 11 vector v(1000000) auto lambda = [v = std::move(v), a = 3](auto x, auto y) {return a + x + y;};
  121. C++14 - minor patch to 11 그외 … 01010b, 1’234’5678’9 == 123456789 make_unique ...
  122. C++14 - minor patch는 훼이크 auto p = [](auto& s, auto...x){ [](...){}((s<<x,1)...); }; p(cout, 42, "hello?"); 위 코드의 실행 결과를 설명하시오. (3점)
  123. rvalue 정확한 얘기를 하려면 lvalue, glvalue, xvalue, rvalue, prvalue를... http://www.open- std.org/jtc1/sc22/wg21/docs/papers/2010/n305 5.pdf 정확히
  124. rvalue vector<string> lines; while(cin) { string line; getline(cin, line) lines.push_back(line); }
  125. rvalue vector<string> lines; while(cin) { string line; getline(cin, line) lines.push_back(std::move(line)); }
  126. string s0("숭"); string s1("그"); string s2("리"); string s3("당"); string s4("당 숭당당"); string dest = s0 + " " + s1 + " " + s2 + " " + s3 + " " + s4;
  127. rvalue Strong exception guarantee vector<T> v1, v2 v1 = std::move(v2); move_if_noexcept
Advertisement