C++: Generic locker class for thread-safe proxy-access to arbitrary objects

I created a template class which can be used to provide thread-safe access to methods and fields of some target object.
It provides a convenient double-smart-pointer proxy mechanism for accessing one method or field of the target, and a functor mechanism for executing multiple operations on the target within one lock.
Naturally with a RAII solution, the double-smart-pointer style provides no thread-safety for iterator/pointer-based access or any other kind of access which involves taking pointers/references to the target and accessing them outside the lock. For such use cases, wrap the entire operation in the functor style instead.

Source is below and also available on Github

#if 0
(
set -eu
declare -r tmp="$(mktemp)"
g++ -std=c++14 -Wall -Wextra -o "$tmp" "$0"
valgrind --quiet "$tmp"
)
exit 0
#endif
/* Run this file with bash to compile+execute it */
#include <mutex>
#include <iostream>
#include <type_traits>

/* Demo: double-smart pointer for automatic locking of arbitrary class */

template <typename T>
class ThreadSafe
{
	T t;
	mutable std::mutex mx;
	class Proxy
	{
		std::unique_lock<std::mutex> lock;
		T& t;
	public:
		explicit Proxy(std::mutex& mx, T& t) : lock(mx), t(t) { std::cout << "  (lock)" << std::endl; }
		Proxy(Proxy&& src) : lock(std::move(src.lock)), t(src.t) { }
		~Proxy() { std::cout << "  (unlock)" << std::endl; }
		T *operator -> () { return &t; }
	};
public:
	template <typename ...Args>
	explicit ThreadSafe(Args&&... args) : t(std::forward<Args>(args)...) { }
	Proxy operator -> () { return Proxy(mx, t); }
	template <typename Func, typename R = typename std::enable_if<!std::is_void<typename std::result_of<Func(T&)>::type>::value>::type>
	auto operator () (const Func& func) {
		std::lock_guard<std::mutex> lock(mx);
		return func(t);
	}
	template <typename Func, typename R = typename std::enable_if<std::is_void<typename std::result_of<Func(T&)>::type>::value>::type>
	void operator () (const Func& func) {
		func( * operator -> () . operator -> () );
	}
};

int main(int argc, char *argv[])
{
	(void) argc;
	(void) argv;
	/* Initialise s as a thread-safe wrapper around a std::string */
	ThreadSafe<std::string> s("potato");
	/* For doing one operation, the pointer proxy style is convenient */
	s->append("es are awesome");
	/* Function style: Passed function is executed within lock and may do multiple operations */
	s([] (auto& str) { str += "!"; });
	/* Not thread safe, as our lock object expires before the pointer is used.  Use the function style in place of this: */
	std::cout << s->c_str() << std::endl;
	return 0;
}

C / C++ data type specification

I see many questions on StackOverflow regarding how to decipher C and C++ type definitions. I also used to get asked about them a fair bit back at university. Needless to say, plenty of people see them as some weird messy voodoo.

Personally, I find them easy as they are precise, terse, and consistent with the language syntax. I’m not some hyper-intelligent demi-god with a superpower for deciphering syntax though, so why do I find them easy?

The parsing rules

There are three rules to follow for parsing a C/C++ type specifier:

  • Start at the type name (or where the name should be in the case of anonymous types)
  • Move right when you can
  • Move left when you must

And that’s all there is to it. Like a really basic Turing machine.

Examples

int *var

int *var;

Start at the variable name. We can’t move right, so we move left: *. We still can’t move right, so we move left again: int.

So we have the expression var * int – “var is a pointer to an int”.

When I first moved to C from Pascal, I used to write this kind of type as int* var, in line with the Pascal style (var: ^Integer;) where you have “variable-name: type”. In C however, variable declarations are expressed a little differently: int *var says that *var is an int. For more complex types, this style of thinking is quite important, so I stopped using the Pascal-style for denoting types in C.

char * const (*)(void (*a)(int), const int * b, int const * c, int * const d)

char * const (*)(long (*a[])(int), const int * b, int const * c, int * const d)
              ^
         start here

Start at the right of the asterisk illustrated above (where the variable name would go if the declaration wasn’t anonymous). We can’t move right, so we move left: *. Now we’ve consumed the parenthesized part so we can move right: (...). We can’t move right, so we must move left, consuming: const, *, char.

Now we have:

* (...) const * char – “pointer to function that returns constant pointer to char”

To parse the types of the function arguments, re-apply the process for each type:

  • a – long (*a[])(int) parses to a [] * (int) long – “a is array of pointers to function of int that returns long”.
  • b – const int * b parses to b * int const – “b is pointer to int that is constant”
  • c – int const * c parses to c * const int – “c is pointer to constant int” (same as type of argument “b”)
  • d – int * const d parses to d const * int – “d is constant pointer to int”.

“b” and “c” point to constant int, so ints *b and *c are immutable but pointers b and c are mutable.

“d” is immutable, but the int that it points to (*d) is mutable.

Putting it all together:

char * const (*)(long (*a[])(int), const int * b, int const * c, int * const d)

“pointer to function of (a, b, c, d) that returns a constant pointer to char”, where:

  • “a” is “array of pointers to function of int that returns long”
  • “b” is “pointer to constant int”
  • “c” is “pointer to constant int”
  • “d” is “constant pointer to int”

Sure, it seems complicated, but the type expression was complicated. To represent that same type in Pascal, you would need several separate type definitions to compose the final data type. In C++, it’s a single expression, which can be aliased or typedef’d as needed.