Coverage Report

Created: 2025-08-05 06:45

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