1 /* 2 * This program source code file is part of KICAD, a free EDA CAD application. 3 * 4 * Copyright (C) 2016 Kicad Developers, see change_log.txt for contributors. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 2 9 * of the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, you may find one here: 18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 19 * or you may search the http://www.gnu.org website for the version 2 license, 20 * or you may write to the Free Software Foundation, Inc., 21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 22 */ 23 24 #include "observable.h" 25 #include <algorithm> 26 27 namespace UTIL { 28 29 namespace DETAIL { 30 31 template<typename T> 32 struct equals { equalsUTIL::DETAIL::equals33 equals( const T& val ) : val_( val ) {} 34 operator ()UTIL::DETAIL::equals35 bool operator()( const T& val ) const 36 { 37 return val == val_; 38 } 39 40 private: 41 const T& val_; 42 }; 43 44 IMPL(OBSERVABLE_BASE * owned_by)45 OBSERVABLE_BASE::IMPL::IMPL( OBSERVABLE_BASE* owned_by ) 46 : iteration_count_( 0 ), owned_by_( owned_by ) 47 {} 48 49 is_shared() const50 bool OBSERVABLE_BASE::IMPL::is_shared() const 51 { 52 return owned_by_ == nullptr; 53 } 54 55 set_shared()56 void OBSERVABLE_BASE::IMPL::set_shared() 57 { 58 owned_by_ = nullptr; 59 } 60 61 ~IMPL()62 OBSERVABLE_BASE::IMPL::~IMPL() 63 { 64 } 65 66 enter_iteration()67 void OBSERVABLE_BASE::IMPL::enter_iteration() 68 { 69 ++iteration_count_; 70 } 71 72 leave_iteration()73 void OBSERVABLE_BASE::IMPL::leave_iteration() 74 { 75 --iteration_count_; 76 77 if( iteration_count_ == 0 ) 78 collect(); 79 } 80 81 is_iterating() const82 bool OBSERVABLE_BASE::IMPL::is_iterating() const 83 { 84 return iteration_count_ != 0; 85 } 86 87 add_observer(void * observer)88 void OBSERVABLE_BASE::IMPL::add_observer( void* observer ) 89 { 90 assert( !is_iterating() ); 91 observers_.push_back( observer ); 92 } 93 94 remove_observer(void * observer)95 void OBSERVABLE_BASE::IMPL::remove_observer( void* observer ) 96 { 97 auto it = std::find( observers_.begin(), observers_.end(), observer ); 98 99 if( it == observers_.end() ) 100 { 101 assert( false ); 102 return; 103 } 104 105 if( is_iterating() ) 106 *it = nullptr; 107 else 108 observers_.erase( it ); 109 } 110 111 collect()112 void OBSERVABLE_BASE::IMPL::collect() 113 { 114 auto it = std::remove_if( observers_.begin(), observers_.end(), DETAIL::equals<void*>( nullptr ) ); 115 observers_.erase( it, observers_.end() ); 116 } 117 } 118 119 LINK()120 LINK::LINK() 121 : observer_( nullptr ) 122 { 123 } 124 125 LINK(std::shared_ptr<DETAIL::OBSERVABLE_BASE::IMPL> token,void * observer)126 LINK::LINK( std::shared_ptr<DETAIL::OBSERVABLE_BASE::IMPL> token, void* observer ) 127 : token_( std::move( token ) ), observer_( observer ) 128 { 129 } 130 131 LINK(LINK && other)132 LINK::LINK( LINK&& other ) 133 : token_( std::move( other.token_ ) ), observer_( other.observer_ ) 134 { 135 other.token_.reset(); 136 } 137 138 operator =(LINK && other)139 LINK& LINK::operator=( LINK&& other ) 140 { 141 token_ = std::move( other.token_ ); 142 other.token_.reset(); 143 observer_ = other.observer_; 144 return *this; 145 } 146 147 operator bool() const148 LINK::operator bool() const 149 { 150 return token_ ? true : false; 151 } 152 153 ~LINK()154 LINK::~LINK() 155 { 156 reset(); 157 } 158 159 reset()160 void LINK::reset() 161 { 162 if(token_) 163 { 164 token_->remove_observer( observer_ ); 165 token_.reset(); 166 } 167 } 168 169 170 namespace DETAIL { 171 OBSERVABLE_BASE()172 OBSERVABLE_BASE::OBSERVABLE_BASE() 173 { 174 } 175 176 OBSERVABLE_BASE(OBSERVABLE_BASE & other)177 OBSERVABLE_BASE::OBSERVABLE_BASE( OBSERVABLE_BASE& other ) 178 : impl_( other.get_shared_impl() ) 179 { 180 } 181 182 ~OBSERVABLE_BASE()183 OBSERVABLE_BASE::~OBSERVABLE_BASE() 184 { 185 } 186 187 allocate_impl()188 void OBSERVABLE_BASE::allocate_impl() 189 { 190 if(!impl_) 191 impl_ = std::make_shared<IMPL>( this ); 192 } 193 194 allocate_shared_impl()195 void OBSERVABLE_BASE::allocate_shared_impl() 196 { 197 if(!impl_) 198 impl_ = std::make_shared<IMPL>(); 199 else 200 impl_->set_shared(); 201 } 202 203 deallocate_impl()204 void OBSERVABLE_BASE::deallocate_impl() { 205 impl_.reset(); 206 } 207 208 get_shared_impl()209 std::shared_ptr<OBSERVABLE_BASE::IMPL> OBSERVABLE_BASE::get_shared_impl() 210 { 211 allocate_shared_impl(); 212 return impl_; 213 } 214 215 add_observer(void * observer)216 void OBSERVABLE_BASE::add_observer( void* observer ) 217 { 218 allocate_impl(); 219 impl_->add_observer( observer ); 220 } 221 222 remove_observer(void * observer)223 void OBSERVABLE_BASE::remove_observer( void* observer ) 224 { 225 assert( impl_ ); 226 impl_->remove_observer( observer ); 227 } 228 229 enter_iteration()230 void OBSERVABLE_BASE::enter_iteration() 231 { 232 if( impl_ ) 233 impl_->enter_iteration(); 234 } 235 236 leave_iteration()237 void OBSERVABLE_BASE::leave_iteration() 238 { 239 if( impl_) 240 { 241 impl_->leave_iteration(); 242 243 if( !impl_->is_iterating() && !impl_->is_shared() && impl_.use_count() == 1 ) 244 impl_.reset(); 245 } 246 } 247 248 size() const249 size_t OBSERVABLE_BASE::size() const 250 { 251 if( impl_ ) 252 return impl_->observers_.size(); 253 else 254 return 0; 255 } 256 257 on_observers_empty()258 void OBSERVABLE_BASE::on_observers_empty() 259 { 260 // called by an impl that is owned by this, ie. it is a non-shared impl 261 // also it is not iterating 262 deallocate_impl(); 263 } 264 265 } 266 267 } 268