process.hpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. #ifndef ENTT_PROCESS_PROCESS_HPP
  2. #define ENTT_PROCESS_PROCESS_HPP
  3. #include <type_traits>
  4. #include <functional>
  5. #include <utility>
  6. #include "../config/config.h"
  7. namespace entt {
  8. /**
  9. * @brief Base class for processes.
  10. *
  11. * This class stays true to the CRTP idiom. Derived classes must specify what's
  12. * the intended type for elapsed times.<br/>
  13. * A process should expose publicly the following member functions whether
  14. * required:
  15. *
  16. * * @code{.cpp}
  17. * void update(Delta, void *);
  18. * @endcode
  19. *
  20. * It's invoked once per tick until a process is explicitly aborted or it
  21. * terminates either with or without errors. Even though it's not mandatory to
  22. * declare this member function, as a rule of thumb each process should at
  23. * least define it to work properly. The `void *` parameter is an opaque
  24. * pointer to user data (if any) forwarded directly to the process during an
  25. * update.
  26. *
  27. * * @code{.cpp}
  28. * void init(void *);
  29. * @endcode
  30. *
  31. * It's invoked at the first tick, immediately before an update. The `void *`
  32. * parameter is an opaque pointer to user data (if any) forwarded directly to
  33. * the process during an update.
  34. *
  35. * * @code{.cpp}
  36. * void succeeded();
  37. * @endcode
  38. *
  39. * It's invoked in case of success, immediately after an update and during the
  40. * same tick.
  41. *
  42. * * @code{.cpp}
  43. * void failed();
  44. * @endcode
  45. *
  46. * It's invoked in case of errors, immediately after an update and during the
  47. * same tick.
  48. *
  49. * * @code{.cpp}
  50. * void aborted();
  51. * @endcode
  52. *
  53. * It's invoked only if a process is explicitly aborted. There is no guarantee
  54. * that it executes in the same tick, this depends solely on whether the
  55. * process is aborted immediately or not.
  56. *
  57. * Derived classes can change the internal state of a process by invoking the
  58. * `succeed` and `fail` protected member functions and even pause or unpause the
  59. * process itself.
  60. *
  61. * @sa Scheduler
  62. *
  63. * @tparam Derived Actual type of process that extends the class template.
  64. * @tparam Delta Type to use to provide elapsed time.
  65. */
  66. template<typename Derived, typename Delta>
  67. class Process {
  68. enum class State: unsigned int {
  69. UNINITIALIZED = 0,
  70. RUNNING,
  71. PAUSED,
  72. SUCCEEDED,
  73. FAILED,
  74. ABORTED,
  75. FINISHED
  76. };
  77. template<State state>
  78. using tag = std::integral_constant<State, state>;
  79. template<typename Target = Derived>
  80. auto tick(int, tag<State::UNINITIALIZED>, void *data)
  81. -> decltype(std::declval<Target>().init(data)) {
  82. static_cast<Target *>(this)->init(data);
  83. }
  84. template<typename Target = Derived>
  85. auto tick(int, tag<State::RUNNING>, Delta delta, void *data)
  86. -> decltype(std::declval<Target>().update(delta, data)) {
  87. static_cast<Target *>(this)->update(delta, data);
  88. }
  89. template<typename Target = Derived>
  90. auto tick(int, tag<State::SUCCEEDED>)
  91. -> decltype(std::declval<Target>().succeeded()) {
  92. static_cast<Target *>(this)->succeeded();
  93. }
  94. template<typename Target = Derived>
  95. auto tick(int, tag<State::FAILED>)
  96. -> decltype(std::declval<Target>().failed()) {
  97. static_cast<Target *>(this)->failed();
  98. }
  99. template<typename Target = Derived>
  100. auto tick(int, tag<State::ABORTED>)
  101. -> decltype(std::declval<Target>().aborted()) {
  102. static_cast<Target *>(this)->aborted();
  103. }
  104. template<State S, typename... Args>
  105. void tick(char, tag<S>, Args &&...) const ENTT_NOEXCEPT {}
  106. protected:
  107. /**
  108. * @brief Terminates a process with success if it's still alive.
  109. *
  110. * The function is idempotent and it does nothing if the process isn't
  111. * alive.
  112. */
  113. void succeed() ENTT_NOEXCEPT {
  114. if(alive()) {
  115. current = State::SUCCEEDED;
  116. }
  117. }
  118. /**
  119. * @brief Terminates a process with errors if it's still alive.
  120. *
  121. * The function is idempotent and it does nothing if the process isn't
  122. * alive.
  123. */
  124. void fail() ENTT_NOEXCEPT {
  125. if(alive()) {
  126. current = State::FAILED;
  127. }
  128. }
  129. /**
  130. * @brief Stops a process if it's in a running state.
  131. *
  132. * The function is idempotent and it does nothing if the process isn't
  133. * running.
  134. */
  135. void pause() ENTT_NOEXCEPT {
  136. if(current == State::RUNNING) {
  137. current = State::PAUSED;
  138. }
  139. }
  140. /**
  141. * @brief Restarts a process if it's paused.
  142. *
  143. * The function is idempotent and it does nothing if the process isn't
  144. * paused.
  145. */
  146. void unpause() ENTT_NOEXCEPT {
  147. if(current == State::PAUSED) {
  148. current = State::RUNNING;
  149. }
  150. }
  151. public:
  152. /*! @brief Type used to provide elapsed time. */
  153. using delta_type = Delta;
  154. /*! @brief Default destructor. */
  155. virtual ~Process() ENTT_NOEXCEPT {
  156. static_assert(std::is_base_of<Process, Derived>::value, "!");
  157. }
  158. /**
  159. * @brief Aborts a process if it's still alive.
  160. *
  161. * The function is idempotent and it does nothing if the process isn't
  162. * alive.
  163. *
  164. * @param immediately Requests an immediate operation.
  165. */
  166. void abort(const bool immediately = false) ENTT_NOEXCEPT {
  167. if(alive()) {
  168. current = State::ABORTED;
  169. if(immediately) {
  170. tick(0);
  171. }
  172. }
  173. }
  174. /**
  175. * @brief Returns true if a process is either running or paused.
  176. * @return True if the process is still alive, false otherwise.
  177. */
  178. bool alive() const ENTT_NOEXCEPT {
  179. return current == State::RUNNING || current == State::PAUSED;
  180. }
  181. /**
  182. * @brief Returns true if a process is already terminated.
  183. * @return True if the process is terminated, false otherwise.
  184. */
  185. bool dead() const ENTT_NOEXCEPT {
  186. return current == State::FINISHED;
  187. }
  188. /**
  189. * @brief Returns true if a process is currently paused.
  190. * @return True if the process is paused, false otherwise.
  191. */
  192. bool paused() const ENTT_NOEXCEPT {
  193. return current == State::PAUSED;
  194. }
  195. /**
  196. * @brief Returns true if a process terminated with errors.
  197. * @return True if the process terminated with errors, false otherwise.
  198. */
  199. bool rejected() const ENTT_NOEXCEPT {
  200. return stopped;
  201. }
  202. /**
  203. * @brief Updates a process and its internal state if required.
  204. * @param delta Elapsed time.
  205. * @param data Optional data.
  206. */
  207. void tick(const Delta delta, void *data = nullptr) {
  208. switch (current) {
  209. case State::UNINITIALIZED:
  210. tick(0, tag<State::UNINITIALIZED>{}, data);
  211. current = State::RUNNING;
  212. // no break on purpose, tasks are executed immediately
  213. case State::RUNNING:
  214. tick(0, tag<State::RUNNING>{}, delta, data);
  215. default:
  216. // suppress warnings
  217. break;
  218. }
  219. // if it's dead, it must be notified and removed immediately
  220. switch(current) {
  221. case State::SUCCEEDED:
  222. tick(0, tag<State::SUCCEEDED>{});
  223. current = State::FINISHED;
  224. break;
  225. case State::FAILED:
  226. tick(0, tag<State::FAILED>{});
  227. current = State::FINISHED;
  228. stopped = true;
  229. break;
  230. case State::ABORTED:
  231. tick(0, tag<State::ABORTED>{});
  232. current = State::FINISHED;
  233. stopped = true;
  234. break;
  235. default:
  236. // suppress warnings
  237. break;
  238. }
  239. }
  240. private:
  241. State current{State::UNINITIALIZED};
  242. bool stopped{false};
  243. };
  244. /**
  245. * @brief Adaptor for lambdas and functors to turn them into processes.
  246. *
  247. * Lambdas and functors can't be used directly with a scheduler for they are not
  248. * properly defined processes with managed life cycles.<br/>
  249. * This class helps in filling the gap and turning lambdas and functors into
  250. * full featured processes usable by a scheduler.
  251. *
  252. * The signature of the function call operator should be equivalent to the
  253. * following:
  254. *
  255. * @code{.cpp}
  256. * void(Delta delta, void *data, auto succeed, auto fail);
  257. * @endcode
  258. *
  259. * Where:
  260. *
  261. * * `delta` is the elapsed time.
  262. * * `data` is an opaque pointer to user data if any, `nullptr` otherwise.
  263. * * `succeed` is a function to call when a process terminates with success.
  264. * * `fail` is a function to call when a process terminates with errors.
  265. *
  266. * The signature of the function call operator of both `succeed` and `fail`
  267. * is equivalent to the following:
  268. *
  269. * @code{.cpp}
  270. * void();
  271. * @endcode
  272. *
  273. * Usually users shouldn't worry about creating adaptors. A scheduler will
  274. * create them internally each and avery time a lambda or a functor is used as
  275. * a process.
  276. *
  277. * @sa Process
  278. * @sa Scheduler
  279. *
  280. * @tparam Func Actual type of process.
  281. * @tparam Delta Type to use to provide elapsed time.
  282. */
  283. template<typename Func, typename Delta>
  284. struct ProcessAdaptor: Process<ProcessAdaptor<Func, Delta>, Delta>, private Func {
  285. /**
  286. * @brief Constructs a process adaptor from a lambda or a functor.
  287. * @tparam Args Types of arguments to use to initialize the actual process.
  288. * @param args Parameters to use to initialize the actual process.
  289. */
  290. template<typename... Args>
  291. ProcessAdaptor(Args &&... args)
  292. : Func{std::forward<Args>(args)...}
  293. {}
  294. /**
  295. * @brief Updates a process and its internal state if required.
  296. * @param delta Elapsed time.
  297. * @param data Optional data.
  298. */
  299. void update(const Delta delta, void *data) {
  300. Func::operator()(delta, data, [this]() { this->succeed(); }, [this]() { this->fail(); });
  301. }
  302. };
  303. }
  304. #endif // ENTT_PROCESS_PROCESS_HPP