Add ScopeGuard utility
authorZbigniew Kostrzewa <z.kostrzewa@samsung.com>
Thu, 18 Jul 2013 11:36:45 +0000 (13:36 +0200)
committerZbigniew Kostrzewa <z.kostrzewa@samsung.com>
Fri, 19 Jul 2013 05:42:39 +0000 (07:42 +0200)
[Issue#] N/A
[Bug] N/A
[Cause] N/A
[Solution] ScopeGuard is utility class that allows to register a piece
of code to be executed when control exits a scope in which ScopeGuard
object has been created.
Implementation based on:
* Scope guard pointer from Loki library
  http://loki-lib.cvs.sourceforge.net/loki-lib/loki/include/loki/ScopeGuard.h?view=markup
* Scope guard implementation in folly library
  https://github.com/facebook/folly/blob/master/folly/ScopeGuard.h
* Lecture "Systematic Error Handling in C++" conducted by Andrei Alexandrescu
  http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C
[Verification]
1. Build repository
2. Run command
  `wrt-commons-tests-core --output=text --regexp='ScopeGuard*'`

[Notes]
It will be utilized e.g. in wrt-installer for guarding detaching from
WRT DB.

Change-Id: I13cdd15b9c9c83a927faeee658d88340052ef66d

modules/core/config.cmake
modules/core/include/dpl/preprocessor.h
modules/core/include/dpl/scope_guard.h [new file with mode: 0644]
tests/core/CMakeLists.txt
tests/core/test_scope_guard.cpp [new file with mode: 0644]

index b929c35..35bbbf3 100644 (file)
@@ -110,6 +110,7 @@ SET(DPL_CORE_HEADERS
     ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/preprocessor.h
     ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/read_write_mutex.h
     ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/recursive_mutex.h
+    ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/scope_guard.h
     ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/scoped_resource.h
     ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/scoped_array.h
     ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/scoped_close.h
index 0118d76..6fca34c 100644 (file)
 #define DPL_MACRO_CONCAT_IMPL(x, y) x##y
 #define DPL_MACRO_CONCAT(x, y) DPL_MACRO_CONCAT_IMPL(x, y)
 
+#ifdef __COUNTER__
+#define DPL_ANONYMOUS_VARIABLE(name) DPL_MACRO_CONCAT(name, __COUNTER__)
+#else
+#define DPL_ANONYMOUS_VARIABLE(name) DPL_MACRO_CONCAT(name, __LINE__)
+#endif
+
 #endif //DPL_PREPROCESSOR_H
diff --git a/modules/core/include/dpl/scope_guard.h b/modules/core/include/dpl/scope_guard.h
new file mode 100644 (file)
index 0000000..2471937
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2013 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*!
+ * @file        scope_guard.h
+ * @author      Zbigniew Kostrzewa (z.kostrzewa@samsung.com)
+ * @version     1.0
+ * @brief       This file is the implementation file of scope guard RAII
+ */
+#ifndef DPL_SCOPE_GUARD_H_
+#define DPL_SCOPE_GUARD_H_
+
+#include <cstddef>
+#include <utility>
+#include <new>
+#include <type_traits>
+#include <dpl/preprocessor.h>
+
+namespace DPL {
+template<typename FunctionType>
+class ScopeGuard
+{
+  public:
+    explicit ScopeGuard(const FunctionType& function)
+        : m_function{function},
+          m_released{false}
+    {}
+
+    explicit ScopeGuard(FunctionType&& function)
+        : m_function{std::move(function)},
+          m_released{false}
+    {}
+
+    ScopeGuard(ScopeGuard&& other)
+        : m_function{std::move(other.m_function)},
+          m_released{other.m_released}
+    {
+        other.Release();
+    }
+
+    ScopeGuard(const ScopeGuard&) = delete;
+
+    ~ScopeGuard()
+    {
+        if (!m_released)
+        {
+            Execute();
+        }
+    }
+
+    ScopeGuard& operator=(const ScopeGuard&) = delete;
+
+    void Release()
+    {
+        m_released = true;
+    }
+
+    void* operator new(size_t) = delete;
+
+  private:
+    // FIXME change to noexcept when available
+    void Execute() throw()
+    {
+        m_function();
+    }
+
+    FunctionType m_function;
+    bool m_released;
+};
+
+template<typename FunctionType>
+ScopeGuard<typename std::decay<FunctionType>::type>
+MakeScopeGuard(FunctionType&& function)
+{
+  return ScopeGuard<typename std::decay<FunctionType>::type>(
+          std::forward<FunctionType>(function));
+}
+
+namespace detail {
+enum class ScopeGuardOnExit {};
+
+template <typename FunctionType>
+ScopeGuard<typename std::decay<FunctionType>::type>
+operator+(detail::ScopeGuardOnExit, FunctionType&& function)
+{
+  return ScopeGuard<typename std::decay<FunctionType>::type>(
+          std::forward<FunctionType>(function));
+}
+}
+}
+
+// FIXME provide support for compilers not supporting variadic macros
+#define DPL_SCOPE_EXIT(...) \
+    auto DPL_ANONYMOUS_VARIABLE(DPL_SCOPE_EXIT_STATE) \
+    = ::DPL::detail::ScopeGuardOnExit() + [__VA_ARGS__]()
+
+#endif // DPL_SCOPE_GUARD_H_
index 4348519..18665eb 100644 (file)
@@ -51,6 +51,7 @@ SET(DPL_TESTS_CORE_SOURCES
     ${TESTS_DIR}/core/test_thread.cpp
     ${TESTS_DIR}/core/test_type_list.cpp
     ${TESTS_DIR}/core/test_zip_input.cpp
+    ${TESTS_DIR}/core/test_scope_guard.cpp
 )
 
 WRT_TEST_ADD_INTERNAL_DEPENDENCIES(${TARGET_NAME} ${TARGET_DPL_EFL})
diff --git a/tests/core/test_scope_guard.cpp b/tests/core/test_scope_guard.cpp
new file mode 100644 (file)
index 0000000..7c09645
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/*
+ * @file        test_scope_guard.cpp
+ * @author      Zbigniew Kostrzewa (z.kostrzewa@samsung.com)
+ * @version     1.0
+ * @brief       This file is the implementation file of test scope guard
+ */
+#include <dpl/test/test_runner.h>
+#include <dpl/scope_guard.h>
+
+namespace {
+bool g_guardCalled = false;
+
+void regularFunction()
+{
+    g_guardCalled = true;
+}
+
+struct Functor
+{
+    explicit Functor(bool& guardCalled)
+        : m_guardCalled(guardCalled)
+    {}
+
+    void operator()()
+    {
+        m_guardCalled = true;
+    }
+
+    bool& m_guardCalled;
+};
+}
+
+RUNNER_TEST_GROUP_INIT(DPL)
+
+/*
+Name: ScopeGuard_MakeScopeGuard_RegularFunction
+Description: tests scope guard, created explicitly, with regular function
+Expected: guard should be called
+*/
+RUNNER_TEST(ScopeGuard_MakeScopeGuard_RegularFunction)
+{
+    g_guardCalled = false;
+    {
+        auto guard = DPL::MakeScopeGuard(regularFunction);
+    }
+    RUNNER_ASSERT(g_guardCalled);
+}
+
+/*
+Name: ScopeGuard_MakeScopeGuard_Functor
+Description: tests scope guard, created explicitly, with a functor
+Expected: guard should be called
+*/
+RUNNER_TEST(ScopeGuard_MakeScopeGuard_Functor)
+{
+    g_guardCalled = false;
+    {
+        auto guard = DPL::MakeScopeGuard(Functor(g_guardCalled));
+    }
+    RUNNER_ASSERT(g_guardCalled);
+}
+
+/*
+Name: ScopeGuard_MakeScopeGuard_Lambda
+Description: tests scope guard, created explicitly, with a lambda
+Expected: guard should be called
+*/
+RUNNER_TEST(ScopeGuard_MakeScopeGuard_Lambda)
+{
+    g_guardCalled = false;
+    {
+        auto guard = DPL::MakeScopeGuard(
+          [&g_guardCalled](){ g_guardCalled = true; });
+    }
+    RUNNER_ASSERT(g_guardCalled);
+}
+
+/*
+Name: ScopeGuard_Macro
+Description: tests scope guard, created implicitly, with a lambda
+Expected: guard should be called
+*/
+RUNNER_TEST(ScopeGuard_Macro)
+{
+    g_guardCalled = false;
+    {
+        DPL_SCOPE_EXIT(&g_guardCalled)
+        {
+            g_guardCalled = true;
+        };
+    }
+    RUNNER_ASSERT(g_guardCalled);
+}
+
+/*
+Name: ScopeGuard_Release
+Description: tests scope guard releasing API
+Expected: guard should not be called
+*/
+RUNNER_TEST(ScopeGuard_Release)
+{
+    g_guardCalled = false;
+    {
+        auto guard = DPL::MakeScopeGuard(
+                [&g_guardCalled](){ g_guardCalled = true; });
+        guard.Release();
+    }
+    RUNNER_ASSERT(!g_guardCalled);
+}