fork download
  1.  
  2. // This is example from
  3. // http://v...content-available-to-author-only...y.info/c11-functional-decomposition-easy-way-to-do-aop/
  4. // by Victor Laskin
  5.  
  6. #include <iostream>
  7. #include <functional>
  8. #include <map>
  9. #include <vector>
  10. #include <memory>
  11. #include <chrono>
  12. #include <mutex>
  13. using namespace std;
  14.  
  15.  
  16. #define LOG std::cout
  17. #define NL std::endl
  18.  
  19. /// Simple immutable data
  20. class UserData {
  21. public:
  22. const int id;
  23. const string name;
  24. const int parent;
  25. UserData(int id, string name, int parent) : id(id), name(name), parent(parent) {}
  26. };
  27.  
  28. /// Shared pointer to immutable data
  29. using User = std::shared_ptr<UserData>;
  30.  
  31. /// Error type - code + description
  32. class Error {
  33. public:
  34. Error(int code, string message) : code(code), message(message) {}
  35. Error(const Error& e) : code(e.code), message(e.message) {}
  36. const int code;
  37. const string message;
  38. };
  39.  
  40.  
  41. /// MayBe monad from Haskel over shared_ptr
  42. /// with additional error field
  43. template < typename T >
  44. class Maybe {
  45. private:
  46. const T data;
  47. const shared_ptr<Error> error;
  48. public:
  49. Maybe(T data) : data(std::forward<T>(data)), error(nullptr) {}
  50. Maybe() : data(nullptr), error(nullptr) {}
  51. Maybe(decltype(nullptr) nothing) : data(nullptr), error(nullptr) {}
  52. Maybe(Error&& error) : data(nullptr), error(make_shared<Error>(error)) {}
  53.  
  54. bool isEmpty() { return (data == nullptr); };
  55. bool hasError() { return (error != nullptr); };
  56. T operator()(){ return data; };
  57. shared_ptr<Error> getError(){ return error; };
  58.  
  59. };
  60.  
  61. template <class T>
  62. Maybe<T> just(T t)
  63. {
  64. return Maybe<T>(t);
  65. }
  66.  
  67. // Helpers to convert lambda into std::function
  68.  
  69. template <typename Function>
  70. struct function_traits
  71. : public function_traits<decltype(&Function::operator())>
  72. {};
  73.  
  74. template <typename ClassType, typename ReturnType, typename... Args>
  75. struct function_traits<ReturnType(ClassType::*)(Args...) const>
  76. {
  77. typedef ReturnType (*pointer)(Args...);
  78. typedef std::function<ReturnType(Args...)> function;
  79. };
  80.  
  81. template <typename Function>
  82. typename function_traits<Function>::function
  83. to_function (Function& lambda)
  84. {
  85. return static_cast<typename function_traits<Function>::function>(lambda);
  86. }
  87.  
  88.  
  89. // Aspect logging duration of execution
  90. template <typename R, typename ...Args>
  91. std::function<R(Args...)> logged(string name, std::function<R(Args...)> f)
  92. {
  93. return [f,name](Args... args){
  94.  
  95. LOG << name << " start" << NL;
  96. auto start = std::chrono::high_resolution_clock::now();
  97.  
  98. R result = f(std::forward<Args>(args)...);
  99.  
  100. auto end = std::chrono::high_resolution_clock::now();
  101. auto total = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
  102. LOG << "Elapsed: " << total << "us" << NL;
  103.  
  104. return result;
  105. };
  106. }
  107.  
  108. // Security checking
  109. template <typename R, typename ...Args, typename S>
  110. std::function<Maybe<R>(Args...)> secured(S session, std::function<Maybe<R>(Args...)> f)
  111. {
  112. // if user is not valid - return nothing
  113. return [f, &session](Args... args) -> Maybe<R> {
  114. if (session.isValid())
  115. return f(std::forward<Args>(args)...);
  116. else
  117. return Error(403, "Forbidden");
  118. };
  119. }
  120.  
  121.  
  122. // Use local cache (memoize)
  123. template <typename R, typename C, typename ...Args>
  124. std::function<Maybe<R>(Args...)> cached(C & cache, std::function<Maybe<R>(Args...)> f)
  125. {
  126. return [f,&cache](Args... args){
  127.  
  128. // get key as tuple of arguments
  129. auto key = make_tuple(args...);
  130.  
  131. if (cache.count(key) > 0)
  132. return just(cache[key]);
  133. else
  134. {
  135. Maybe<R> result = f(std::forward<Args>(args)...);
  136. if (!result.hasError())
  137. cache.insert(std::pair<decltype(key), R>(key, result())); //add to cache
  138. return result;
  139. }
  140. };
  141. }
  142.  
  143. // If there was error - try again
  144. template <typename R, typename ...Args>
  145. std::function<Maybe<R>(Args...)> triesTwice(std::function<Maybe<R>(Args...)> f)
  146. {
  147. return [f](Args... args){
  148. Maybe<R> result = f(std::forward<Args>(args)...);
  149. if (result.hasError())
  150. return f(std::forward<Args>(args)...);
  151. return result;
  152. };
  153. }
  154.  
  155. // Treat empty state as error
  156. template <typename R, typename ...Args>
  157. std::function<Maybe<R>(Args...)> notEmpty(std::function<Maybe<R>(Args...)> f)
  158. {
  159. return [f](Args... args) -> Maybe<R> {
  160. Maybe<R> result = f(std::forward<Args>(args)...);
  161. if ((!result.hasError()) && (result.isEmpty()))
  162. return Error(404, "Not Found");
  163. return result;
  164. };
  165. }
  166.  
  167. template <typename R, typename ...Args>
  168. std::function<R(Args...)> locked(std::mutex& m, std::function<R(Args...)> f)
  169. {
  170. return [f,&m](Args... args){
  171. std::unique_lock<std::mutex> lock(m);
  172. return f(std::forward<Args>(args)...);
  173. };
  174. }
  175.  
  176. // Couple of additional helpers
  177.  
  178. template <class F, class... Args>
  179. void for_each_argument(F f, Args&&... args) {
  180. (void)(int[]){(f(forward<Args>(args)), 0)...};
  181. }
  182.  
  183. template <class T, class... P>
  184. inline auto make(P&&... args) -> T {
  185. return std::make_shared<typename T::element_type>(std::forward<P>(args)...);
  186. }
  187.  
  188.  
  189. int main() {
  190.  
  191. // Database...
  192. vector<User> users {make<User>(1, "John", 0), make<User>(2, "Bob", 1), make<User>(3, "Max", 1)};
  193.  
  194. // Request method
  195. auto findUser = [&users](int id) -> Maybe<User> {
  196. for (User user : users) {
  197. if (user->id == id)
  198. return user;
  199. }
  200. return nullptr;
  201. };
  202.  
  203. // Local cache
  204. map<tuple<int>,User> userCache;
  205.  
  206. // Security
  207. class Session {
  208. public:
  209. bool isValid() { return true; }
  210. } session;
  211.  
  212. // Mutex to test locked aspect
  213. std::mutex lockMutex;
  214.  
  215. // Main functional factorization
  216. auto findUserFinal = locked(lockMutex, secured(session, notEmpty( cached(userCache, triesTwice( logged("findUser", to_function(findUser)))))));
  217.  
  218. // TEST:
  219.  
  220. auto testUser = [&](int id) {
  221. auto user = findUserFinal(id);
  222. LOG << (user.hasError() ? "ERROR: " + user.getError()->message : "NAME:" + user()->name) << NL;
  223. };
  224.  
  225. for_each_argument(testUser, 2, 30, 2, 1);
  226.  
  227. return 0;
  228. }
Success #stdin #stdout 0s 3248KB
stdin
Standard input is empty
stdout
findUser start
Elapsed: 0us
NAME:Bob
findUser start
Elapsed: 0us
ERROR: Not Found
NAME:Bob
findUser start
Elapsed: 0us
NAME:John