I am trying to write an event-driven simulation in C++. Right now it's just a bare-bones priority queue of unique_ptrs to base Event class:
class Event
{
public:
double time;
Event(double time);
virtual void handle() = 0;
};
struct EventCompare
{
bool operator()(std::unique_ptr<Event> e1, std::unique_ptr<Event> e2) {
return e1->time > e2->time;
}
};
class DumpSimulationEvent : public Event
{
public:
DumpSimulationEvent(const double time);
void handle();
};
typedef std::priority_queue<std::unique_ptr<Event>, std::vector<std::unique_ptr<Event>>, EventCompare> EventQueue;
class Simulation
{
double time;
EventQueue eventQueue;
public:
Simulation();
void run();
};
Event::Event(const double t)
{
time = t;
}
DumpSimulationEvent::DumpSimulationEvent(const double t) : Event(t)
{
}
void DumpSimulationEvent::handle()
{
std::cout << "Event time: " << time;
}
Simulation::Simulation()
{
time = 0;
eventQueue = EventQueue();
std::unique_ptr<DumpSimulationEvent> dumpEvent5(new DumpSimulationEvent(5));
//eventQueue.emplace(dumpEvent5);
}
void Simulation::run()
{
while (!eventQueue.empty()) {
std::unique_ptr<Event> currentEvent = std::move(eventQueue.top());
//eventQueue.pop();
time += currentEvent->time;
currentEvent->handle();
}
}
Main function (not shown above) just creates an instance of Simulation and calls the run() method. Problem is that uncommenting either emplace() or pop() results in
error C2280: 'std::unique_ptr<Event,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)' : attempting to reference a deleted function c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility 521 1
Research indicates that most likely cause is an attempt to copy an unique_ptr. I am, however, at loss whether is it actual reason and does it actually happen at commented lines or just becomes visible there. Adding std::move to emplace argument doesn't seem to help.
Your problem is that you are not moving things correctly, but you are trying to make copies in several places.
Here is a diff that makes your code work, with some commentary:
struct EventCompare
{
- bool operator()(std::unique_ptr<Event> e1, std::unique_ptr<Event> e2) {
+ bool operator()(std::unique_ptr<Event> const &e1, std::unique_ptr<Event> const &e2) {
return e1->time > e2->time;
}
};
Here, as juanchopanza mentioned in his answer, you have to take std::unique_ptrs by reference, not by value, otherwise you are asking the compiler to make copies for you, which is not allowed.
time = 0;
eventQueue = EventQueue();
std::unique_ptr<DumpSimulationEvent> dumpEvent5(new DumpSimulationEvent(5));
- //eventQueue.emplace(dumpEvent5);
+ eventQueue.emplace(std::move(dumpEvent5));
}
In the above code, you have to MOVE your std::unique_ptr into the queue. Emplace doesn't magically move things, it just forwards arguments to the constructor. Without std::move here, you are asking to make a copy. You could have also just written: eventQueue.emplace(new DumpSimulationEvent(5)); and skipped the intermediate object.
while (!eventQueue.empty()) {
- std::unique_ptr<Event> currentEvent = std::move(eventQueue.top());
- //eventQueue.pop();
+ std::unique_ptr<Event> currentEvent(std::move(const_cast<std::unique_ptr<Event>&>(eventQueu
+ eventQueue.pop();
time += currentEvent->time;
currentEvent->handle();
Finally, in the above code, you are trying to move from eventQueue.top(), but you can't move from a const reference, which is what top() returns. If you want to force the move to work, you have to use both const_cast and std::move() as shown above.
Here is the complete modified code which compiles fine here with g++-4.8 -std=c++11:
#include <memory>
#include <queue>
#include <iostream>
class Event
{
public:
double time;
Event(double time);
virtual void handle() = 0;
};
struct EventCompare
{
bool operator()(std::unique_ptr<Event> const &e1, std::unique_ptr<Event> const &e2) {
return e1->time > e2->time;
}
};
class DumpSimulationEvent : public Event
{
public:
DumpSimulationEvent(const double time);
void handle();
};
typedef std::priority_queue<std::unique_ptr<Event>, std::vector<std::unique_ptr<Event>>, EventCompare> EventQueue;
class Simulation
{
double time;
EventQueue eventQueue;
public:
Simulation();
void run();
};
Event::Event(const double t)
{
time = t;
}
DumpSimulationEvent::DumpSimulationEvent(const double t) : Event(t)
{
}
void DumpSimulationEvent::handle()
{
std::cout << "Event time: " << time;
}
Simulation::Simulation()
{
time = 0;
eventQueue = EventQueue();
std::unique_ptr<DumpSimulationEvent> dumpEvent5(new DumpSimulationEvent(5));
eventQueue.emplace(std::move(dumpEvent5));
}
void Simulation::run()
{
while (!eventQueue.empty()) {
std::unique_ptr<Event> currentEvent(std::move(const_cast<std::unique_ptr<Event>&>(eventQueue.top())));
eventQueue.pop();
time += currentEvent->time;
currentEvent->handle();
}
}
Related
I did some profiling using this code
#include "Timer.h"
#include <iostream>
enum class BackendAPI {
B_API_NONE,
B_API_VULKAN,
B_API_DIRECTX_12,
B_API_WEB_GPU,
};
namespace Functional
{
typedef void* VertexBufferHandle;
namespace Vulkan
{
struct VulkanVertexBuffer {};
VertexBufferHandle CreateVertexBuffer(size_t size)
{
return nullptr;
}
__forceinline void Hello() {}
__forceinline void Bello() {}
__forceinline void Mello() {}
}
class RenderBackend {
public:
RenderBackend() {}
~RenderBackend() {}
void SetupBackendMethods(BackendAPI api)
{
switch (api)
{
case BackendAPI::B_API_VULKAN:
{
CreateVertexBuffer = Vulkan::CreateVertexBuffer;
Hello = Vulkan::Hello;
Bello = Vulkan::Bello;
Mello = Vulkan::Mello;
}
break;
case BackendAPI::B_API_DIRECTX_12:
break;
case BackendAPI::B_API_WEB_GPU:
break;
default:
break;
}
}
VertexBufferHandle(*CreateVertexBuffer)(size_t size) = nullptr;
void (*Hello)() = nullptr;
void (*Bello)() = nullptr;
void (*Mello)() = nullptr;
};
}
namespace ObjectOriented
{
struct VertexBuffer {};
class RenderBackend {
public:
RenderBackend() {}
virtual ~RenderBackend() {}
virtual VertexBuffer* CreateVertexBuffer(size_t size) = 0;
virtual void Hello() = 0;
virtual void Bello() = 0;
virtual void Mello() = 0;
};
class VulkanBackend final : public RenderBackend {
struct VulkanVertexBuffer : public VertexBuffer {};
public:
VulkanBackend() {}
~VulkanBackend() {}
__forceinline virtual VertexBuffer* CreateVertexBuffer(size_t size) override
{
return nullptr;
}
__forceinline virtual void Hello() override {}
__forceinline virtual void Bello() override {}
__forceinline virtual void Mello() override {}
};
RenderBackend* CreateBackend(BackendAPI api)
{
switch (api)
{
case BackendAPI::B_API_VULKAN:
return new VulkanBackend;
break;
case BackendAPI::B_API_DIRECTX_12:
break;
case BackendAPI::B_API_WEB_GPU:
break;
default:
break;
}
return nullptr;
}
}
int main()
{
constexpr int maxItr = 1000000;
for (int i = 0; i < 100; i++)
{
int counter = maxItr;
Timer t;
auto pBackend = ObjectOriented::CreateBackend(BackendAPI::B_API_VULKAN);
while (counter--)
{
pBackend->Hello();
pBackend->Bello();
pBackend->Mello();
auto pRef = pBackend->CreateVertexBuffer(100);
}
delete pBackend;
}
std::cout << "\n";
for (int i = 0; i < 100; i++)
{
int counter = maxItr;
Timer t;
{
Functional::RenderBackend backend;
backend.SetupBackendMethods(BackendAPI::B_API_VULKAN);
while (counter--)
{
backend.Hello();
backend.Bello();
backend.Mello();
auto pRef = backend.CreateVertexBuffer(100);
}
}
}
}
In which `#include "Timer.h" is
#pragma once
#include <chrono>
/**
* Timer class.
* This calculates the total time taken from creation till the termination of the object.
*/
class Timer {
public:
/**
* Default contructor.
*/
Timer()
{
// Set the time point at the creation of the object.
startPoint = std::chrono::high_resolution_clock::now();
}
/**
* Default destructor.
*/
~Timer()
{
// Get the time point of the time of the object's termination.
auto endPoint = std::chrono::high_resolution_clock::now();
// Convert time points.
long long start = std::chrono::time_point_cast<std::chrono::microseconds>(startPoint).time_since_epoch().count();
long long end = std::chrono::time_point_cast<std::chrono::microseconds>(endPoint).time_since_epoch().count();
// Print the time to the console.
printf("Time taken: %15I64d\n", static_cast<__int64>(end - start));
}
private:
std::chrono::time_point<std::chrono::high_resolution_clock> startPoint; // The start time point.
};
And after the output in a graph (compiled using the Release configuration in Visual Studio 2019), the results are as follows,
Note: The above code is made to profile Functional vs Object oriented approach performance differences when building a large scale library. The profiling is done by running the application 5 times, recompiling the source code. Each run has 100 iterations. The tests are done both ways (object oriented first, functional second and vise versa) but the performance results are more or less the same.
I am aware that inheritance is somewhat slow because it has to resolve the function pointers from the V-Table at runtime. But the part which I don't understand is, if I'm correct, function pointers are also resolved at runtime. Which means that the program needs to fetch the function code prior to executing it.
So my questions are,
Why does the function pointers perform somewhat better than virtual methods?
Why does the virtual methods have performance drops at some points but the function pointers are somewhat stable?
Thank You!
Virtual method lookup tables need to be accessed (basically) every time the method is called. It adds another indirection to every call.
When you initialize a backend and then save the function pointers you essentially take out this extra indirection and pre-compute it once at the start.
It is thus not a surprise to see a small performance benefit from direct function pointers.
I'm working on a call center call queue simulation model. I've created a vector of caller objects and assigned them exponential distributed random inter-arrival times, then assigned calculated arrival times in the call center class. I would like to then copy each caller object into a vector priority queue, however I keep receiving this error when i push a caller vector object into the queue:
Error C2280 'Caller::Caller(const Caller &)': attempting to reference a deleted function
I've been trying to fix it for a while now and cannot seem to figure out what is causing the issue or how to fix it. I'm trying to push an already created object, so I'm not sure why I would get a reference to a deleted function. Any help would be appreciated.
My Caller.h file
#pragma once
#include <random>
#include <time.h>
using namespace std;
class Caller
{
private:
bool isPaid;
int priority;
double arrivalTime;
double iarrivalTime;
default_random_engine e1;
random_device rd1;
public:
Caller();
Caller(bool p);
void setPriority();
int getPriority();
void generateInterArrivalTime();
double getInterArrivalTime();
void setArrivalTime(double t);
double getArrivalTime();
};
My Caller.Cpp file
#include "Caller.h"
Caller::Caller() : isPaid(false), priority(0), iarrivalTime(0), arrivalTime(0)
{
}
Caller::Caller(bool p): isPaid(false), priority(0), iarrivalTime(0)
{
isPaid = p;
}
void Caller::setPriority()
{
if (isPaid == true)
{
priority = 1;
}
else(priority = 0);
}
int Caller::getPriority()
{
return priority;
}
void Caller::generateInterArrivalTime()
{
e1.seed(rd1());
exponential_distribution<>callNums(25);
iarrivalTime = callNums(e1);
}
double Caller::getInterArrivalTime()
{
return iarrivalTime;
}
void Caller::setArrivalTime(double t)
{
arrivalTime = t;
}
double Caller::getArrivalTime()
{
return arrivalTime;
}
My CallCenter.h file
class CallCenter
{
private:
vector<Caller> callers;
priority_queue<Caller, vector<Caller>, CompareFunction > callQ;
public:
CallCenter();
void queueCalls();
void assignArrivalTime();
My CallCenter.Cpp file
CallCenter::CallCenter(): callers(10)
{
}
void CallCenter::assignArrivalTime()
{
for (int i = 0; i < callers.size(); i++)
{
callers[i].generateInterArrivalTime();
if (i==0)
{
callers[i].setArrivalTime(callers[i].getInterArrivalTime());
}
else {callers[i].setArrivalTime(callers[i - 1].getArrivalTime() + callers[i].getInterArrivalTime());}
cout << callers[i].getInterArrivalTime() << "\t" << callers[i].getArrivalTime() << endl;
}
}
void CallCenter::queueCalls()
{
for (int i = 0; i < callers.size(); i++)
{
callQ.push(callers[i]);
}
}
My CompareFunction.h file
#pragma once
#include "Caller.h"
class CompareFunction
{
public: bool operator()(Caller& lowp, Caller& highp)
{
return lowp.getArrivalTime() > highp.getArrivalTime();
}
};
random_device rd1;
Your class has a std::random_device as a class member.
std::random_device's copy constructor is deleted:
The copy constructor is deleted: std::random_device is not copyable
nor movable.
This makes this class, which contains this class member, also have a deleted copy constructor.
After all, if a class member cannot be copied, by default, then the class itself can't be copied by default either.
priority_queue<Caller, vector<Caller>, CompareFunction > callQ;
Your priority queue is based on a std::vector.
callQ.push(callers[i]);
std::vectors cannot be used with non-copyable/movable classes. You can only use std::vector with classes that can be copied or moved.
You will have to change your class design, in some form or fashion. The simplest change would be a priority queue of std::unique_ptrs or std::shared_ptrs to your Callers, which you will need to construct in dynamic scope (you will also have to provide a custom comparator class for std::priority_queue, so it knows how to prioritize the smart pointers correctly, this is a little bit of extra work but it's not too complicated once you have a complete grasp on all the moving pieces).
With the changes made in C++11 (such as the inclusion of std::bind), is there a recommended way to implement a simple single-threaded observer pattern without dependence on anything external to the core language or standard library (like boost::signal)?
EDIT
If someone could post some code showing how dependence on boost::signal could be reduced using new language features, that would still be very useful.
I think that bind makes it easier to create slots (cfr. the 'preferred' syntax vs. the 'portable' syntax - that's all going away). The observer management, however, is not becoming less complex.
But as #R. Martinho Fernandes mentions: an std::vector<std::function< r(a1) > > is now easily created without the hassle for an (artificial) 'pure virtual' interface class.
Upon request: an idea on connection management - probably full of bugs, but you'll get the idea:
// note that the Func parameter is something
// like std::function< void(int,int) > or whatever, greatly simplified
// by the C++11 standard
template<typename Func>
struct signal {
typedef int Key; //
Key nextKey;
std::map<Key,Func> connections;
// note that connection management is the same in C++03 or C++11
// (until a better idea arises)
template<typename FuncLike>
Key connect( FuncLike f ) {
Key k=nextKey++;
connections[k]=f;
return k;
}
void disconnect(Key k){
connections.erase(k);
}
// note: variadic template syntax to be reviewed
// (not the main focus of this post)
template<typename Args...>
typename Func::return_value call(Args... args){
// supposing no subcription changes within call:
for(auto &connection: connections){
(*connection.second)(std::forward(...args));
}
}
};
Usage:
signal<function<void(int,int)>> xychanged;
void dump(int x, int y) { cout << x << ", " << y << endl; }
struct XY { int x, y; } xy;
auto dumpkey=xychanged.connect(dump);
auto lambdakey=xychanged.connect([&xy](int x, int y){ xy.x=x; xy.y=y; });
xychanged.call(1,2);
Since you're asking for code, my blog entry Performance of a C++11 Signal System contains a single-file implementation of a fully functional signal system based on C++11 features without further dependencies (albeit single-threaded, which was a performance requirement).
Here is a brief usage example:
Signal<void (std::string, int)> sig2;
sig2() += [] (std::string msg, int d) { /* handler logic */ };
sig2.emit ("string arg", 17);
More examples can be found in this unit test.
I wrote my own light weight Signal/Slot classes which return connection handles. The existing answer's key system is pretty fragile in the face of exceptions. You have to be exceptionally careful about deleting things with an explicit call. I much prefer using RAII for open/close pairs.
One notable lack of support in my library is the ability to get a return value from your calls. I believe boost::signal has methods of calculating the aggregate return values. In practice usually you don't need this and I just find it cluttering, but I may come up with such a return method for fun as an exercise in the future.
One cool thing about my classes is the Slot and SlotRegister classes. SlotRegister provides a public interface which you can safely link to a private Slot. This protects against external objects calling your observer methods. It's simple, but nice encapsulation.
I do not believe my code is thread safe, however.
//"MIT License + do not delete this comment" - M2tM : http://michaelhamilton.com
#ifndef __MV_SIGNAL_H__
#define __MV_SIGNAL_H__
#include <memory>
#include <utility>
#include <functional>
#include <vector>
#include <set>
#include "Utility/scopeGuard.hpp"
namespace MV {
template <typename T>
class Signal {
public:
typedef std::function<T> FunctionType;
typedef std::shared_ptr<Signal<T>> SharedType;
static std::shared_ptr< Signal<T> > make(std::function<T> a_callback){
return std::shared_ptr< Signal<T> >(new Signal<T>(a_callback, ++uniqueId));
}
template <class ...Arg>
void notify(Arg... a_parameters){
if(!isBlocked){
callback(std::forward<Arg>(a_parameters)...);
}
}
template <class ...Arg>
void operator()(Arg... a_parameters){
if(!isBlocked){
callback(std::forward<Arg>(a_parameters)...);
}
}
void block(){
isBlocked = true;
}
void unblock(){
isBlocked = false;
}
bool blocked() const{
return isBlocked;
}
//For sorting and comparison (removal/avoiding duplicates)
bool operator<(const Signal<T>& a_rhs){
return id < a_rhs.id;
}
bool operator>(const Signal<T>& a_rhs){
return id > a_rhs.id;
}
bool operator==(const Signal<T>& a_rhs){
return id == a_rhs.id;
}
bool operator!=(const Signal<T>& a_rhs){
return id != a_rhs.id;
}
private:
Signal(std::function<T> a_callback, long long a_id):
id(a_id),
callback(a_callback),
isBlocked(false){
}
bool isBlocked;
std::function< T > callback;
long long id;
static long long uniqueId;
};
template <typename T>
long long Signal<T>::uniqueId = 0;
template <typename T>
class Slot {
public:
typedef std::function<T> FunctionType;
typedef Signal<T> SignalType;
typedef std::shared_ptr<Signal<T>> SharedSignalType;
//No protection against duplicates.
std::shared_ptr<Signal<T>> connect(std::function<T> a_callback){
if(observerLimit == std::numeric_limits<size_t>::max() || cullDeadObservers() < observerLimit){
auto signal = Signal<T>::make(a_callback);
observers.insert(signal);
return signal;
} else{
return nullptr;
}
}
//Duplicate Signals will not be added. If std::function ever becomes comparable this can all be much safer.
bool connect(std::shared_ptr<Signal<T>> a_value){
if(observerLimit == std::numeric_limits<size_t>::max() || cullDeadObservers() < observerLimit){
observers.insert(a_value);
return true;
}else{
return false;
}
}
void disconnect(std::shared_ptr<Signal<T>> a_value){
if(!inCall){
observers.erase(a_value);
} else{
disconnectQueue.push_back(a_value);
}
}
template <typename ...Arg>
void operator()(Arg... a_parameters){
inCall = true;
SCOPE_EXIT{
inCall = false;
for(auto& i : disconnectQueue){
observers.erase(i);
}
disconnectQueue.clear();
};
for (auto i = observers.begin(); i != observers.end();) {
if (i->expired()) {
observers.erase(i++);
} else {
auto next = i;
++next;
i->lock()->notify(std::forward<Arg>(a_parameters)...);
i = next;
}
}
}
void setObserverLimit(size_t a_newLimit){
observerLimit = a_newLimit;
}
void clearObserverLimit(){
observerLimit = std::numeric_limits<size_t>::max();
}
int getObserverLimit(){
return observerLimit;
}
size_t cullDeadObservers(){
for(auto i = observers.begin(); i != observers.end();) {
if(i->expired()) {
observers.erase(i++);
}
}
return observers.size();
}
private:
std::set< std::weak_ptr< Signal<T> >, std::owner_less<std::weak_ptr<Signal<T>>> > observers;
size_t observerLimit = std::numeric_limits<size_t>::max();
bool inCall = false;
std::vector< std::shared_ptr<Signal<T>> > disconnectQueue;
};
//Can be used as a public SlotRegister member for connecting slots to a private Slot member.
//In this way you won't have to write forwarding connect/disconnect boilerplate for your classes.
template <typename T>
class SlotRegister {
public:
typedef std::function<T> FunctionType;
typedef Signal<T> SignalType;
typedef std::shared_ptr<Signal<T>> SharedSignalType;
SlotRegister(Slot<T> &a_slot) :
slot(a_slot){
}
//no protection against duplicates
std::shared_ptr<Signal<T>> connect(std::function<T> a_callback){
return slot.connect(a_callback);
}
//duplicate shared_ptr's will not be added
bool connect(std::shared_ptr<Signal<T>> a_value){
return slot.connect(a_value);
}
void disconnect(std::shared_ptr<Signal<T>> a_value){
slot.disconnect(a_value);
}
private:
Slot<T> &slot;
};
}
#endif
Supplimental scopeGuard.hpp:
#ifndef _MV_SCOPEGUARD_H_
#define _MV_SCOPEGUARD_H_
//Lifted from Alexandrescu's ScopeGuard11 talk.
namespace MV {
template <typename Fun>
class ScopeGuard {
Fun f_;
bool active_;
public:
ScopeGuard(Fun f)
: f_(std::move(f))
, active_(true) {
}
~ScopeGuard() { if(active_) f_(); }
void dismiss() { active_ = false; }
ScopeGuard() = delete;
ScopeGuard(const ScopeGuard&) = delete;
ScopeGuard& operator=(const ScopeGuard&) = delete;
ScopeGuard(ScopeGuard&& rhs)
: f_(std::move(rhs.f_))
, active_(rhs.active_) {
rhs.dismiss();
}
};
template<typename Fun>
ScopeGuard<Fun> scopeGuard(Fun f){
return ScopeGuard<Fun>(std::move(f));
}
namespace ScopeMacroSupport {
enum class ScopeGuardOnExit {};
template <typename Fun>
MV::ScopeGuard<Fun> operator+(ScopeGuardOnExit, Fun&& fn) {
return MV::ScopeGuard<Fun>(std::forward<Fun>(fn));
}
}
#define SCOPE_EXIT \
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
= MV::ScopeMacroSupport::ScopeGuardOnExit() + [&]()
#define CONCATENATE_IMPL(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
#ifdef __COUNTER__
#define ANONYMOUS_VARIABLE(str) \
CONCATENATE(str, __COUNTER__)
#else
#define ANONYMOUS_VARIABLE(str) \
CONCATENATE(str, __LINE__)
#endif
}
#endif
An example application making use of my library:
#include <iostream>
#include <string>
#include "signal.hpp"
class Observed {
private:
//Note: This is private to ensure not just anyone can spawn a signal
MV::Slot<void (int)> onChangeSlot;
public:
typedef MV::Slot<void (int)>::SharedSignalType ChangeEventSignal;
//SlotRegister is public, users can hook up signals to onChange with this value.
MV::SlotRegister<void (int)> onChange;
Observed():
onChange(onChangeSlot){ //Here is where the binding occurs
}
void change(int newValue){
onChangeSlot(newValue);
}
};
class Observer{
public:
Observer(std::string a_name, Observed &a_observed){
connection = a_observed.onChange.connect([=](int value){
std::cout << a_name << " caught changed value: " << value << std::endl;
});
}
private:
Observed::ChangeEventSignal connection;
};
int main(){
Observed observed;
Observer observer1("o[1]", observed);
{
Observer observer2("o[2]", observed);
observed.change(1);
}
observed.change(2);
}
Output of the above would be:
o[1] caught changed value: 1
o[2] caught changed value: 1
o[1] caught changed value: 2
As you can see, the slot disconnects dead signals automatically.
Here's what I came up with.
This assumes no need to aggregate results from the listeners of a broadcast signal.
Also, the "slot" or Signal::Listener is the owner of the callback.
This ought to live with the object that your (I'm guessing...) lambda is probably capturing so that when that object goes out of scope, so does the callback, which prevents it from being called anymore.
You could use methods described in other answers as well to store the Listener owner objects in a way you can lookup.
template <typename... FuncArgs>
class Signal
{
using fp = std::function<void(FuncArgs...)>;
std::forward_list<std::weak_ptr<fp> > registeredListeners;
public:
using Listener = std::shared_ptr<fp>;
Listener add(const std::function<void(FuncArgs...)> &cb) {
// passing by address, until copy is made in the Listener as owner.
Listener result(std::make_shared<fp>(cb));
registeredListeners.push_front(result);
return result;
}
void raise(FuncArgs... args) {
registeredListeners.remove_if([&args...](std::weak_ptr<fp> e) -> bool {
if (auto f = e.lock()) {
(*f)(args...);
return false;
}
return true;
});
}
};
usage
Signal<int> bloopChanged;
// ...
Signal<int>::Listener bloopResponse = bloopChanged.add([](int i) { ... });
// or
decltype(bloopChanged)::Listener bloopResponse = ...
// let bloopResponse go out of scope.
// or re-assign it
// or reset the shared_ptr to disconnect it
bloopResponse.reset();
I have made a gist for this too, with a more in-depth example:
https://gist.github.com/johnb003/dbc4a69af8ea8f4771666ce2e383047d
I have had a go at this myself also. My efforts can be found at this gist, which will continue to evolve . . .
https://gist.github.com/4172757
I use a different style, more similar to the change notifications in JUCE than BOOST signals. Connection management is done using some lambda syntax that does some capture by copy. It is working well so far.
Below I have attempted to write a sudo code for the Observer pattern when observers wish to observe different items.
Ignore the syntax errors. I wish to know if this is the correct way to implement this. If not, please suggest better ways.
// Used by the subject for keeping a track of what items the observer wants to observe
typedef struct observerListStruct
{
bool getTemperatureUpdate;
bool getHumidityUpdate;
bool getPressureUpdate;
observer's-function pointer's address;
};
// Subject's class
class weatherData
{
public:
// Observers will call this function to register themselves. The function pointer will point to the function which will get called when updates are available.
void registerObservers (observer obj, observer's-FunctionPointer)
{
// This observer's function returns which items to observe.
char* f = obj.returnItemsToObserve ();
if f[0] = `1`
observerListStruct.getTemperatureUpdate = true;
}
void unregisterObservers (observer obj) {}
private:
vector <observerListStruct> observerList;
float temperature;
float humidity;
float pressure;
void notifyObservers () {}
float getTemperature () {}
float getHumidity () {}
float getPressure () {}
} weatherDataObject;
// Base class for observers containing common functions
class observers
{
char ItemsToObserve [3] = {1, 2, 3};
// This observer's function returns which items to observe. Default - return all items
virtual char* returnItemsToObserve ()
{
return ItemsToObserve;
}
};
class observerDisplayElementCurrentConditions : public observers
{
char ItemsToObserve [3] = {1, 2};
char* returnItemsToObserve ()
{
return ItemsToObserve;
}
// this function will be used as a function pointer for getting updates
void getUpdatesAndDisplayWeatherData (float, float) {}
};
A more pattern oriented solution (but without function pointers) could be the following. You could parametrize the WeatherObserver-Class to get only the values, you want.
#include <list>
#include <iostream>
class Observable; //forward declaration
//Base class for all observers
class Observer {
friend class Observable; //allow access to observedSubject
protected:
Observable *observedSubject;
public:
virtual void update(){};
};
//Base class for all observables
class Observable {
private:
std::list<Observer * const> m_registeredObservers;
public:
~Observable()
{
//delete the observers
std::list<Observer * const>::iterator it = m_registeredObservers.begin();
while (it != m_registeredObservers.end())
{
delete *it;
it = m_registeredObservers.erase(it);
}
}
void addObserver(Observer * const _pObserver)
{
_pObserver->observedSubject = this;
m_registeredObservers.push_back(_pObserver);
}
void removeObserver(Observer * const _pObserver)
{
m_registeredObservers.remove(_pObserver);
delete _pObserver;
}
void notifyObservers()
{
std::list<Observer * const>::iterator it = m_registeredObservers.begin();
while (it != m_registeredObservers.end())
{
(*it)->update();
it++;
}
}
};
//Concrete Observable
class WeatherData : public Observable {
private:
float temperature;
float humidity;
float pressure;
public:
WeatherData(): temperature(0), humidity(0), pressure(0)
{};
float getTemperature () const
{
return temperature;
}
float getHumidity () const
{
return humidity;
}
float getPressure () const
{
return pressure;
}
void setTemperature(float _temperature)
{
if (temperature != _temperature)
{
temperature = _temperature;
notifyObservers();
}
}
void setHumidity(float _humidity)
{
if (humidity != _humidity)
{
humidity = _humidity;
notifyObservers();
}
}
void setPressure(float _pressure)
{
if (pressure != _pressure)
{
pressure = _pressure;
notifyObservers();
}
}
};
//Concrete implementation of an weather observer
class WeatherObserver : public Observer
{
public:
WeatherObserver():Observer(){};
void update()
{
WeatherData* pWeatherPtr = static_cast<WeatherData*>(observedSubject);
if (pWeatherPtr != 0)
{
float actHumidity = pWeatherPtr->getHumidity();
float actPressure = pWeatherPtr->getPressure();
float actTemperature = pWeatherPtr->getTemperature();
//do something with the data
std::cout << "WeatherObserver update" << std::endl;
std::cout << "Temperature : " << actTemperature << std::endl;
std::cout << "Humidity : " << actHumidity << std::endl;
std::cout << "Pressure : " << actPressure << std::endl;
}
}
};
int main()
{
WeatherData weatherData;
Observer * pObserver = new WeatherObserver();
weatherData.addObserver(pObserver);
weatherData.setHumidity(100);
weatherData.setTemperature(100);
}
#include <algorithm>
#include <vector>
class WeatherFlags
{
public:
WeatherFlags()
: mask_(0)
{}
union {
struct {
unsigned int temperature_ : 1;
unsigned int humidity_ : 1;
unsigned int pressure_ : 1;
};
unsigned int mask_;
};
};
class WeatherData;
class WeatherEvent
{
public:
WeatherEvent(WeatherData* data, WeatherFlags const& flags)
: data_(data)
, flags_(flags)
{}
double getTemperature() const;
WeatherData* data_;
WeatherFlags flags_;
};
class WeatherListener
{
public:
virtual ~WeatherListener() = 0;
virtual void onWeatherUpdate(WeatherEvent& e) = 0;
};
inline WeatherListener::~WeatherListener() {}
class WeatherListenerEntry
{
public:
WeatherListenerEntry()
: listener_(0)
{}
WeatherListenerEntry(WeatherListener* listener, WeatherFlags const& flags)
: listener_(listener)
, flags_(flags)
{}
WeatherListener* listener_;
WeatherFlags flags_;
};
class WeatherData
{
public:
WeatherData();
void addListener(WeatherListener* listener, WeatherFlags const& flags);
void removeListener(WeatherListener* listener);
void notify(WeatherFlags const& flags);
double getTemperature() const { return temperature_; }
private:
typedef std::vector<WeatherListenerEntry> Listeners;
Listeners listeners_;
double temperature_;
};
WeatherData::WeatherData()
: temperature_(0)
{}
void WeatherData::addListener(WeatherListener* listener, WeatherFlags const& flags)
{
// TODO Could maybe check for the addition of duplicates here...
listeners_.push_back(WeatherListenerEntry(listener, flags));
}
void WeatherData::removeListener(WeatherListener* listener)
{
struct ListenerEquals {
WeatherListener* listener_;
ListenerEquals(WeatherListener* listener)
: listener_(listener)
{}
bool operator()(WeatherListenerEntry const& e) const {
return (e.listener_ == listener_);
}
};
listeners_.erase(
std::remove_if(listeners_.begin(), listeners_.end(), ListenerEquals(listener)),
listeners_.end());
}
void WeatherData::notify(WeatherFlags const& flags)
{
WeatherEvent evt(this, flags);
for (Listeners::iterator i = listeners_.begin(); i != listeners_.end(); ++i)
{
if (0 != (i->flags_.mask_ & flags.mask_)) {
i->listener_->onWeatherUpdate(evt);
}
}
}
double
WeatherEvent::getTemperature() const
{
return data_->getTemperature();
}
#include <iostream>
class WeatherObserverStdout : public WeatherListener
{
public:
void observe(WeatherData& data) {
WeatherFlags flags;
flags.temperature_ = true; // interested in temperature only.
data.addListener(this, flags);
}
virtual void onWeatherUpdate(WeatherEvent& e);
};
void
WeatherObserverStdout::onWeatherUpdate(WeatherEvent& e)
{
double temp = e.getTemperature();
std::cout << "Temperatrure: " << temp << std::endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
WeatherData wdata;
WeatherObserverStdout obs;
obs.observe(wdata);
WeatherFlags flags;
wdata.notify(flags);
flags.temperature_ = true;
wdata.notify(flags);
return 0;
}
I think it is easier, and more scalable, to define a set of event types that each observer can listen to. Then you register the observer to listen to that particular event type. The observed then keeps a list of observers registered for each event, and notifies them if and when the event occurs. Using a combination of std::function, std::bind (or boost equivalents), it is easy to register callbacks for a given event type. You could put the callbacks in a map of event type to callback.
For example, something along these lines (almost pseudo-code, has not been tested)
class Publisher {
public :
void subscribe(const std::string& event,
std::function<void(double)> callback) {
m_subscribers[s].push_back(callback);
}
void publish(const std::string& event) const {
for (auto& f : m_subscribers[event]) f( some double );}
void event(const std::string& event) const { publish(event);}
private:
// map of event types (here simply strings) to list of callbacks
std::map<std::string&,
std::list<std::function<void(const std::string&)>>> m_subscribers;
};
struct Foo {
void foo(double x) {
std::cout << "Foo received message: " << x << "\n";
}
};
struct Bar {
void bar(double x) {
std::cout << "Bar received message: " << x << "\n";
}
};
int main() {
Publisher pub;
Foo f0;
Foo f1;
Bar bar0;
pub.subscribe("RED", std::bind(&Foo::foo, &foo0, _1));
pub.subscribe("GREEN", std::bind(&Foo::foo, &foo1, _1));
pub.subscribe("WHITE", std::bind(&Foo::foo, &foo1, _1));
pub.subscribe("RED", std::bind(&Bar::bar, &bar0, _1));
pub.subscribe("BLUE", std::bind(&Bar::bar, &bar0, _1));
pub.subscribe("MAGENTA", std::bind(&Bar::bar, &bar0, _1));
// trigger a "GREEN" event
pub.event("GREEN");
}
Here, the observers (or subscribers) register to some events, represented by strings here, and their registered callbacks get called when this event happens. In the example above I manually trigger an event to illustrate the mechanism.
This event-callback mechanism allows to decouple the actual items from the callback action. The Observed (or publisher) knows what parameter to pass the callback for a given event, and which callbacks to call, so the observers are not dependent on the internal data of the observed object.
I write a lot of C++ code and needed to create an Observer for some game components I was working on. I needed something to distribute "start of frame", "user input", etc., as events in the game to interested parties.
I also wanted more granularity in the events that could be handled. I have a lot of little things that go off...I don't need to have the parts that are interested in resetting for the next frame worried about a change in the user input.
I also wanted it to be straight C++, not dependent on the platform or a specific technology (such as boost, Qt, etc.) because I often build and re-use components (and the ideas behind them) across different projects.
Here is the rough sketch of what I came up with as a solution:
The Observer is a singleton with keys (enumerated values, not strings; this is a speed tradeoff since the keys are not searched hashed, but it means no easy "string" names and you have to define them ahead of time) for Subjects to register interest in. Because it is a singleton, it always exists.
Each subject is derived from a common base class. The base class has an abstract virtual function Notify(...) which must be implemented in derived classes, and a destructor that removes it from the Observer (which it can always reach) when it is deleted.
Inside the Observer itself, if Detach(...) is called while a Notify(...) is in progress, any detached Subjects end up on a list.
When Notify(...) is called on the Observer, it creates a temporary copy of the Subject list. As it iterates over it, it compare it to the recently detached. If the target is not on it, Notify(...) is called on the target. Otherwise, it is skipped.
Notify(...) in the Observer also keeps track of the depth to handle cascading calls (A notifies B, C, D, and the D.Notify(...) triggers a Notify(...) call to E, etc.)
This is what the interface ended up looking like:
/*
The Notifier is a singleton implementation of the Subject/Observer design
pattern. Any class/instance which wishes to participate as an observer
of an event can derive from the Notified base class and register itself
with the Notiifer for enumerated events.
Notifier derived classes MUST implement the notify function, which has
a prototype of:
void Notify(const NOTIFIED_EVENT_TYPE_T& event)
This is a data object passed from the Notifier class. The structure
passed has a void* in it. There is no illusion of type safety here
and it is the responsibility of the user to ensure it is cast properly.
In most cases, it will be "NULL".
Classes derived from Notified do not need to deregister (though it may
be a good idea to do so) as the base class destructor will attempt to
remove itself from the Notifier system automatically.
The event type is an enumeration and not a string as it is in many
"generic" notification systems. In practical use, this is for a closed
application where the messages will be known at compile time. This allows
us to increase the speed of the delivery by NOT having a
dictionary keyed lookup mechanism. Some loss of generality is implied
by this.
This class/system is NOT thread safe, but could be made so with some
mutex wrappers. It is safe to call Attach/Detach as a consequence
of calling Notify(...).
*/
class Notified;
class Notifier : public SingletonDynamic<Notifier>
{
public:
typedef enum
{
NE_MIN = 0,
NE_DEBUG_BUTTON_PRESSED = NE_MIN,
NE_DEBUG_LINE_DRAW_ADD_LINE_PIXELS,
NE_DEBUG_TOGGLE_VISIBILITY,
NE_DEBUG_MESSAGE,
NE_RESET_DRAW_CYCLE,
NE_VIEWPORT_CHANGED,
NE_MAX,
} NOTIFIED_EVENT_TYPE_T;
private:
typedef vector<NOTIFIED_EVENT_TYPE_T> NOTIFIED_EVENT_TYPE_VECTOR_T;
typedef map<Notified*,NOTIFIED_EVENT_TYPE_VECTOR_T> NOTIFIED_MAP_T;
typedef map<Notified*,NOTIFIED_EVENT_TYPE_VECTOR_T>::iterator NOTIFIED_MAP_ITER_T;
typedef vector<Notified*> NOTIFIED_VECTOR_T;
typedef vector<NOTIFIED_VECTOR_T> NOTIFIED_VECTOR_VECTOR_T;
NOTIFIED_MAP_T _notifiedMap;
NOTIFIED_VECTOR_VECTOR_T _notifiedVector;
NOTIFIED_MAP_ITER_T _mapIter;
// This vector keeps a temporary list of observers that have completely
// detached since the current "Notify(...)" operation began. This is
// to handle the problem where a Notified instance has called Detach(...)
// because of a Notify(...) call. The removed instance could be a dead
// pointer, so don't try to talk to it.
vector<Notified*> _detached;
int32 _notifyDepth;
void RemoveEvent(NOTIFIED_EVENT_TYPE_VECTOR_T& orgEventTypes, NOTIFIED_EVENT_TYPE_T eventType);
void RemoveNotified(NOTIFIED_VECTOR_T& orgNotified, Notified* observer);
public:
virtual void Reset();
virtual bool Init() { Reset(); return true; }
virtual void Shutdown() { Reset(); }
void Attach(Notified* observer, NOTIFIED_EVENT_TYPE_T eventType);
// Detach for a specific event
void Detach(Notified* observer, NOTIFIED_EVENT_TYPE_T eventType);
// Detach for ALL events
void Detach(Notified* observer);
/* The design of this interface is very specific. I could
* create a class to hold all the event data and then the
* method would just have take that object. But then I would
* have to search for every place in the code that created an
* object to be used and make sure it updated the passed in
* object when a member is added to it. This way, a break
* occurs at compile time that must be addressed.
*/
void Notify(NOTIFIED_EVENT_TYPE_T, const void* eventData = NULL);
/* Used for CPPUnit. Could create a Mock...maybe...but this seems
* like it will get the job done with minimal fuss. For now.
*/
// Return all events that this object is registered for.
vector<NOTIFIED_EVENT_TYPE_T> GetEvents(Notified* observer);
// Return all objects registered for this event.
vector<Notified*> GetNotified(NOTIFIED_EVENT_TYPE_T event);
};
/* This is the base class for anything that can receive notifications.
*/
class Notified
{
public:
virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, const void* eventData) = 0;
virtual ~Notified();
};
typedef Notifier::NOTIFIED_EVENT_TYPE_T NOTIFIED_EVENT_TYPE_T;
NOTE: The Notified class has a single function, Notify(...) here. Because the void* is not type safe, I created other versions where notify looks like:
virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, int value);
virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, const string& str);
Corresponding Notify(...) methods were added to the Notifier itself. All these used a single function to get the "target list" then called the appropriate function on the targets. This works well and keeps the receiver from having to do ugly casts.
This seems to work well. The solution is posted on the web here along with the source code. This is a relatively new design, so any feedback is greatly appreciated.
My two cents...
Classic (Gang of Four) implementation of Observer pattern notifies observer on changes in any property of the subject. In your question you want to register observer to particular properties, not to a subject as a whole. You can move Observer pattern one level down and take properties as concrete subjects and define their observers (per property) but there is one nicer way to solve this problem.
In C# Observer pattern is implemented through events and delegates. Delegates represent event handlers - functions that should be executed when an event is fired. Delegates can be added (registered) or removed(unregistered) from events.
In C++, functors act as delegates - they can store all necessary information to call some global function or class method in a different context. Events are collections of (registered) functors and when event is raised (called) it basically goes through that list and calls all functors (see Publisher::publish method in juanchopanza's solution).
I tried to implement C++ version of events and delegates and use them in modified Observer pattern which could be applied in your case. This is what I came up with:
#include <list>
#include <iostream>
#include <algorithm>
// use base class to resolve the problem of how to put into collection objects of different types
template <typename TPropertyType>
struct PropertyChangedDelegateBase
{
virtual ~PropertyChangedDelegateBase(){};
virtual void operator()(const TPropertyType& t) = 0;
};
template <typename THandlerOwner, typename TPropertyType>
struct PropertyChangedDelegate : public PropertyChangedDelegateBase<TPropertyType>
{
THandlerOwner* pHandlerOwner_;
typedef void (THandlerOwner::*TPropertyChangeHandler)(const TPropertyType&);
TPropertyChangeHandler handler_;
public:
PropertyChangedDelegate(THandlerOwner* pHandlerOwner, TPropertyChangeHandler handler) :
pHandlerOwner_(pHandlerOwner), handler_(handler){}
void operator()(const TPropertyType& t)
{
(pHandlerOwner_->*handler_)(t);
}
};
template<typename TPropertyType>
class PropertyChangedEvent
{
public:
virtual ~PropertyChangedEvent(){};
void add(PropertyChangedDelegateBase<TPropertyType>* const d)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
if(it != observers_.end())
throw std::runtime_error("Observer already registered");
observers_.push_back(d);
}
void remove(PropertyChangedDelegateBase<TPropertyType>* const d)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
if(it != observers_.end())
observers_.remove(d);
}
// notify
void operator()(const TPropertyType& newValue)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = observers_.begin();
for(; it != observers_.end(); ++it)
{
(*it)->operator()(newValue);
}
}
protected:
std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_;
};
// class that owns concrete subjects
class PropertyOwner1
{
int property1_;
float property2_;
public:
PropertyChangedEvent<int> property1ChangedEvent;
PropertyChangedEvent<float> property2ChangedEvent;
PropertyOwner1() :
property1_(0),
property2_(0.0f)
{}
int property1() const {return property1_;}
void property1(int n)
{
if(property1_ != n)
{
property1_ = n;
std::cout << "PropertyOwner1::property1(): property1_ set to " << property1_ << std::endl;
property1ChangedEvent(property1_);
}
}
float property2() const {return property2_;}
void property2(float n)
{
if(property2_ != n)
{
property2_ = n;
std::cout << "PropertyOwner1::property2(): property2_ set to " << property2_ << std::endl;
property2ChangedEvent(property2_);
}
}
};
// class that owns concrete subjects
class PropertyOwner2
{
bool property1_;
double property2_;
public:
PropertyChangedEvent<bool> property1ChangedEvent;
PropertyChangedEvent<double> property2ChangedEvent;
PropertyOwner2() :
property1_(false),
property2_(0.0)
{}
bool property1() const {return property1_;}
void property1(bool n)
{
if(property1_ != n)
{
property1_ = n;
std::cout << "PropertyOwner2::property1(): property1_ set to " << property1_ << std::endl;
property1ChangedEvent(property1_);
}
}
double property2() const {return property2_;}
void property2(double n)
{
if(property2_ != n)
{
property2_ = n;
std::cout << "PropertyOwner2::property2(): property2_ set to " << property2_ << std::endl;
property2ChangedEvent(property2_);
}
}
};
// class that observes changes in property1 of PropertyOwner1 and property1 of PropertyOwner2
struct PropertyObserver1
{
void OnPropertyOwner1Property1Changed(const int& newValue)
{
std::cout << "\tPropertyObserver1::OnPropertyOwner1Property1Changed(): \n\tnew value is: " << newValue << std::endl;
}
void OnPropertyOwner2Property1Changed(const bool& newValue)
{
std::cout << "\tPropertyObserver1::OnPropertyOwner2Property1Changed(): \n\tnew value is: " << newValue << std::endl;
}
};
// class that observes changes in property2 of PropertyOwner1 and property2 of PropertyOwner2
struct PropertyObserver2
{
void OnPropertyOwner1Property2Changed(const float& newValue)
{
std::cout << "\tPropertyObserver2::OnPropertyOwner1Property2Changed(): \n\tnew value is: " << newValue << std::endl;
}
void OnPropertyOwner2Property2Changed(const double& newValue)
{
std::cout << "\tPropertyObserver2::OnPropertyOwner2Property2Changed(): \n\tnew value is: " << newValue << std::endl;
}
};
int main(int argc, char** argv)
{
PropertyOwner1 propertyOwner1;
PropertyOwner2 propertyOwner2;
PropertyObserver1 propertyObserver1;
PropertyObserver2 propertyObserver2;
// register observers
PropertyChangedDelegate<PropertyObserver1, int> delegate1(&propertyObserver1, &PropertyObserver1::OnPropertyOwner1Property1Changed);
propertyOwner1.property1ChangedEvent.add(&delegate1);
PropertyChangedDelegate<PropertyObserver2, float> delegate2(&propertyObserver2, &PropertyObserver2::OnPropertyOwner1Property2Changed);
propertyOwner1.property2ChangedEvent.add(&delegate2);
PropertyChangedDelegate<PropertyObserver1, bool> delegate3(&propertyObserver1, &PropertyObserver1::OnPropertyOwner2Property1Changed);
propertyOwner2.property1ChangedEvent.add(&delegate3);
PropertyChangedDelegate<PropertyObserver2, double> delegate4(&propertyObserver2, &PropertyObserver2::OnPropertyOwner2Property2Changed);
propertyOwner2.property2ChangedEvent.add(&delegate4);
propertyOwner1.property1(1);
propertyOwner1.property2(1.2f);
propertyOwner2.property1(true);
propertyOwner2.property2(3.4);
// unregister PropertyObserver1
propertyOwner1.property1ChangedEvent.remove(&delegate1);
propertyOwner2.property1ChangedEvent.remove(&delegate3);
propertyOwner1.property1(2);
propertyOwner1.property2(4.5f);
}
Output:
PropertyOwner1::property1(): property1_ set to 1
PropertyObserver1::OnPropertyOwner1Property1Changed():
new value is: 1
PropertyOwner1::property2(): property2_ set to 1.2
PropertyObserver2::OnPropertyOwner1Property2Changed():
new value is: 1.2
PropertyOwner2::property1(): property1_ set to 1
PropertyObserver1::OnPropertyOwner2Property1Changed():
new value is: 1
PropertyOwner2::property2(): property2_ set to 3.4
PropertyObserver2::OnPropertyOwner2Property2Changed():
new value is: 3.4
PropertyOwner1::property1(): property1_ set to 2
PropertyOwner1::property2(): property2_ set to 4.5
PropertyObserver2::OnPropertyOwner1Property2Changed():
new value is: 4.5
Each observer is registered with a particular property and when notified, each observer knows exactly who is the owner of the property and what's property's new value.
-edit- i cant experiment ATM but will tonight. I am thinking maybe a typedef can be used to hold mut and can be used to declare a var. But my initial thought is typedefs don't play nice with templates so i'll have to check later tonight (for now, to class)
I was looking at this piece of code shown below and i was wondering how it might be possible to implement without using defines.
Since I cant compile the code (i don't have any mutex/multithreading libs currently installed) i'll just look at the code and think it out.
It seems like one can completely implement PROTECTED_WITH by inheriting a template class. The problem is now PROTECTED_MEMBER. It uses a name with ## to create a variable. This isnt much of a problem because we create a class which holds the variable with the () operator to make it appear as a function. However accessing is_held() the problem as i would like not to pass this or mut_ in.
My gut says with out of the box thinking its possible to solve this without defines and without passing in to each variable a this, function ptr or reference. I'll allow everyone to cheat and use c++0x features.
template<typename Mutex>
class TestableMutex {
public:
void lock() { m.lock(); id = this_thread::get_id(); }
void unlock() { id = 0; m.unlock(); }
bool try_lock() { bool b = m.try_lock();
if( b ) id = this_thread::get_id();
return b; }
bool is_held() { return id == this_thread::get_id(); }
private:
Mutex m;
atomic<thread::id> id;
// for recursive mutexes, add a count
};
#define PROTECTED_WITH(MutType) \
public: void lock() { mut_.lock(); } \
public: bool try_lock() { return mut_.try_lock(); } \
public: void unlock() { mut_.unlock(); } \
private: TestableMutex<MutType> mut_;
#define PROTECTED_MEMBER(Type,name) \
public: Type& name() { assert(mut_.is_held()); return name##_; } \
private: Type name##_;
struct MyData {
PROTECTED_WITH( some_mutex_type );
PROTECTED_MEMBER( vector<int>, v );
PROTECTED_MEMBER( Widget*, w );
};
You can use an explicit specialization containing using declarations to list the objects protected by the mutex. Then use a base class to "pass" the access out to the user via operator->, so object->member (with object not being a pointer) performs the mutex assertion.
This is easier done than said:
// Imagine that the members of this class must be locked by the mutex.
class a : public expose_locked_by_arrow< a > {
protected:
int i;
void f();
};
// Declare which members are conditionally locked. Pretty simple and idiomatic.
template<>
struct member_expose< a > : a {
using a::i;
using a::f;
};
#include <iostream>
// Access mutex-locked members with ->
int main() {
a x;
x->i = 5;
a const y( x );
std::cout << y->i << '\n';
}
The library code:
// This template is specialized for each mutex protection client.
template< class >
struct member_expose;
// Base class provides mutex; parameter is derived class (CRTP).
template< class c >
struct expose_locked_by_arrow {
member_expose< c > *
operator->() {
assert ( expose_lock_mutex.is_held() );
return static_cast< member_expose< c > * >( this );
}
member_expose< c > const *
operator->() const {
assert ( expose_lock_mutex.is_held() );
return static_cast< member_expose< c > const * >( this );
}
expose_locked_by_arrow( mutex const &m = mutex() )
: expose_lock_mutex( m ) {}
protected:
mutex expose_lock_mutex;
};
See it run.
The #defines aren't providing any protection as such, rather they are just reducing the amount of typing you'd have to do (in turn, they make sure all the "protected" members have the proper code in place).
There isn't a way that I am aware of to avoid having to put the checks into each getter function - and locking the whole object, as they are returning references to data stored within the protected object.
If however, they could all be returned by value (or not returning anything at all), then you could use a container that locks everything using a proxy object, something like the following (this could probably be done better, i've just quickly hacked it together):
#include <iostream>
struct Mutex
{
void lock()
{
std::cout << "Mutex::lock" << std::endl;
}
void unlock()
{
std::cout << "Mutex::unlock" << std::endl;
}
};
template <class Object>
class ThreadSafeObject
{
mutable Mutex d_mutex;
Object d_object;
public:
struct Proxy
{
mutable Mutex *d_mutex;
Object *d_object;
Proxy(Mutex *mutex, Object *object)
: d_mutex(mutex)
, d_object(object)
{
d_mutex->lock();
}
Proxy(const Proxy& proxy)
: d_mutex(proxy.d_mutex)
, d_object(proxy.d_object)
{
proxy.d_mutex = NULL;
}
~Proxy()
{
if (d_mutex)
{
d_mutex->unlock();
}
}
Object *operator->()
{
return d_object;
}
};
struct ConstProxy
{
mutable Mutex *d_mutex;
const Object *d_object;
ConstProxy(Mutex *mutex, const Object *object)
: d_mutex(mutex)
, d_object(object)
{
d_mutex->lock();
}
ConstProxy(const ConstProxy& proxy)
: d_mutex(proxy.d_mutex)
, d_object(proxy.d_object)
{
proxy.d_mutex = NULL;
}
~ConstProxy()
{
if (d_mutex)
{
d_mutex->unlock();
}
}
const Object *operator->() const
{
return d_object;
}
};
Proxy operator->()
{
return Proxy(&d_mutex, &d_object);
}
ConstProxy operator->() const
{
return ConstProxy(&d_mutex, &d_object);
}
};
struct Foo
{
void foo()
{
std::cout << "Foo::foo" << std::endl;
}
};
int main()
{
ThreadSafeObject<Foo> myFoo;
myFoo->foo();
return 0;
}
Which uses the operator->() trick (when operator-> doesnt reutrn a pointer type, the compiler will keep calling operator-> on the returned values until eventually a regular pointer type is returned) and gives the following output:
Mutex::lock
Foo::foo
Mutex::unlock
Generally speaking though, an object that needs to be used by multiple threads shouldn't be exposing its internals like that, it would be safer to have it accept parameters and use its internal values to act on them.