scoped_state.h 3.1 KB

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