scoped_state.h 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. #pragma once
  2. #include <functional>
  3. #include <type_traits>
  4. #include <jvalidate/_macro.h>
  5. /**
  6. * @breif Create an anonymous scoped state object, which represents a temporary
  7. * change of value. Since we only need to give ScopedState a name to ensure that
  8. * its lifetime isn't for only a single line, this macro allows us to be more
  9. * appropriately terse.
  10. *
  11. * @code
  12. * {
  13. * scoped_state(property_, value...);
  14. * // do some things...
  15. * }
  16. * @endcode
  17. *
  18. * but this one provides exit guards in the same way that @see OnBlockExit does.
  19. *
  20. * @param prop A reference to a property that should be altered in the current
  21. * function-scope. Is immediately modified to {@see value}, and will be returned
  22. * to its original value when the current scope exits.
  23. *
  24. * @param value The new value to be set into prop.
  25. */
  26. #define scoped_state(prop, value) \
  27. auto JVALIDATE_CONCAT(scoped_state_, __LINE__) = detail::ScopedState(prop, value)
  28. namespace jvalidate::detail {
  29. /**
  30. * @brief An object that alters a given value to a provided temporary, and then
  31. * restores it to the original value upon being destructed. Because of this
  32. * characteristic, the following two pieces of code are equivalent:
  33. *
  34. * @code
  35. * T tmp = value...;
  36. * std::swap(property_, tmp);
  37. * // do some things...
  38. * std::swap(property_, tmp);
  39. * @endcode
  40. *
  41. * @code
  42. * {
  43. * ScopedState tmp(property_, value...);
  44. * // do some things...
  45. * }
  46. * @endcode
  47. */
  48. class ScopedState {
  49. private:
  50. std::function<void()> reset_;
  51. public:
  52. /**
  53. * @brief Initialize a scoped change-in-value to a property, properly guarded
  54. * against early-returns, exceptions, and forgetting to reset the property.
  55. *
  56. * @tparam T The type of the value being updated
  57. * @tparam S Any type that is compatible with T
  58. *
  59. * @param prop A reference to a property that should be altered in the current
  60. * function-scope. Is immediately modified to {@see value}, and will be returned
  61. * to its original value when the current scope exits.
  62. *
  63. * @param value The new value to be set into prop.
  64. */
  65. template <typename T, typename S>
  66. requires(std::is_constructible_v<T, S>)
  67. ScopedState(T & prop, S value) : reset_([reset = prop, &prop]() { prop = reset; }) {
  68. prop = std::move(value);
  69. }
  70. ~ScopedState() { reset_(); }
  71. /**
  72. * @brief By providing an explicit operator bool, it is possible to use
  73. * ScopedState in an if statement, allowing you to write something like:
  74. *
  75. * @code
  76. * if (scoped_state(property_, value...)) {
  77. * // do some things...
  78. * }
  79. * @endcode
  80. */
  81. explicit operator bool() const { return true; }
  82. };
  83. }