16 #ifndef SCOPE_SCOPE_HPP
17 #define SCOPE_SCOPE_HPP
19 #include <type_traits>
33 #if __cplusplus >= 201703L
34 # define SCOPE_NODISCARD [[nodiscard]]
35 #elif defined(__GNUC__) || defined(__CLANG__)
36 # define SCOPE_NODISCARD __attribute__ ((warn_unused_result))
37 #elif defined(_MSC_VER) && (_MSC_VER >= 1700)
38 # define SCOPE_NODISCARD _Check_return_
40 # define SCOPE_NODISCARD
52 #if (defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411) || \
53 defined(_MSC_VER) && _MSC_VER >= 1900
54 # define SCOPE_HAS_UNCAUGHT_EXCEPTIONS
57 #if !defined(SCOPE_HAS_UNCAUGHT_EXCEPTIONS)
60 # if defined(__has_include)
61 # if __has_include(<cxxabi.h>)
62 # define SCOPE_HAS_CXXABI_H
64 # elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
65 # define SCOPE_HAS_CXXABI_H
68 # if defined(SCOPE_HAS_CXXABI_H)
72 # if !(defined(__MINGW32__) && (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) < 405))
75 # define SCOPE_HAS_CXA_GET_GLOBALS
99 # if !defined(__FreeBSD__) && \
101 (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) < 407) || \
102 defined(__OpenBSD__) || \
103 (defined(__QNXNTO__) && !defined(__GLIBCXX__) && !defined(__GLIBCPP__)) || \
104 defined(_LIBCPPABI_VERSION) \
106 namespace __cxxabiv1 {
107 struct __cxa_eh_globals;
108 # if defined(__OpenBSD__)
109 extern "C" __cxa_eh_globals* __cxa_get_globals();
111 extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept __attribute__((__const__));
115 # endif // !(defined(__MINGW32__) && (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) < 405))
116 # endif // defined(SCOPE_HAS_CXXABI_H)
118 # if defined(_MSC_VER) && _MSC_VER >= 1400
120 # define SCOPE_HAS_GETPTD
123 extern "C" void* _getptd();
126 # endif // defined(_MSC_VER) && _MSC_VER >= 1400
127 #endif // !defined(SCOPE_HAS_UNCAUGHT_EXCEPTIONS)
129 #if !defined(SCOPE_HAS_UNCAUGHT_EXCEPTIONS) && \
130 !defined(SCOPE_HAS_CXA_GET_GLOBALS) && \
131 !defined(SCOPE_HAS_GETPTD)
132 # define SCOPE_UNCAUGHT_EXCEPTIONS_EMULATED
145 inline int uncaught_exception_count()
147 #if defined(SCOPE_HAS_UNCAUGHT_EXCEPTIONS)
149 return std::uncaught_exceptions();
150 #elif defined(SCOPE_HAS_CXA_GET_GLOBALS)
152 using byte =
unsigned char;
154 const auto* ptr =
reinterpret_cast<const byte*
>(::abi::__cxa_get_globals()) +
sizeof(
void*);
157 std::memcpy(&count, ptr,
sizeof(count));
159 #elif defined(SCOPE_HAS_GETPTD)
161 using byte =
unsigned char;
164 const auto offset = (
sizeof(
void*) == 8u ? 0x100 : 0x90);
165 const auto* ptr =
static_cast<const byte*
>(::scope::detail::_getptd()) + offset;
168 std::memcpy(&count, ptr,
sizeof(count));
177 return static_cast<int>(std::uncaught_exception());
188 on_exit_policy() noexcept
189 : m_should_execute{
true}
193 on_exit_policy(on_exit_policy&&) =
default;
194 on_exit_policy(
const on_exit_policy&) =
default;
195 on_exit_policy& operator=(on_exit_policy&&) =
default;
196 on_exit_policy& operator=(
const on_exit_policy&) =
default;
198 void release() noexcept
200 m_should_execute =
false;
203 bool should_execute() const noexcept
205 return m_should_execute;
210 bool m_should_execute;
220 on_fail_policy() noexcept
221 : m_exception_count{uncaught_exception_count()}
225 on_fail_policy(on_fail_policy&&) =
default;
226 on_fail_policy(
const on_fail_policy&) =
default;
227 on_fail_policy& operator=(on_fail_policy&&) =
default;
228 on_fail_policy& operator=(
const on_fail_policy&) =
default;
230 void release() noexcept
232 m_exception_count = std::numeric_limits<int>::max();
235 bool should_execute() const noexcept
237 return m_exception_count < uncaught_exception_count();
242 int m_exception_count;
249 class on_success_policy
252 on_success_policy() noexcept
253 : m_exception_count{uncaught_exception_count()}
258 on_success_policy(on_success_policy&&) =
default;
259 on_success_policy(
const on_success_policy&) =
default;
260 on_success_policy& operator=(on_success_policy&&) =
default;
261 on_success_policy& operator=(
const on_success_policy&) =
default;
263 void release() noexcept
265 m_exception_count = -1;
268 bool should_execute() const noexcept
270 return m_exception_count == uncaught_exception_count();
275 int m_exception_count;
284 std::is_nothrow_move_assignable<T>::value,
288 move_if_noexcept_assignable(T& x) noexcept
294 const T& as_const(T& t) noexcept {
return t; }
300 template <
typename T>
305 template<
typename TT,
typename Guard>
306 explicit storage(TT&& t, Guard&& guard)
307 : m_value(std::forward<TT>(t))
311 explicit storage(
const T& t)
316 explicit storage(T&& t)
317 : m_value(std::move_if_noexcept(t))
325 const T& cref() const noexcept
331 return std::move(m_value);
340 m_value = move_if_noexcept_assignable(t);
348 template <
typename T>
353 template<
typename TT,
typename Guard>
354 explicit storage(TT&& t, Guard&& guard)
359 template<
typename TT>
360 explicit storage(TT&& t)
361 : m_value(std::forward<TT>(t))
368 return m_value.get();
370 const T& cref() const noexcept
372 return m_value.get();
376 return m_value.get();
386 std::reference_wrapper<T> m_value;
393 template <
typename Fn,
typename ExitPolicy>
394 class basic_scope_guard :
public ExitPolicy
397 template <
typename ExitFunction>
398 explicit basic_scope_guard(ExitFunction&& fn)
399 noexcept(std::is_nothrow_move_constructible<Fn>::value)
401 m_function{std::forward<ExitFunction>(fn)}
406 basic_scope_guard(basic_scope_guard&& other)
407 noexcept(std::is_nothrow_move_constructible<Fn>::value)
409 m_function{other.m_function.rref(), other}
415 noexcept(noexcept(std::declval<Fn&>()()))
417 if (should_execute()) {
422 basic_scope_guard(
const basic_scope_guard&) =
delete;
423 basic_scope_guard& operator=(
const basic_scope_guard&) =
delete;
424 basic_scope_guard& operator=(basic_scope_guard&&) =
delete;
431 using ExitPolicy::release;
438 using ExitPolicy::should_execute;
442 storage<Fn> m_function;
477 template <
typename Fn>
479 :
private detail::basic_scope_guard<Fn,detail::on_exit_policy>
481 using base_type = detail::basic_scope_guard<Fn,detail::on_exit_policy>;
485 using base_type::base_type;
487 using base_type::release;
489 using base_type::should_execute;
492 #if __cplusplus >= 201703L
493 template <
typename Fn>
519 template <
typename Fn>
522 make_scope_exit(Fn&& fn);
555 template <
typename Fn>
557 :
private detail::basic_scope_guard<Fn,detail::on_success_policy>
559 using base_type = detail::basic_scope_guard<Fn,detail::on_success_policy>;
563 using base_type::base_type;
565 using base_type::release;
567 using base_type::should_execute;
570 #if __cplusplus >= 201703L
571 template <
typename Fn>
597 template <
typename Fn>
600 make_scope_success(Fn&& fn);
633 template <
typename Fn>
635 :
private detail::basic_scope_guard<Fn,detail::on_fail_policy>
637 using base_type = detail::basic_scope_guard<Fn,detail::on_fail_policy>;
641 using base_type::base_type;
643 using base_type::release;
645 using base_type::should_execute;
648 #if __cplusplus >= 201703L
649 template <
typename Fn>
675 template <
typename Fn>
678 make_scope_fail(Fn&& fn);
708 template <
typename R,
typename D>
712 (std::is_move_constructible<R>::value &&
713 std::is_nothrow_move_constructible<R>::value) ||
714 std::is_copy_constructible<R>::value,
715 "unique_resource must be nothrow_move_constructible or copy_constructible"
718 (std::is_move_constructible<R>::value &&
719 std::is_nothrow_move_constructible<D>::value) ||
720 std::is_copy_constructible<D>::value,
721 "deleter must be nothrow_move_constructible or copy_constructible"
724 template <
typename T,
typename TT>
725 using is_nothrow_move_or_copy_constructible_from
726 = std::integral_constant<bool,(std::is_reference<TT>::value ||
727 !std::is_nothrow_constructible<T,TT>::value)
728 ? std::is_constructible<T, TT const &>::value
729 : std::is_constructible<T, TT>::value>;
731 template <
typename T,
typename U,
typename TT,
typename UU>
732 using enable_constructor_t =
typename std::enable_if<
733 is_nothrow_move_or_copy_constructible_from<T,TT>::value &&
734 is_nothrow_move_or_copy_constructible_from<U,UU>::value
742 using resource_type =
typename std::remove_reference<R>::type;
743 using deleter_type =
typename std::remove_reference<D>::type;
753 template <
typename RR,
typename DD,
754 typename = enable_constructor_t<R,D,RR,DD>>
756 noexcept(std::is_nothrow_constructible<R, RR>::value &&
757 std::is_nothrow_constructible<D, DD>::value);
766 noexcept(std::is_nothrow_move_constructible<R>::value &&
767 std::is_nothrow_move_constructible<D>::value);
784 noexcept(std::is_nothrow_assignable<R&, R>::value &&
785 std::is_nothrow_assignable<D&, D>::value);
799 void reset() noexcept;
808 template <
typename RR>
817 void release() noexcept;
827 const resource_type&
get()
const noexcept;
832 const deleter_type& get_deleter()
const noexcept;
834 template<
typename RR=R>
835 typename std::enable_if<std::is_pointer<RR>::value,
typename std::remove_pointer<R>::type&>::type
836 operator*()
const noexcept;
838 template<
typename RR=R>
839 typename std::enable_if<std::is_pointer<RR>::value,R>::type
840 operator->()
const noexcept;
847 detail::storage<R> m_resource;
848 detail::storage<D> m_deleter;
849 bool m_execute_on_destruction;
856 template <
typename RR,
typename DD>
859 void do_assign(std::true_type, std::true_type,
unique_resource&& other);
860 void do_assign(std::false_type, std::true_type,
unique_resource&& other);
861 void do_assign(std::true_type, std::false_type,
unique_resource&& other);
862 void do_assign(std::false_type, std::false_type,
unique_resource&& other);
864 template <
typename T,
typename U,
typename S>
866 make_unique_resource_checked(T&&,
const S&, U&&);
872 #if __cplusplus >= 201703L
875 template<
typename R,
typename D>
904 template <
typename R,
typename D,
typename S=R>
907 make_unique_resource_checked(R&& resource,
const S& invalid, D&& d);
929 template <
typename R,
typename D>
932 make_unique_resource(R&& resource, D&& d)
933 noexcept(std::is_nothrow_constructible<typename std::decay<R>::type, R>::value &&
934 std::is_nothrow_constructible<typename std::decay<D>::type, D>::value);
938 template <typename Fn>
939 scope::
scope_exit<typename std::decay<Fn>::type>
940 scope::make_scope_exit(Fn&& fn)
944 return result_type{std::forward<Fn>(fn)};
947 template <
typename Fn>
949 scope::make_scope_success(Fn&& fn)
951 using result_type = scope_success<typename std::decay<Fn>::type>;
953 return result_type{std::forward<Fn>(fn)};
956 template <
typename Fn>
958 scope::make_scope_fail(Fn&& fn)
960 using result_type = scope_fail<typename std::decay<Fn>::type>;
962 return result_type{std::forward<Fn>(fn)};
973 template <
typename R,
typename D>
974 template <
typename RR,
typename DD,
typename>
976 noexcept(std::is_nothrow_constructible<R, RR>::value &&
977 std::is_nothrow_constructible<D, DD>::value)
983 template <
typename R,
typename D>
985 noexcept(std::is_nothrow_move_constructible<R>::value &&
986 std::is_nothrow_move_constructible<D>::value)
987 : m_resource{other.m_resource.rref(), make_scope_exit([]{})},
988 m_deleter{other.m_deleter.rref(), make_scope_exit([&other,
this]
990 if (other.m_execute_on_destruction) {
991 auto& deleter = m_deleter.ref();
992 deleter(m_resource.ref());
996 m_execute_on_destruction{other.m_execute_on_destruction}
998 other.m_execute_on_destruction =
false;
1002 template <
typename R,
typename D>
1010 template <
typename R,
typename D>
1013 noexcept(std::is_nothrow_assignable<R&, R>::value &&
1014 std::is_nothrow_assignable<D&, D>::value)
1016 if (&other ==
this )
return (*
this);
1019 std::is_nothrow_move_assignable<R>{},
1020 std::is_nothrow_move_assignable<D>{},
1023 m_execute_on_destruction = other.m_execute_on_destruction;
1024 other.m_execute_on_destruction =
false;
1032 template <
typename R,
typename D>
1036 if (m_execute_on_destruction) {
1037 m_execute_on_destruction =
false;
1039 auto& deleter = m_deleter.ref();
1040 deleter( m_resource.ref() );
1044 template <
typename R,
typename D>
1045 template <
typename RR>
1048 auto guard = make_scope_fail([&r,
this]{
1049 auto& deleter = m_deleter.ref();
1055 m_resource.set( std::forward<RR>(r) );
1056 m_execute_on_destruction =
true;
1060 template <
typename R,
typename D>
1064 m_execute_on_destruction =
false;
1071 template <
typename R,
typename D>
1072 inline const typename scope::unique_resource<R,D>::resource_type&
1076 return m_resource.cref();
1079 template <
typename R,
typename D>
1080 inline const typename scope::unique_resource<R,D>::deleter_type&
1084 return m_deleter.cref();
1087 template <
typename R,
typename D>
1088 template<
typename RR>
1089 inline typename std::enable_if<std::is_pointer<RR>::value,
typename std::remove_pointer<R>::type&>::type
1096 template <
typename R,
typename D>
1097 template<
typename RR>
1098 inline typename std::enable_if<std::is_pointer<RR>::value,R>::type
1110 template <
typename R,
typename D>
1111 template <
typename RR,
typename DD>
1114 bool execute_on_destruction)
1115 : m_resource{std::forward<RR>(r), make_scope_exit([&d,&r]
1119 m_deleter{std::forward<DD>(d), make_scope_exit([&d,
this]{
1120 d(m_resource.ref());
1122 m_execute_on_destruction{execute_on_destruction}
1129 template <
typename R,
typename D>
1134 m_resource = std::move(other.resource);
1135 m_deleter = std::move(other.deleter);
1138 template <
typename R,
typename D>
1143 m_resource = detail::as_const(other.resource);
1144 m_deleter = std::move(other.deleter);
1147 template <
typename R,
typename D>
1152 m_resource = std::move(other.resource);
1153 m_deleter = detail::as_const(other.deleter);
1156 template <
typename R,
typename D>
1161 m_resource = detail::as_const(other.resource);
1162 m_deleter = detail::as_const(other.deleter);
1173 template <
typename R,
typename D,
typename S>
1175 scope::make_unique_resource_checked(R&& resource,
const S& invalid, D&& d)
1179 using resource_type =
typename std::decay<R>::type;
1180 using deleter_type =
typename std::decay<D>::type;
1183 const auto execute_on_destruction = !(resource == invalid);
1186 std::forward<R>(resource),
1188 execute_on_destruction
1192 template <
typename R,
typename D>
1194 scope::make_unique_resource(R&& resource, D&& d)
1195 noexcept(std::is_nothrow_constructible<
typename std::decay<R>::type, R>::value &&
1196 std::is_nothrow_constructible<
typename std::decay<D>::type, D>::value)
1198 using resource_type =
typename std::decay<R>::type;
1199 using deleter_type =
typename std::decay<D>::type;
1203 std::forward<R>(resource),
const deleter_type & get_deleter() const noexcept
Gets a reference to the underlying deleter.
An exit handler for handling throwing cases.
A resource with unique-ownership semantics.
~unique_resource()
Equivalent to invoking reset()
An exit handler for handling both sucess an error cases.
const resource_type & get() const noexcept
Gets a reference to the underlying resource.
void release() noexcept
Releases the underlying stored resource from being managed by this unique_resource class...
An exit handler for handling non-throwing cases.
unique_resource & operator=(unique_resource &&other) noexcept(std::is_nothrow_assignable< R &, R >::value &&std::is_nothrow_assignable< D &, D >::value)
void reset() noexcept
Resets the resource managed by this unique_resource.