scoped_state.h 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. #pragma once
  2. #include <functional>
  3. #include <type_traits>
  4. #define JVALIDATE_CONCAT2(A, B) A##B
  5. #define JVALIDATE_CONCAT(A, B) JVALIDATE_CONCAT2(A, B)
  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() { reset_(); }
  72. /**
  73. * @brief By providing an explicit operator bool, it is possible to use
  74. * ScopedState in an if statement, allowing you to write something like:
  75. *
  76. * @code
  77. * if (scoped_state(property_, value...)) {
  78. * // do some things...
  79. * }
  80. * @endcode
  81. */
  82. explicit operator bool() const { return true; }
  83. };
  84. }