summaryrefslogtreecommitdiffstats
path: root/src/safe/safe.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/safe/safe.h')
-rw-r--r--src/safe/safe.h359
1 files changed, 359 insertions, 0 deletions
diff --git a/src/safe/safe.h b/src/safe/safe.h
new file mode 100644
index 0000000..2beab55
--- /dev/null
+++ b/src/safe/safe.h
@@ -0,0 +1,359 @@
+/**
+ * @file safe.h
+ * @author L.-C. C.
+ * @brief
+ * @version 0.1
+ * @date 2018-09-21
+ *
+ * @copyright Copyright (c) 2018
+ *
+ */
+
+#pragma once
+
+#include "accessmode.h"
+#include "defaulttypes.h"
+#include "mutableref.h"
+
+#include <type_traits>
+#include <utility>
+
+#if __cplusplus >= 201703L
+#define EXPLICIT_IF_CPP17 explicit
+#define EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17 ReturnType
+#else
+#define EXPLICIT_IF_CPP17
+#define EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17
+#endif
+
+namespace safe
+{
+ /**
+ * @brief Use this tag to default construct the mutex when constructing a
+ * Safe object.
+ */
+ struct DefaultConstructMutex {};
+ static constexpr DefaultConstructMutex default_construct_mutex;
+
+ /**
+ * @brief Wraps a value together with a mutex.
+ *
+ * @tparam ValueType The type of the value to protect.
+ * @tparam MutexType The type of the mutex.
+ */
+ template<typename ValueType, typename MutexType = DefaultMutex>
+ class Safe
+ {
+ private:
+ /// Type ValueType with reference removed, if present
+ using RemoveRefValueType = typename std::remove_reference<ValueType>::type;
+ /// Type MutexType with reference removed, if present
+ using RemoveRefMutexType = typename std::remove_reference<MutexType>::type;
+
+ /**
+ * @brief Manages a mutex and gives pointer-like access to a value
+ * object.
+ *
+ * @tparam LockType The type of the lock object that manages the
+ * mutex, example: std::lock_guard.
+ * @tparam Mode Determines the access mode of the Access
+ * object. Can be either AccessMode::ReadOnly or
+ * AccessMode::ReadWrite.
+ */
+ template<template<typename> class LockType, AccessMode Mode>
+ class Access
+ {
+ // Make sure AccessMode is ReadOnly if a read-only lock is used
+ static_assert(!(AccessTraits<LockType<RemoveRefMutexType>>::IsReadOnly && Mode==AccessMode::ReadWrite), "Cannot have ReadWrite access mode with ReadOnly lock. Check the value of AccessTraits<LockType>::IsReadOnly if it exists.");
+
+ /// ValueType with const qualifier if AccessMode is ReadOnly.
+ using ConstIfReadOnlyValueType = typename std::conditional<Mode==AccessMode::ReadOnly, const RemoveRefValueType, RemoveRefValueType>::type;
+
+ public:
+ /// Pointer-to-const ValueType
+ using ConstPointerType = const ConstIfReadOnlyValueType*;
+ /// Pointer-to-const ValueType if Mode is ReadOnly, pointer to ValueType otherwise.
+ using PointerType = ConstIfReadOnlyValueType*;
+ /// Reference-to-const ValueType
+ using ConstReferenceType = const ConstIfReadOnlyValueType&;
+ /// Reference-to-const ValueType if Mode is ReadOnly, reference to ValueType otherwise.
+ using ReferenceType = ConstIfReadOnlyValueType&;
+
+ /**
+ * @brief Construct an Access object from a possibly const
+ * reference to the value object and any additionnal argument
+ * needed to construct the Lock object.
+ *
+ * @tparam LockArgs Deduced from lockArgs.
+ * @param value Reference to the value.
+ * @param lockArgs Arguments needed to construct the lock object.
+ */
+ template<typename... OtherLockArgs>
+ EXPLICIT_IF_CPP17
+ Access(ReferenceType value, MutexType& mutex, OtherLockArgs&&... otherLockArgs):
+ lock(mutex, std::forward<OtherLockArgs>(otherLockArgs)...),
+ m_value(value)
+ {}
+
+ /**
+ * @brief Construct a read-only Access object from a const
+ * safe::Safe object and any additionnal argument needed to
+ * construct the Lock object.
+ *
+ * If needed, you can provide additionnal arguments to construct
+ * the lock object (such as std::adopt_lock). The mutex from the
+ * safe::Locakble object is already passed to the lock object's
+ * constructor though, you must not provide it.
+ *
+ * @tparam OtherLockArgs Deduced from otherLockArgs.
+ * @param safe The const Safe object to give protected access to.
+ * @param otherLockArgs Other arguments needed to construct the lock
+ * object.
+ */
+ template<typename... OtherLockArgs>
+ EXPLICIT_IF_CPP17
+ Access(const Safe& safe, OtherLockArgs&&... otherLockArgs):
+ Access(safe.m_value, safe.m_mutex.get, std::forward<OtherLockArgs>(otherLockArgs)...)
+ {}
+
+ /**
+ * @brief Construct a read-write Access object from a
+ * safe::Safe object and any additionnal argument needed to
+ * construct the Lock object.
+ *
+ * If needed, you can provide additionnal arguments to construct
+ * the lock object (such as std::adopt_lock). The mutex from the
+ * safe object is already passed to the lock object's constructor
+ * though, you must not provide it.
+ *
+ * @tparam OtherLockArgs Deduced from otherLockArgs.
+ * @param safe The Safe object to give protected access to.
+ * @param otherLockArgs Other arguments needed to construct the lock
+ * object.
+ */
+ template<typename... OtherLockArgs>
+ EXPLICIT_IF_CPP17
+ Access(Safe& safe, OtherLockArgs&&... otherLockArgs):
+ Access(safe.m_value, safe.m_mutex.get, std::forward<OtherLockArgs>(otherLockArgs)...)
+ {}
+
+ /**
+ * @brief Construct an Access object from another one.
+ * OtherLockType must implement release() like std::unique_lock
+ * does.
+ *
+ * @tparam OtherLockType Deduced from otherAccess.
+ * @tparam OtherMode Deduced from otherAccess.
+ * @tparam OtherLockArgs Deduced from otherLockArgs.
+ * @param otherAccess The Access object to construct from.
+ * @param otherLockArgs Other arguments needed to construct the lock
+ * object.
+ */
+ template<template<typename> class OtherLockType, AccessMode OtherMode, typename... OtherLockArgs>
+ EXPLICIT_IF_CPP17
+ Access(Access<OtherLockType, OtherMode>& otherAccess, OtherLockArgs&&... otherLockArgs):
+ Access(*otherAccess, *otherAccess.lock.release(), std::adopt_lock, std::forward<OtherLockArgs>(otherLockArgs)...)
+ {
+ static_assert(OtherMode == AccessMode::ReadWrite || OtherMode == Mode, "Cannot construct a ReadWrite Access object from a ReadOnly one!");
+ }
+
+ /**
+ * @brief Const accessor to the value.
+ * @return ConstPointerType Const pointer to the protected value.
+ */
+ ConstPointerType operator->() const noexcept
+ {
+ return &m_value;
+ }
+
+ /**
+ * @brief Accessor to the value.
+ * @return ValuePointerType Pointer to the protected value.
+ */
+ PointerType operator->() noexcept
+ {
+ return &m_value;
+ }
+
+ /**
+ * @brief Const accessor to the value.
+ * @return ConstValueReferenceType Const reference to the protected
+ * value.
+ */
+ ConstReferenceType operator*() const noexcept
+ {
+ return m_value;
+ }
+
+ /**
+ * @brief Accessor to the value.
+ * @return ValueReferenceType Reference to the protected.
+ */
+ ReferenceType operator*() noexcept
+ {
+ return m_value;
+ }
+
+ /// The lock that manages the mutex.
+ mutable LockType<RemoveRefMutexType> lock;
+
+ private:
+ /// The protected value.
+ ReferenceType m_value;
+ };
+
+ /// Reference-to-const ValueType.
+ using ConstValueReferenceType = const RemoveRefValueType&;
+ /// Reference to ValueType.
+ using ValueReferenceType = RemoveRefValueType&;
+ /// Reference to MutexType.
+ using MutexReferenceType = RemoveRefMutexType&;
+
+ public:
+ /// Aliases to ReadAccess and WriteAccess classes for this Safe class.
+ template<template<typename> class LockType=DefaultReadOnlyLock>
+ using ReadAccess = Access<LockType, AccessMode::ReadOnly>;
+ template<template<typename> class LockType=DefaultReadWriteLock>
+ using WriteAccess = Access<LockType, AccessMode::ReadWrite>;
+
+ /**
+ * @brief Construct a Safe object
+ */
+ Safe() = default;
+
+ /**
+ * @brief Construct a Safe object with default construction of
+ * the mutex and perfect forwarding of the other arguments to
+ * construct the value object.
+ *
+ * @tparam ValueArgs Deduced from valueArgs.
+ * @param valueArgs Perfect forwarding arguments to construct the value object.
+ * @param tag Indicates that the mutex should be default constructed.
+ */
+ template<typename... ValueArgs>
+ explicit Safe(DefaultConstructMutex, ValueArgs&&... valueArgs):
+ m_mutex(),
+ m_value(std::forward<ValueArgs>(valueArgs)...)
+ {}
+ /**
+ * @brief Construct a Safe object, forwarding the first
+ * argument to construct the mutex and the other arguments to
+ * construct the value object.
+ *
+ * @tparam MutexArg Deduced from mutexArg.
+ * @tparam ValueArgs Deduced from valueArgs.
+ * @param valueArgs Perfect forwarding arguments to construct the
+ * value object.
+ * @param mutexArg Perfect forwarding argument to construct the
+ * mutex object.
+ */
+ template<typename MutexArg, typename... ValueArgs>
+ explicit Safe(MutexArg&& mutexArg, ValueArgs&&... valueArgs):
+ m_mutex{std::forward<MutexArg>(mutexArg)},
+ m_value(std::forward<ValueArgs>(valueArgs)...)
+ {}
+
+ /// Delete all copy/move construction/assignment, as these operations
+ /// require locking the mutex under the covers.
+ /// Use copy(), assign() and other defined constructors to get the behavior
+ /// you need with an explicit syntax.
+ Safe(const Safe&) = delete;
+ Safe(Safe&&) = delete;
+ Safe& operator =(const Safe&) = delete;
+ Safe& operator =(Safe&&) = delete;
+
+ template<template<typename> class LockType=DefaultReadOnlyLock, typename... LockArgs>
+ ReadAccess<LockType> readAccess(LockArgs&&... lockArgs) const
+ {
+ // using ReturnType = ReadAccess<LockType>;
+ return EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17{*this, std::forward<LockArgs>(lockArgs)...};
+ }
+
+ template<template<typename> class LockType=DefaultReadWriteLock, typename... LockArgs>
+ WriteAccess<LockType> writeAccess(LockArgs&&... lockArgs)
+ {
+ // using ReturnType = WriteAccess<LockType>;
+ return EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17{*this, std::forward<LockArgs>(lockArgs)...};
+ }
+
+ template<template<typename> class LockType=DefaultReadOnlyLock, typename... LockArgs>
+ RemoveRefValueType copy(LockArgs&&... lockArgs) const
+ {
+ return *readAccess<LockType>(std::forward<LockArgs>(lockArgs)...);
+ }
+
+ template<template<typename> class LockType=DefaultReadWriteLock, typename... LockArgs>
+ void assign(ConstValueReferenceType value, LockArgs&&... lockArgs)
+ {
+ *writeAccess<LockType>(std::forward<LockArgs>(lockArgs)...) = value;
+ }
+ template<template<typename> class LockType=DefaultReadWriteLock, typename... LockArgs>
+ void assign(RemoveRefValueType&& value, LockArgs&&... lockArgs)
+ {
+ *writeAccess<LockType>(std::forward<LockArgs>(lockArgs)...) = std::move(value);
+ }
+
+ /**
+ * @brief Unsafe const accessor to the value. If you use this
+ * function, you exit the realm of safe!
+ *
+ * @return ConstValueReferenceType Const reference to the value
+ * object.
+ */
+ ConstValueReferenceType unsafe() const noexcept
+ {
+ return m_value;
+ }
+ /**
+ * @brief Unsafe accessor to the value. If you use this function,
+ * you exit the realm of safe!
+ *
+ * @return ValueReferenceType Reference to the value object.
+ */
+ ValueReferenceType unsafe() noexcept
+ {
+ return m_value;
+ }
+
+ /**
+ * @brief Accessor to the mutex.
+ *
+ * @return MutexReferenceType Reference to the mutex.
+ */
+ MutexReferenceType mutex() const noexcept
+ {
+ return m_mutex.get;
+ }
+
+ private:
+ /// The helper object that holds the mutable mutex, or a reference to a mutex.
+ impl::MutableIfNotReference<MutexType> m_mutex;
+ /// The value to protect.
+ ValueType m_value;
+ };
+
+ /**
+ * @brief Type alias for read-only Access.
+ *
+ * @tparam SafeType The type of Safe object to give read-only access to.
+ * @tparam LockType=DefaultReadOnlyLock The type of lock.
+ */
+ template<
+ typename SafeType,
+ template<typename> class LockType=DefaultReadOnlyLock>
+ using ReadAccess = typename SafeType::template ReadAccess<LockType>;
+
+ /**
+ * @brief Type alias for read-write Access.
+ *
+ * @tparam SafeType The type of Safe object to give read-write access to.
+ * @tparam LockType=DefaultReadWriteLock The type of lock.
+ */
+ template<
+ typename SafeType,
+ template<typename> class LockType=DefaultReadWriteLock>
+ using WriteAccess = typename SafeType::template WriteAccess<LockType>;
+} // namespace safe
+
+#undef EXPLICIT_IF_CPP17
+#undef EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17