Created
August 29, 2017 10:46
-
-
Save zesterer/b6f7a685ee2a33e8fd4b28fe57e5d170 to your computer and use it in GitHub Desktop.
Thread-safe reference-based object bucket in C++
This file contains 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 <unordered_map> | |
namespace PtrTest | |
{ | |
/* Definitions */ | |
typedef long id_t; | |
const id_t ID_INVALID = -1; | |
template <typename T> | |
class table_t; | |
template <typename T, table_t<T>& CONTAINER> | |
class ptr_t; | |
template <typename T> | |
class table_t | |
{ | |
private: | |
struct _item_t | |
{ | |
T* _item = nullptr; | |
std::mutex _mut; | |
_item_t() {} | |
_item_t(T* item) : _item(item) {} | |
}; | |
std::unordered_map<id_t, _item_t> _items; | |
id_t _id_count; | |
std::mutex _mut; | |
id_t _new_id() { return ++this->_id_count; } | |
public: | |
T* get(id_t id) | |
{ | |
this->_mut.lock(); // Begin critical section | |
T* val = this->_items[id]._item; | |
this->_mut.unlock(); // End critical section | |
return val; | |
} | |
id_t add(T* item) | |
{ | |
this->_mut.lock(); // Begin critical section | |
id_t id = this->_new_id(); | |
this->_items.emplace(id, item); | |
this->_mut.unlock(); // End critical section | |
return id; | |
} | |
void lock(id_t id) | |
{ | |
this->_mut.lock(); // Begin critical section | |
this->_items[id]._mut.lock(); | |
this->_mut.unlock(); // End critical section | |
} | |
void unlock(id_t id) | |
{ | |
this->_mut.lock(); // Begin critical section | |
this->_items[id]._mut.unlock(); | |
this->_mut.unlock(); // End critical section | |
} | |
template <table_t<T>& table, typename ... Args> | |
ptr_t<T, table> create(Args ... args) | |
{ | |
return this->add(new T(args...)); | |
} | |
}; | |
template <typename T, table_t<T>& CONTAINER> | |
class ptr_t | |
{ | |
private: | |
id_t _id; | |
public: | |
class _proxy_t | |
{ | |
private: | |
id_t _id; | |
T* _item; | |
public: | |
_proxy_t(id_t id, T* item) : _id(id), _item(item) | |
{ | |
CONTAINER.lock(_id); | |
} | |
T* operator->() { return this->_item; } | |
~_proxy_t() | |
{ | |
CONTAINER.unlock(_id); | |
} | |
}; | |
ptr_t(id_t id) | |
{ | |
this->_id = id; | |
} | |
ptr_t(T* item) | |
{ | |
this->_id = CONTAINER.add(item); | |
} | |
void lock() | |
{ | |
CONTAINER.lock(this->_id); | |
} | |
void unlock() | |
{ | |
CONTAINER.unlock(this->_id); | |
} | |
_proxy_t operator->() { return _proxy_t(this->_id, CONTAINER.get(this->_id)); } | |
}; | |
} | |
/* Application */ | |
struct Number | |
{ | |
int num; | |
Number(int n) : num(n) {} | |
void say() { std::cout << "The number is " << this->num << std::endl; } | |
}; | |
PtrTest::table_t<Number> table; | |
int main() | |
{ | |
auto ptr1 = table.create<table>(7); | |
auto ptr2 = table.create<table>(1337); | |
auto ptr3 = table.create<table>(42); | |
auto ptr4 = ptr2; | |
ptr1->say(); | |
ptr2->say(); | |
ptr3->say(); | |
ptr4->say(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment