Skip to content

Commit

Permalink
Use last argument to construct the mutex.
Browse files Browse the repository at this point in the history
  • Loading branch information
LouisCharlesC committed Oct 31, 2023
1 parent ddcc4a2 commit e8f78c9
Show file tree
Hide file tree
Showing 3 changed files with 396 additions and 367 deletions.
77 changes: 59 additions & 18 deletions include/safe/safe.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@

namespace safe
{
namespace impl
{
template <typename... Ts> struct Last;
template <typename First, typename Second, typename... Others> struct Last<First, Second, Others...>
{
using type = typename Last<Second, Others...>::type;
};
template <typename T> struct Last<T>
{
using type = T;
};
template <> struct Last<>
{
using type = void;
};
} // namespace impl
template <typename... Ts> using Last = typename impl::Last<Ts...>::type;

/**
* @brief Use this tag to default construct the mutex when constructing a Safe object.
*/
Expand Down Expand Up @@ -201,32 +219,47 @@ template <typename ValueType, typename MutexType = std::mutex> class Safe
* @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.
* @brief Construct a Safe object, forwarding the last argument to construct the mutex and the other arguments to
* construct the value object. This constructor will be selected if the mutex can be constructed from the last
* argument of the parameter pack. To avoid using the last argument to construct the mutex, use the
* default_construct_mutex tag.
*
* @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.
* @tparam Args Deduced from args.
* @tparam SFINAE constraint.
* @param args Perfect forwarding arguments to split between the value and mutex.
*/
template <typename... ValueArgs>
explicit Safe(DefaultConstructMutex, ValueArgs &&...valueArgs)
: m_mutex(), m_value(std::forward<ValueArgs>(valueArgs)...)
template <typename... Args,
typename std::enable_if_t<std::is_constructible_v<MutexType, Last<Args...>>, bool> = true>
explicit Safe(Args &&...args)
: Safe(std::forward_as_tuple(std::forward<Args>(args)...),
std::make_index_sequence<sizeof...(args) - 1>()) // delegate to a private constructor to split the
// parameter pack
{
}
/**
* @brief Construct a Safe object, forwarding the first argument to construct the mutex and the other arguments to
* construct the value object.
* @brief Construct a Safe object, forwarding all arguments to construct the value object. This constructor will be
* selected if the mutex cannot be constructed from the last argument of the parameter pack.
*
* @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.
* @tparam Args Deduced from args.
* @param args Perfect forwarding arguments to construct the value object.
*/
template <typename MutexArg, typename... ValueArgs>
explicit Safe(MutexArg &&mutexArg, ValueArgs &&...valueArgs)
: m_mutex{std::forward<MutexArg>(mutexArg)}, m_value(std::forward<ValueArgs>(valueArgs)...)
template <typename... Args,
typename std::enable_if_t<!std::is_constructible_v<MutexType, Last<Args...>>, bool> = true>
explicit Safe(Args &&...args) : m_mutex{}, m_value(std::forward<Args>(args)...)
{
}
/**
* @brief Construct a Safe object, forwarding all arguments but the first (the default_construct_mutex tag) to
* construct the value object. This constructor will be selected even if the mutex can be constructed from the last
* argument of the parameter pack.
*
* @tparam Args Deduced from args.
* @param default_construct_mutex tag.
* @param args Perfect forwarding arguments to construct the value object.
*/
template <typename... Args>
explicit Safe(const DefaultConstructMutex &, Args &&...args) : m_mutex{}, m_value(std::forward<Args>(args)...)
{
}

Expand Down Expand Up @@ -298,6 +331,14 @@ template <typename ValueType, typename MutexType = std::mutex> class Safe
}

private:
template <typename ArgsTuple, size_t... AllButLast>
explicit Safe(ArgsTuple &&args, std::index_sequence<AllButLast...>)
: m_mutex{std::get<sizeof...(AllButLast)>(
std::forward<ArgsTuple>(args))}, // use the last argument to conrtuct the mutex
m_value(std::get<AllButLast>(std::forward<ArgsTuple>(args))...) // and the rest to construc the value
{
}

/// The helper object that holds the mutable mutex, or a reference to a mutex.
impl::MutableIfNotReference<MutexType> m_mutex;
/// The value to protect.
Expand Down

0 comments on commit e8f78c9

Please sign in to comment.