#pragma once #include #include #define JVALIDATE_CONCAT2(A, B) A##B #define JVALIDATE_CONCAT(A, B) JVALIDATE_CONCAT2(A, B) /** * @breif Create an anonymous scoped state object, which represents a temporary * change of value. Since we only need to give ScopedState a name to ensure that * its lifetime isn't for only a single line, this macro allows us to be more * appropriately terse. * * @code * { * scoped_state(property_, value...); * // do some things... * } * @endcode * * but this one provides exit guards in the same way that @see OnBlockExit does. * * @param prop A reference to a property that should be altered in the current * function-scope. Is immediately modified to {@see value}, and will be returned * to its original value when the current scope exits. * * @param value The new value to be set into prop. */ #define scoped_state(prop, value) \ auto JVALIDATE_CONCAT(scoped_state_, __LINE__) = detail::ScopedState(prop, value) namespace jvalidate::detail { /** * @brief An object that alters a given value to a provided temporary, and then * restores it to the original value upon being destructed. Because of this * characteristic, the following two pieces of code are equivalent: * * @code * T tmp = value...; * std::swap(property_, tmp); * // do some things... * std::swap(property_, tmp); * @endcode * * @code * { * ScopedState tmp(property_, value...); * // do some things... * } * @endcode */ class ScopedState { private: std::function reset_; public: /** * @brief Initialize a scoped change-in-value to a property, properly guarded * against early-returns, exceptions, and forgetting to reset the property. * * @tparam T The type of the value being updated * @tparam S Any type that is compatible with T * * @param prop A reference to a property that should be altered in the current * function-scope. Is immediately modified to {@see value}, and will be returned * to its original value when the current scope exits. * * @param value The new value to be set into prop. */ template requires(std::is_constructible_v) ScopedState(T & prop, S value) : reset_([reset = prop, &prop]() { prop = reset; }) { prop = std::move(value); } ~ScopedState() { reset_(); } /** * @brief By providing an explicit operator bool, it is possible to use * ScopedState in an if statement, allowing you to write something like: * * @code * if (scoped_state(property_, value...)) { * // do some things... * } * @endcode */ explicit operator bool() const { return true; } }; }