Coverage Report

Created: 2026-02-03 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/patterns/observable.hpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
5
Copyright (C) 2003, 2004, 2005, 2006 StatPro Italia srl
6
Copyright (C) 2011, 2012 Ferdinando Ametrano
7
Copyright (C) 2013 Chris Higgs
8
Copyright (C) 2015 Klaus Spanderen
9
10
11
This file is part of QuantLib, a free-software/open-source library
12
for financial quantitative analysts and developers - http://quantlib.org/
13
14
QuantLib is free software: you can redistribute it and/or modify it
15
under the terms of the QuantLib license.  You should have received a
16
copy of the license along with this program; if not, please email
17
<quantlib-dev@lists.sf.net>. The license is also available online at
18
<https://www.quantlib.org/license.shtml>.
19
20
This program is distributed in the hope that it will be useful, but WITHOUT
21
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22
FOR A PARTICULAR PURPOSE.  See the license for more details.
23
*/
24
25
/*! \file observable.hpp
26
    \brief observer/observable pattern
27
*/
28
29
#ifndef quantlib_observable_hpp
30
#define quantlib_observable_hpp
31
32
#include <ql/errors.hpp>
33
#include <ql/patterns/singleton.hpp>
34
#include <ql/shared_ptr.hpp>
35
#include <ql/types.hpp>
36
#include <set>
37
#include <map>
38
39
#if !defined(QL_USE_STD_SHARED_PTR) && BOOST_VERSION < 107400
40
41
namespace std {
42
43
    template<typename T>
44
    struct hash<boost::shared_ptr<T>> {
45
        std::size_t operator()(const boost::shared_ptr<T>& ptr) const noexcept {
46
            return std::hash<typename boost::shared_ptr<T>::element_type*>()(ptr.get());
47
        }
48
    };
49
50
}
51
52
#endif
53
54
#ifndef QL_ENABLE_THREAD_SAFE_OBSERVER_PATTERN
55
56
namespace QuantLib {
57
58
    class Observer;
59
    class ObservableSettings;
60
61
    //! Object that notifies its changes to a set of observers
62
    /*! \ingroup patterns */
63
    class Observable {
64
        friend class Observer;
65
        friend class ObservableSettings;
66
      public:
67
        // constructors, assignment, destructor
68
56.2M
        Observable() = default;
69
        Observable(const Observable&);
70
        Observable& operator=(const Observable&);
71
        // delete the move operations because the semantics are not yet clear
72
        Observable(Observable&&) = delete;
73
        Observable& operator=(Observable&&) = delete;
74
56.2M
        virtual ~Observable() = default;
75
        /*! This method should be called at the end of non-const methods
76
            or when the programmer desires to notify any changes.
77
        */
78
        void notifyObservers();
79
      private:
80
        typedef std::set<Observer*> set_type;
81
        typedef set_type::iterator iterator;
82
        std::pair<iterator, bool> registerObserver(Observer*);
83
        Size unregisterObserver(Observer*);
84
        set_type observers_;
85
    };
86
87
    //! global repository for run-time library settings
88
    class ObservableSettings : public Singleton<ObservableSettings> {
89
        friend class Singleton<ObservableSettings>;
90
        friend class Observable;
91
      public:
92
0
        void disableUpdates(bool deferred=false) {
93
0
            updatesEnabled_  = false;
94
0
            updatesDeferred_ = deferred;
95
0
        }
96
        void enableUpdates();
97
98
3.71M
        bool updatesEnabled() const { return updatesEnabled_; }
99
1.01M
        bool updatesDeferred() const { return updatesDeferred_; }
100
1.01M
        bool runningDeferredUpdates() const { return runningDeferredUpdates_; }
101
102
      private:
103
2
        ObservableSettings() = default;
104
105
        typedef std::map<Observer*, bool> set_type;
106
        typedef set_type::iterator iterator;
107
108
        void registerDeferredObservers(const Observable::set_type& observers);
109
        void unregisterDeferredObserver(Observer*);
110
111
        set_type deferredObservers_;
112
113
        bool updatesEnabled_ = true, updatesDeferred_ = false;
114
        bool runningDeferredUpdates_ = false;
115
    };
116
117
    //! Object that gets notified when a given observable changes
118
    /*! \ingroup patterns */
119
    class Observer { // NOLINT(cppcoreguidelines-special-member-functions)
120
      private:
121
        typedef std::set<ext::shared_ptr<Observable>> set_type;
122
      public:
123
        typedef set_type::iterator iterator;
124
125
        // constructors, assignment, destructor
126
55.7M
        Observer() = default;
127
        Observer(const Observer&);
128
        Observer& operator=(const Observer&);
129
        virtual ~Observer();
130
131
        // observer interface
132
        std::pair<iterator, bool>
133
        registerWith(const ext::shared_ptr<Observable>&);
134
135
        /*! register with all observables of a given observer. Note
136
            that this does not include registering with the observer
137
            itself.
138
        */
139
        void registerWithObservables(const ext::shared_ptr<Observer>&);
140
141
        Size unregisterWith(const ext::shared_ptr<Observable>&);
142
        void unregisterWithAll();
143
144
        /*! This method must be implemented in derived classes. An
145
            instance of %Observer does not call this method directly:
146
            instead, it will be called by the observables the instance
147
            registered with when they need to notify any changes.
148
        */
149
        virtual void update() = 0;
150
151
        /*! This method allows to explicitly update the instance itself
152
          and nested observers. If notifications are disabled a call to
153
          this method ensures an update of such nested observers. It
154
          should be implemented in derived classes whenever applicable */
155
        virtual void deepUpdate();
156
157
      private:
158
        set_type observables_;
159
    };
160
161
162
    // inline definitions
163
164
0
    inline void ObservableSettings::registerDeferredObservers(const Observable::set_type& observers) {
165
0
        if (updatesDeferred()) {
166
0
            for (Observer* obs : observers)
167
0
                deferredObservers_.emplace(obs, true);
168
0
        }
169
0
    }
170
171
0
    inline void ObservableSettings::unregisterDeferredObserver(Observer* o) {
172
0
        if (updatesDeferred())
173
0
            deferredObservers_.erase(o);
174
0
        else
175
0
        {
176
0
            auto it = deferredObservers_.find(o);
177
0
            if (it != deferredObservers_.end())
178
0
                it->second = false;
179
0
        }
180
0
    }
181
182
0
    inline Observable::Observable(const Observable&) {
183
        // the observer set is not copied; no observer asked to
184
        // register with this object
185
0
    }
186
187
    /*! \warning notification is sent before the copy constructor has
188
                 a chance of actually change the data
189
                 members. Therefore, observers whose update() method
190
                 tries to use their observables will not see the
191
                 updated values. It is suggested that the update()
192
                 method just raise a flag in order to trigger
193
                 a later recalculation.
194
    */
195
0
    inline Observable& Observable::operator=(const Observable& o) {
196
0
        // as above, the observer set is not copied. Moreover,
197
0
        // observers of this object must be notified of the change
198
0
        if (&o != this)
199
0
            notifyObservers();
200
0
        return *this;
201
0
    }
202
203
    inline std::pair<Observable::iterator, bool>
204
1.09M
    Observable::registerObserver(Observer* o) {
205
1.09M
        return observers_.insert(o);
206
1.09M
    }
207
208
1.01M
    inline Size Observable::unregisterObserver(Observer* o) {
209
1.01M
        if (ObservableSettings::instance().updatesDeferred() ||
210
1.01M
            ObservableSettings::instance().runningDeferredUpdates())
211
0
            ObservableSettings::instance().unregisterDeferredObserver(o);
212
213
1.01M
        return observers_.erase(o);
214
1.01M
    }
215
216
217
    inline Observer::Observer(const Observer& o)
218
0
    : observables_(o.observables_) {
219
0
        for (const auto& observable : observables_)
220
0
            observable->registerObserver(this);
221
0
    }
222
223
0
    inline Observer& Observer::operator=(const Observer& o) {
224
0
        for (const auto& observable : observables_)
225
0
            observable->unregisterObserver(this);
226
0
        observables_ = o.observables_;
227
0
        for (const auto& observable : observables_)
228
0
            observable->registerObserver(this);
229
0
        return *this;
230
0
    }
231
232
55.7M
    inline Observer::~Observer() {
233
55.7M
        for (const auto& observable : observables_)
234
1.01M
            observable->unregisterObserver(this);
235
55.7M
    }
236
237
    inline std::pair<Observer::iterator, bool>
238
1.09M
    Observer::registerWith(const ext::shared_ptr<Observable>& h) {
239
1.09M
        if (h != nullptr) {
240
1.09M
            h->registerObserver(this);
241
1.09M
            return observables_.insert(h);
242
1.09M
        }
243
0
        return std::make_pair(observables_.end(), false);
244
1.09M
    }
245
246
    inline void
247
0
    Observer::registerWithObservables(const ext::shared_ptr<Observer> &o) {
248
0
        if (o != nullptr) {
249
0
            for (const auto& observable : o->observables_)
250
0
                registerWith(observable);
251
0
        }
252
0
    }
253
254
    inline
255
0
    Size Observer::unregisterWith(const ext::shared_ptr<Observable>& h) {
256
0
        if (h != nullptr)
257
0
            h->unregisterObserver(this);
258
0
        return observables_.erase(h);
259
0
    }
260
261
0
    inline void Observer::unregisterWithAll() {
262
0
        for (const auto& observable : observables_)
263
0
            observable->unregisterObserver(this);
264
0
        observables_.clear();
265
0
    }
266
267
0
    inline void Observer::deepUpdate() {
268
0
        update();
269
0
    }
270
271
}
272
273
#else
274
275
#ifndef QL_USE_STD_SHARED_PTR
276
#include <boost/smart_ptr/owner_less.hpp>
277
#endif
278
#include <atomic>
279
#include <mutex>
280
#include <set>
281
#include <thread>
282
283
namespace QuantLib {
284
285
    class Observable;
286
    class ObservableSettings;
287
288
    //! Object that gets notified when a given observable changes
289
    /*! \ingroup patterns */
290
    class Observer : public ext::enable_shared_from_this<Observer> {
291
        friend class Observable;
292
        friend class ObservableSettings;
293
      private:
294
        typedef std::set<ext::shared_ptr<Observable>> set_type;
295
      public:
296
        typedef set_type::iterator iterator;
297
298
        // constructors, assignment, destructor
299
        Observer() {}
300
        Observer(const Observer&);
301
        Observer& operator=(const Observer&);
302
        virtual ~Observer();
303
        // observer interface
304
        std::pair<iterator, bool>
305
        registerWith(const ext::shared_ptr<Observable>&);
306
        /*! register with all observables of a given observer. Note
307
            that this does not include registering with the observer
308
            itself.
309
        */
310
        void registerWithObservables(const ext::shared_ptr<Observer>&);
311
312
        Size unregisterWith(const ext::shared_ptr<Observable>&);
313
        void unregisterWithAll();
314
315
        /*! This method must be implemented in derived classes. An
316
            instance of %Observer does not call this method directly:
317
            instead, it will be called by the observables the instance
318
            registered with when they need to notify any changes.
319
        */
320
        virtual void update() = 0;
321
322
        /*! This method allows to explicitly update the instance itself
323
          and nested observers. If notifications are disabled a call to
324
          this method ensures an update of such nested observers. It
325
          should be implemented in derived classes whenever applicable */
326
        virtual void deepUpdate();
327
328
      private:
329
330
        class Proxy {
331
          public:
332
            explicit Proxy(Observer* const observer)
333
             : active_  (true),
334
               observer_(observer) {
335
            }
336
337
            void update() const {
338
                std::lock_guard<std::recursive_mutex> lock(mutex_);
339
                if (active_) {
340
                    // c++17 is required if used with std::shared_ptr<T>
341
                    const ext::weak_ptr<Observer> o
342
                        = observer_->weak_from_this();
343
344
                    //check for empty weak reference
345
                    //https://stackoverflow.com/questions/45507041/how-to-check-if-weak-ptr-is-empty-non-assigned
346
                    const ext::weak_ptr<Observer> empty;
347
                    if (o.owner_before(empty) || empty.owner_before(o)) {
348
                        const ext::shared_ptr<Observer> obs(o.lock());
349
                        if (obs)
350
                            obs->update();
351
                    }
352
                    else {
353
                        observer_->update();
354
                    }
355
                }
356
            }
357
358
            void deactivate() {
359
                std::lock_guard<std::recursive_mutex> lock(mutex_);
360
                active_ = false;
361
            }
362
363
        private:
364
            bool active_;
365
            mutable std::recursive_mutex mutex_;
366
            Observer* const observer_;
367
        };
368
369
        ext::shared_ptr<Proxy> proxy_;
370
        mutable std::recursive_mutex mutex_;
371
372
        set_type observables_;
373
    };
374
375
    namespace detail {
376
        class Signal;
377
    }
378
379
    //! Object that notifies its changes to a set of observers
380
    /*! \ingroup patterns */
381
    class Observable {
382
        friend class Observer;
383
        friend class ObservableSettings;
384
      private:
385
        typedef std::set<ext::shared_ptr<Observer::Proxy>> set_type;
386
      public:
387
        typedef set_type::iterator iterator;
388
389
        // constructors, assignment, destructor
390
        Observable();
391
        Observable(const Observable&);
392
        Observable& operator=(const Observable&);
393
        virtual ~Observable() {}
394
        /*! This method should be called at the end of non-const methods
395
            or when the programmer desires to notify any changes.
396
        */
397
        void notifyObservers();
398
      private:
399
        void registerObserver(const ext::shared_ptr<Observer::Proxy>&);
400
        void unregisterObserver(
401
            const ext::shared_ptr<Observer::Proxy>& proxy, bool disconnect);
402
403
        ext::shared_ptr<detail::Signal> sig_;
404
        set_type observers_;
405
        mutable std::recursive_mutex mutex_;
406
    };
407
408
    //! global repository for run-time library settings
409
    class ObservableSettings : public Singleton<ObservableSettings> {
410
        friend class Singleton<ObservableSettings>;
411
        friend class Observable;
412
413
      public:
414
        void disableUpdates(bool deferred=false) {
415
            std::lock_guard<std::mutex> lock(mutex_);
416
            updatesType_ = (deferred) ? UpdatesDeferred : UpdatesDisabled;
417
        }
418
        void enableUpdates();
419
420
        bool updatesEnabled()  {return (updatesType_ & UpdatesEnabled) != 0; }
421
        bool updatesDeferred() {return (updatesType_ & UpdatesDeferred) != 0; }
422
      private:
423
        ObservableSettings() : updatesType_(UpdatesEnabled) {}
424
425
#if defined(QL_USE_STD_SHARED_PTR)
426
        typedef std::set<ext::weak_ptr<Observer::Proxy>,
427
                         std::owner_less<ext::weak_ptr<Observer::Proxy> > >
428
            set_type;
429
#else
430
        typedef std::set<ext::weak_ptr<Observer::Proxy>,
431
                         boost::owner_less<ext::weak_ptr<Observer::Proxy> > >
432
            set_type;
433
#endif
434
435
        void registerDeferredObservers(const Observable::set_type& observers);
436
        void unregisterDeferredObserver(const ext::shared_ptr<Observer::Proxy>& proxy);
437
438
        set_type deferredObservers_;
439
        mutable std::mutex mutex_;
440
441
        enum UpdateType { UpdatesDisabled = 0, UpdatesEnabled = 1, UpdatesDeferred = 2} ;
442
        std::atomic<int> updatesType_;
443
    };
444
445
446
    // inline definitions
447
448
    inline void ObservableSettings::registerDeferredObservers(const Observable::set_type& observers) {
449
        deferredObservers_.insert(observers.begin(), observers.end());
450
    }
451
452
    inline void ObservableSettings::unregisterDeferredObserver(
453
        const ext::shared_ptr<Observer::Proxy>& o) {
454
        deferredObservers_.erase(o);
455
    }
456
457
    inline void ObservableSettings::enableUpdates() {
458
        std::lock_guard<std::mutex> lock(mutex_);
459
460
        // if there are outstanding deferred updates, do the notification
461
        updatesType_ = UpdatesEnabled;
462
463
        if (deferredObservers_.size()) {
464
            bool successful = true;
465
            std::string errMsg;
466
467
            for (auto i=deferredObservers_.begin();
468
                i!=deferredObservers_.end(); ++i) {
469
                try {
470
                    const ext::shared_ptr<Observer::Proxy> proxy = i->lock();
471
                    if (proxy)
472
                        proxy->update();
473
                } catch (std::exception& e) {
474
                    successful = false;
475
                    errMsg = e.what();
476
                } catch (...) {
477
                    successful = false;
478
                }
479
            }
480
481
            deferredObservers_.clear();
482
483
            QL_ENSURE(successful,
484
                  "could not notify one or more observers: " << errMsg);
485
        }
486
    }
487
488
489
    /*! \warning notification is sent before the copy constructor has
490
             a chance of actually change the data
491
             members. Therefore, observers whose update() method
492
             tries to use their observables will not see the
493
             updated values. It is suggested that the update()
494
             method just raise a flag in order to trigger
495
            a later recalculation.
496
    */
497
    inline Observable& Observable::operator=(const Observable& o) {
498
        // as above, the observer set is not copied. Moreover,
499
        // observers of this object must be notified of the change
500
        if (&o != this)
501
            notifyObservers();
502
        return *this;
503
    }
504
505
    inline Observer::Observer(const Observer& o) {
506
        proxy_.reset(new Proxy(this));
507
508
        {
509
             std::lock_guard<std::recursive_mutex> lock(o.mutex_);
510
             observables_ = o.observables_;
511
        }
512
513
        for (const auto& observable : observables_)
514
            observable->registerObserver(proxy_);
515
    }
516
517
    inline Observer& Observer::operator=(const Observer& o) {
518
        std::lock_guard<std::recursive_mutex> lock(mutex_);
519
        if (!proxy_) {
520
            proxy_.reset(new Proxy(this));
521
        }
522
523
        for (const auto& observable : observables_)
524
            observable->unregisterObserver(proxy_, true);
525
526
        {
527
            std::lock_guard<std::recursive_mutex> lock(o.mutex_);
528
            observables_ = o.observables_;
529
        }
530
        for (const auto& observable : observables_)
531
            observable->registerObserver(proxy_);
532
533
        return *this;
534
    }
535
536
    inline Observer::~Observer() {
537
        std::lock_guard<std::recursive_mutex> lock(mutex_);
538
        if (proxy_)
539
            proxy_->deactivate();
540
541
        for (const auto& observable : observables_)
542
            observable->unregisterObserver(proxy_, false);
543
    }
544
545
    inline std::pair<Observer::iterator, bool>
546
    Observer::registerWith(const ext::shared_ptr<Observable>& h) {
547
        std::lock_guard<std::recursive_mutex> lock(mutex_);
548
        if (!proxy_) {
549
            proxy_.reset(new Proxy(this));
550
        }
551
552
        if (h) {
553
            h->registerObserver(proxy_);
554
            return observables_.insert(h);
555
        }
556
        return std::make_pair(observables_.end(), false);
557
    }
558
559
    inline void
560
    Observer::registerWithObservables(const ext::shared_ptr<Observer>& o) {
561
        if (o) {
562
            std::lock_guard<std::recursive_mutex> lock(o->mutex_);
563
564
            for (const auto& observable : o->observables_)
565
                registerWith(observable);
566
        }
567
    }
568
569
    inline
570
    Size Observer::unregisterWith(const ext::shared_ptr<Observable>& h) {
571
        std::lock_guard<std::recursive_mutex> lock(mutex_);
572
573
        if (h && proxy_)  {
574
            h->unregisterObserver(proxy_, true);
575
        }
576
577
        return observables_.erase(h);
578
    }
579
580
    inline void Observer::unregisterWithAll() {
581
        std::lock_guard<std::recursive_mutex> lock(mutex_);
582
583
        for (const auto& observable : observables_)
584
            observable->unregisterObserver(proxy_, true);
585
586
        observables_.clear();
587
    }
588
589
    inline void Observer::deepUpdate() {
590
        update();
591
    }
592
}
593
#endif
594
#endif