• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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