В современном C ++ (C ++ 17) функция будет выглядеть примерно так:
template <typename Mutex, typename Func, typename... Args>
decltype(auto) doMutexProtected(Mutex& mutex, Func&& func, Args&&... args)
{
std::unique_lock lg(mutex);
return std::forward<Func>(func)(std::forward<Args>(args)...);
}
Это блокирует мьютекс в типе RAII, так что все пути выхода освобождают мьютекс, а затем прекрасно перенаправляют функцию и ееаргументы, возвращающие точный тип func
возвращает.
Теперь, поскольку вы не можете использовать современный C ++, мы должны постараться реализовать как можно больше из вышеперечисленного, и есть пара способов, которыми вы могли быподойти к проблеме.Реализация std::unique_lock
довольно тривиальна.В зависимости от того, какие функции вы хотите, это может быть просто, как
template <typename Mutex>
class my_unique_lock
{
public:
unique_lock(Mutex& mutex) : mutex(mutex) { mutex.lock(); }
~unique_lock() { mutex.unlock(); }
private:
Mutex& mutex;
unique_lock(unique_lock const&); // make it non copyable
};
Так, что покрыть 25% проблемы :).К сожалению, это была самая легкая часть.Поскольку в C ++ 98/03 нет decltype(auto)
, или даже decltype
или auto
, нам нужно найти другой способ получения возвращаемого типа.Мы могли бы сделать это void
и использовать выходной параметр, что означало бы, что вам не нужно ничего указывать при вызове функции, но это означает, что вы не можете получить ссылку на то, что возвращается.Ценой необходимости указать тип возвращаемого вами значения вы могли бы иметь такую функцию, как
template <typename Ret, typename Mutex, typename Func, typename Arg1>
Ret doMutexProtected(Mutex& mutex, Func func, Arg1 arg1)
{
my_unique_lock<Mutex> lg(mutex);
return func(arg1);
}
, и вы бы назвали ее как
T foo = doMutexProtected<T>(mutex, func, arg);
T& bar = doMutexProtected<T&>(mutex, func, arg);
, поскольку C ++ 98/03 неУ вас нет вариативных шаблонов, вам остается добавить к этому кучу перегрузок для различного количества аргументов, и вам придется решить, в какой момент достаточно аргументов, то есть:
template <typename Ret, typename Mutex, typename Func, typename Arg1>
Ret doMutexProtected(Mutex& mutex, Func func, Arg1 arg1) {...}
template <typename Ret, typename Mutex, typename Func, typename Arg1, typename Arg2>
Ret doMutexProtected(Mutex& mutex, Func func, Arg1 arg1, Arg2 arg2) {...}
template <typename Ret, typename Mutex, typename Func, typename Arg1, typename Arg2, typename Arg3>
Ret doMutexProtected(Mutex& mutex, Func func, Arg1 arg1, Arg2 arg2, Arg3 arg3) {...}
...
итогда вам приходится иметь дело со ссылками.Современная версия отлично пересылает все (ничего не копируется, если это не требуется Func
).Мы не можем сделать это в C ++ 98/03, поэтому нам нужно добавить все имеющиеся ссылочные перестановки, чтобы мы не делали ненужных копий, как в первой версии.Это означает, что
template <typename Ret, typename Mutex, typename Func, typename Arg1>
Ret doMutexProtected(Mutex& mutex, Func func, Arg1 arg1)
на самом деле должно быть
template <typename Ret, typename Mutex, typename Func, typename Arg1>
Ret doMutexProtected(Mutex& mutex, Func& func, Arg1& arg1) {...}
template <typename Ret, typename Mutex, typename Func, typename Arg1>
Ret doMutexProtected(Mutex& mutex, Func const& func, Arg1& arg1) {...}
template <typename Ret, typename Mutex, typename Func, typename Arg1>
Ret doMutexProtected(Mutex& mutex, Func& func, Arg1 const& arg1) {...}
template <typename Ret, typename Mutex, typename Func, typename Arg1>
Ret doMutexProtected(Mutex& mutex, Func conts& func, Arg1 const& arg1) {...}
, и это будет увеличиваться по мере добавления дополнительных параметров.
Если вы не хотите делатьвсе это, я полагаю, Boost выполнило хотя бы часть этой работы для C ++ 03, и вы можете использовать их утилиты.