Story
故事起源來自於看到類似這樣的程式碼
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | #define VL_RESTORER(var) \const VRestorer<typename std::decay<decltype(var)>::type> restorer_##var(var);
 
 template <typename T> class VRestorer {
 T& m_ref;
 const T m_saved;
 public:
 explicit VRestorer(T& permr)
 : m_ref{permr}
 , m_saved{permr} {}
 ~VRestorer() { m_ref = m_saved; }
 };
 
 | 
利用RAII來保存上下文當前的值,執行到結束的時候恢復原狀
不過
| 12
 3
 4
 5
 
 | int a = 1, b = 2;VL_RESTORER(a);
 VL_RESTORER(b);
 a = 3;
 b = 4;
 
 | 
用起來沒什麼問題,不過總要找個題目來練習
ScopeExit
基本上就是RAII的變形,在Destructor的部分執行我們需要的Function,隨便在github搜尋就一堆了,這邊有個最簡單的方案
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | template <typename F>struct scope_exit
 {
 F f;
 ~scope_exit() { f(); }
 };
 
 template <typename F>
 inline scope_exit<F> make_scope_exit(F&& f)
 {
 return scope_exit<F>{f};
 }
 
 | 
如果使用上C++17的CTAD,底下的make_scope_exit也不一定得存在
所以問題就變成了這樣,我希望在結束的時候,將所存的變數恢復原狀
問題就變成了該怎麼做
Higher Order Function
雖然C++不是標準的Functional Programming Language,不過要做點手腳還是辦得到的
問題變成了,傳入需要保存狀態的變數,回傳是一個函數,執行這個函數就能恢復原狀,這裡用上了Variadic Template和Tuple
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | template <typename ...Ts>inline auto restore(Ts&& ...ts)
 {
 return [restore_ref = std::tuple<std::add_lvalue_reference_t<std::decay_t<Ts>>...>(std::forward<Ts>(ts)...),
 store = std::tuple<std::add_const_t<std::decay_t<Ts>>...>(ts...)]() mutable noexcept
 {
 restore_ref = store;
 };
 }
 
 | 
這邊有兩個tuple,其中restore_ref保存了所有變數的reference,store則是變數這個時間點的值
Combo
上面的方式能夠寫成
| 12
 3
 4
 
 | int a = 1, b = 2;auto _ = make_scope_exit(restore(a, b));
 a = 3;
 b = 4;
 
 | 
好壞就見仁見智了