Created
February 4, 2016 22:05
-
-
Save Jiwan/31f8f837e4f4b90fed13 to your computer and use it in GitHub Desktop.
Final C++14 example for my post - An introduction to C++'s variadic templates: a thread-safe multi-type map
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 <memory> | |
#include <string> | |
#include "repository.hpp" | |
// Incomplete types used as compile-time keys. | |
struct Key1; | |
struct Key2; | |
// Create a type for our repository. | |
using MyRepository = Repository | |
< | |
Slot<std::string>, // One slot for std::string. | |
Slot<int, Key1>, // Two slots for int. | |
Slot<int, Key2> // Must be differentiate using "type keys" (Key1, Key2). | |
>; | |
int main() | |
{ | |
MyRepository myRepository; | |
myRepository.emplace<std::string>("test"); // Construct the shared_ptr within the repository. | |
myRepository.emplace<int, Key1>(1337); | |
myRepository.set<int, Key2>(std::make_shared<int>(42)); // Set the shared_ptr manually. | |
// Note: I use '*' as get returns a shared_ptr. | |
std::cout << *myRepository.get<std::string>() << std::endl; // Print "test". | |
std::cout << *myRepository.get<int, Key1>() << std::endl; // Print 1337. | |
std::cout << *myRepository.get<int, Key2>() << std::endl; // Print 42. | |
// std::cout << *myRepository.get<int>() << std::endl; | |
// ^^^ Compilation error: which int shall be selected? Key1 or Key2? | |
auto watcher = myRepository.getWatcher<std::string>(); // Create a watcher object to observe changes on std::string. | |
std::cout << watcher->hasBeenChanged() << std::endl; // 0: no changes since the watcher creation. | |
myRepository.emplace<std::string>("yo"); // Emplace a new value into the std::string slot. | |
std::cout << watcher->hasBeenChanged() << std::endl; // 1: the std::string slot has been changed. | |
std::cout << *watcher->get() << std::endl; // Poll the value and print "yo". | |
std::cout << watcher->hasBeenChanged() << std::endl; // 0: no changes since the last polling. | |
return EXIT_SUCCESS; | |
} |
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
// | |
// Created by jguegant on 1/30/16. | |
// | |
#ifndef REPOSITORY_HPP | |
#define REPOSITORY_HPP | |
#include <atomic> | |
#include <iostream> | |
#include <memory> | |
#include <mutex> | |
#include <vector> | |
struct DefaultSlotKey; | |
template <class Type, class Key = DefaultSlotKey> class Slot; | |
template <class Type, class Key> | |
class Watcher | |
{ | |
public: | |
Watcher(Slot<Type, Key>& slot): | |
slot_(slot), | |
hasBeenChanged_(false) | |
{ | |
} | |
Watcher(const Watcher&) = delete; | |
Watcher & operator=(const Watcher&) = delete; | |
bool hasBeenChanged() const | |
{ | |
return hasBeenChanged_; | |
} | |
void triggerChanges() | |
{ | |
hasBeenChanged_ = true; | |
} | |
auto get() -> decltype(std::declval<Slot<Type, Key>>().doGet()) | |
{ | |
hasBeenChanged_ = false; // Note: even if there is an update of the value between this line and the getValue one, | |
// we will still have the latest version. | |
// Note 2: atomic_bool automatically use a barrier and the two operations can't be inversed. | |
return slot_.doGet(); | |
} | |
private: | |
Slot<Type, Key>& slot_; | |
std::atomic_bool hasBeenChanged_; | |
}; | |
template <class Type, class Key = DefaultSlotKey> | |
using WatcherPtr = std::unique_ptr<Watcher<Type, Key>, | |
std::function<void(Watcher<Type, Key>*)>>; | |
template <class Type, class Key> | |
class Slot | |
{ | |
public: | |
using ThisType = Slot<Type, Key>; | |
using WatcherType = Watcher<Type, Key>; | |
using WatcherTypePtr = std::unique_ptr<WatcherType, std::function<void(WatcherType*)>> ; | |
public: | |
std::shared_ptr<Type> doGet() const | |
{ | |
return std::atomic_load(&value_); | |
} | |
void doSet(const std::shared_ptr<Type> &value) | |
{ | |
std::atomic_exchange(&value_, value); | |
signal(); | |
} | |
WatcherTypePtr doGetWatcher() | |
{ | |
WatcherTypePtr watcher(new WatcherType(*this), [this](WatcherType* toBeDelete) { | |
this->unregisterWatcher(toBeDelete);}); | |
registerWatcher(watcher.get()); | |
return watcher; | |
} | |
private: | |
void registerWatcher(WatcherType* newWatcher) | |
{ | |
std::lock_guard<std::mutex> l(watchers_mutex_); | |
watchers_.push_back(newWatcher); | |
} | |
void unregisterWatcher(WatcherType *toBeDelete) | |
{ | |
std::lock_guard<std::mutex> l(watchers_mutex_); | |
watchers_.erase(std::remove(watchers_.begin(), watchers_.end(), toBeDelete), watchers_.end()); | |
delete toBeDelete; | |
} | |
void signal() | |
{ | |
std::lock_guard<std::mutex> l(watchers_mutex_); | |
for (auto watcher : watchers_) { | |
watcher->triggerChanges(); | |
} | |
} | |
private: | |
std::vector<WatcherType*> watchers_; | |
std::shared_ptr<Type> value_; | |
std::mutex watchers_mutex_; | |
}; | |
template<class... Slots> | |
class Repository : private Slots... { | |
public: | |
template <class Type, class Key = DefaultSlotKey> | |
std::shared_ptr<Type> get() | |
{ | |
static_assert(std::is_base_of<Slot<Type, Key>, Repository<Slots...>>::value, | |
"Please ensure that this type or this key exists in this repository"); | |
return Slot<Type, Key>::doGet(); | |
} | |
template <class Type, class Key = DefaultSlotKey> | |
void set(const std::shared_ptr<Type>& value) | |
{ | |
static_assert(std::is_base_of<Slot<Type, Key>, Repository<Slots...>>::value, | |
"Please ensure that this type or this key exists in this repository"); | |
Slot<Type, Key>::doSet(value); | |
} | |
template <class Type, class Key = DefaultSlotKey, class ...Args> | |
void emplace(Args&&... args) | |
{ | |
static_assert(std::is_base_of<Slot<Type, Key>, Repository<Slots...>>::value, | |
"Please ensure that this type or this key exists in this repository"); | |
Slot<Type, Key>::doSet(std::make_shared<Type>(std::forward<Args>(args)...)); | |
} | |
template <class Type, class Key = DefaultSlotKey> | |
auto getWatcher() | |
{ | |
static_assert(std::is_base_of<Slot<Type, Key>, Repository<Slots...>>::value, | |
"Please ensure that this type or this key exists in this repository"); | |
return Slot<Type, Key>::doGetWatcher(); | |
} | |
}; | |
#endif // REPOSITORY_HPP | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment