1 2 // Copyright (C) 2008-2018 Lorenzo Caminiti 3 // Distributed under the Boost Software License, Version 1.0 (see accompanying 4 // file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt). 5 // See: http://www.boost.org/doc/libs/release/libs/contract/doc/html/index.html 6 7 //[mitchell02_subject 8 #ifndef SUBJECT_HPP_ 9 #define SUBJECT_HPP_ 10 11 #include "observer.hpp" 12 #include <boost/contract.hpp> 13 #include <vector> 14 #include <algorithm> 15 #include <cassert> 16 17 // Subject for observer design pattern. 18 class subject { 19 friend class boost::contract::access; 20 invariant() const21 void invariant() const { 22 BOOST_CONTRACT_ASSERT_AUDIT(all_observers_valid(observers())); // Valid. 23 } 24 25 public: 26 /* Creation */ 27 28 // Construct subject with no observer. subject()29 subject() { 30 // Check invariant. 31 boost::contract::check c = boost::contract::constructor(this); 32 } 33 34 // Destroy subject. ~subject()35 virtual ~subject() { 36 // Check invariant. 37 boost::contract::check c = boost::contract::destructor(this); 38 } 39 40 /* Queries */ 41 42 // If given object is attached. attached(observer const * ob) const43 bool attached(observer const* ob) const { 44 boost::contract::check c = boost::contract::public_function(this) 45 .precondition([&] { 46 BOOST_CONTRACT_ASSERT(ob); // Not null. 47 }) 48 ; 49 50 return std::find(observers_.cbegin(), observers_.cend(), ob) != 51 observers_.cend(); 52 } 53 54 /* Commands */ 55 56 // Attach given object as an observer. attach(observer * ob)57 void attach(observer* ob) { 58 boost::contract::old_ptr<std::vector<observer const*> > old_observers; 59 #ifdef BOOST_CONTRACT_AUDITS 60 old_observers = BOOST_CONTRACT_OLDOF(observers()); 61 #endif 62 boost::contract::check c = boost::contract::public_function(this) 63 .precondition([&] { 64 BOOST_CONTRACT_ASSERT(ob); // Not null. 65 BOOST_CONTRACT_ASSERT(!attached(ob)); // Not already attached. 66 }) 67 .postcondition([&] { 68 BOOST_CONTRACT_ASSERT(attached(ob)); // Attached. 69 // Others not changed (frame rule). 70 BOOST_CONTRACT_ASSERT_AUDIT(other_observers_unchanged( 71 *old_observers, observers(), ob)); 72 }) 73 ; 74 75 observers_.push_back(ob); 76 } 77 78 protected: 79 // Contracts could have been omitted for protected/private with no pre/post. 80 81 /* Queries */ 82 83 // All observers attached to this subject. observers() const84 std::vector<observer const*> observers() const { 85 std::vector<observer const*> obs; 86 for(std::vector<observer*>::const_iterator i = observers_.cbegin(); 87 i != observers_.cend(); ++i) { 88 obs.push_back(*i); 89 } 90 return obs; 91 } 92 93 /* Commands */ 94 95 // Update all attached observers. notify()96 void notify() { 97 // Protected members use `function` (no inv and no subcontracting). 98 boost::contract::check c = boost::contract::function() 99 .postcondition([&] { 100 // All updated. 101 BOOST_CONTRACT_ASSERT_AUDIT(all_observers_updated(observers())); 102 }) 103 ; 104 105 for(std::vector<observer*>::iterator i = observers_.begin(); 106 i != observers_.end(); ++i) { 107 // Class invariants ensure no null pointers in observers but class 108 // invariants not checked for non-public functions so assert here. 109 assert(*i); // Pointer not null (defensive programming). 110 (*i)->update(); 111 } 112 } 113 114 private: 115 /* Contract Helpers */ 116 all_observers_valid(std::vector<observer const * > const & obs)117 static bool all_observers_valid(std::vector<observer const*> const& obs) { 118 for(std::vector<observer const*>::const_iterator i = obs.cbegin(); 119 i != obs.cend(); ++i) { 120 if(!*i) return false; 121 } 122 return true; 123 } 124 other_observers_unchanged(std::vector<observer const * > const & old_obs,std::vector<observer const * > const & new_obs,observer const * ob)125 static bool other_observers_unchanged( 126 std::vector<observer const*> const& old_obs, 127 std::vector<observer const*> const& new_obs, 128 observer const* ob 129 ) { 130 // Private members use `function` (no inv and no subcontracting). 131 boost::contract::check c = boost::contract::function() 132 .precondition([&] { 133 BOOST_CONTRACT_ASSERT(ob); // Not null. 134 }) 135 ; 136 137 std::vector<observer const*> remaining = new_obs; 138 std::remove(remaining.begin(), remaining.end(), ob); 139 140 std::vector<observer const*>::const_iterator remaining_it = 141 remaining.begin(); 142 std::vector<observer const*>::const_iterator old_it = old_obs.begin(); 143 while(remaining.cend() != remaining_it && old_obs.cend() != old_it) { 144 if(*remaining_it != *old_it) return false; 145 ++remaining_it; 146 ++old_it; 147 } 148 return true; 149 } 150 all_observers_updated(std::vector<observer const * > const & obs)151 static bool all_observers_updated(std::vector<observer const*> const& obs) { 152 for(std::vector<observer const*>::const_iterator i = obs.cbegin(); 153 i != obs.cend(); ++i) { 154 if(!*i) return false; 155 if(!(*i)->up_to_date_with_subject()) return false; 156 } 157 return true; 158 } 159 160 std::vector<observer*> observers_; 161 }; 162 163 #endif // #include guard 164 //] 165 166