Created
April 19, 2021 19:06
-
-
Save nixiz/5bc33bbc7d439e2c0e2a293a4bd55664 to your computer and use it in GitHub Desktop.
Thread safe accessor encapsulation implementation in C++
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <mutex> | |
#include <assert.h> | |
/* | |
From the book "Modern C++ Design: Generic Programming and Design Patterns Applied" Andrei Alexandrescu | |
There are interesting applications of smart pointer layering, mainly because of the mechanics of | |
operator->. When you apply operator-> to a type that's not a built-in pointer, the compiler does an | |
interesting thing. After looking up and applying the user-defined operator-> to that type, it applies | |
operator-> again to the result. The compiler keeps doing this recursively until it reaches a pointer to a | |
built-in type, and only then proceeds with member access. It follows that a smart pointer's operator-> | |
does not have to return a pointer. It can return an object that in turn implements operator->, without | |
changing the use syntax. | |
This leads to a very interesting idiom: pre-and postfunction calls (Stroustrup 2000). If you return an object | |
of type PointerType by value from operator->, the sequence of execution is as follows: | |
1. Constructor of PointerType | |
2. PointerType::operator-> called; likely returns a pointer to an object of type | |
PointeeType | |
3. Member access for PointeeType?likely a function call | |
4. Destructor of PointerType | |
In a nutshell, you have a nifty way of implementing locked function calls. This idiom has broad uses with | |
multithreading and locked resource access. You can have PointerType's constructor lock the resource, | |
and then you can access the resource; finally, Pointer Type's destructor unlocks the resource. | |
*/ | |
template <typename T> | |
struct thread_safe_mediator { | |
thread_safe_mediator(const T* p) : ptr(const_cast<T*>(p)) | |
{ | |
//printf("\nthread_safe_mediator::thread_safe_mediator()"); | |
if (ptr != 0) | |
ptr->lock(); | |
} | |
~thread_safe_mediator(void) | |
{ | |
//printf("\nthread_safe_mediator::~thread_safe_mediator()"); | |
if (ptr != 0) | |
ptr->unlock(); | |
} | |
operator T* () | |
{ | |
return ptr; | |
} | |
T* operator->() | |
{ | |
return ptr; | |
} | |
private: | |
thread_safe_mediator() = delete; | |
// cannot delete copy ctor because of thread safety policy | |
// classes are returning copy of this class | |
//thread_safe_mediator(const thread_safe_mediator&) = delete; | |
thread_safe_mediator& operator = (const thread_safe_mediator&) = delete; | |
T* ptr; | |
}; | |
template <typename T> | |
class thread_safe | |
{ | |
class lock_helper_t : public T { | |
public: | |
template<typename ...Args> | |
lock_helper_t(Args&& ...args) | |
: T{ std::forward<Args>(args)... } | |
, locked(false) | |
{ | |
//printf("\nLockerHelper_T::lock_helper_t()"); | |
} | |
~lock_helper_t() { | |
//printf("\nLockerHelper_T::~lock_helper_t()"); | |
assert(!locked); | |
} | |
void lock() const { | |
std::cout << "lock_helper_t::lock()\n"; | |
guard.lock(); locked = true; | |
} | |
void unlock() const { | |
std::cout << "lock_helper_t::unlock()\n"; | |
assert(locked); | |
guard.unlock(); locked = false; | |
} | |
private: | |
mutable std::mutex guard; | |
mutable bool locked; | |
}; | |
public: | |
template<typename ...Args> | |
thread_safe(Args&& ...args) | |
: ptr{ new lock_helper_t{ std::forward<Args>(args)... } } | |
{ } | |
//typedef thread_safe_mediator<lock_helper_t> AccessType; | |
auto operator->() { | |
return thread_safe_mediator<lock_helper_t>(ptr); | |
} | |
auto operator->() const { | |
return thread_safe_mediator<lock_helper_t>(ptr); | |
} | |
private: | |
lock_helper_t* ptr; | |
}; | |
class DummyClass | |
{ | |
int val; | |
public: | |
DummyClass(int x) | |
: val{x} { } | |
void foo(int x) { | |
printf("\nDummyClass::foo()"); | |
val = x; | |
} | |
int bar() { | |
printf("\nDummyClass::bar()"); | |
return val; | |
} | |
}; | |
int main() | |
{ | |
thread_safe<DummyClass> tsafe{ 5 }; | |
tsafe->foo(12); | |
auto x = tsafe->bar(); | |
return x; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment