QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.34
Loading...
Searching...
No Matches
lazyobject.hpp
Go to the documentation of this file.
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2003 RiskMap srl
5
6 This file is part of QuantLib, a free-software/open-source library
7 for financial quantitative analysts and developers - http://quantlib.org/
8
9 QuantLib is free software: you can redistribute it and/or modify it
10 under the terms of the QuantLib license. You should have received a
11 copy of the license along with this program; if not, please email
12 <quantlib-dev@lists.sf.net>. The license is also available online at
13 <http://quantlib.org/license.shtml>.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20/*! \file lazyobject.hpp
21 \brief framework for calculation on demand and result caching
22*/
23
24#ifndef quantlib_lazy_object_h
25#define quantlib_lazy_object_h
26
28#include <ql/shared_ptr.hpp>
29
30namespace QuantLib {
31
32 //! Framework for calculation on demand and result caching.
33 /*! \ingroup patterns */
34 class LazyObject : public virtual Observable,
35 public virtual Observer {
36 public:
37 LazyObject();
38 ~LazyObject() override = default;
39 //! \name Observer interface
40 //@{
41 void update() override;
42 //@}
43 /*! Returns true if the instrument is calculated */
44 bool isCalculated() const;
45 /*! \name Calculations
46 These methods do not modify the structure of the object
47 and are therefore declared as <tt>const</tt>. Data members
48 which will be calculated on demand need to be declared as
49 mutable.
50 */
51 //@{
52 /*! This method force the recalculation of any results which
53 would otherwise be cached. It is not declared as
54 <tt>const</tt> since it needs to call the
55 non-<tt>const</tt> <i><b>notifyObservers</b></i> method.
56
57 \note Explicit invocation of this method is <b>not</b>
58 necessary if the object registered itself as
59 observer with the structures on which such results
60 depend. It is strongly advised to follow this
61 policy when possible.
62 */
63 void recalculate();
64 /*! This method constrains the object to return the presently
65 cached results on successive invocations, even if
66 arguments upon which they depend should change.
67 */
68 void freeze();
69 /*! This method reverts the effect of the <i><b>freeze</b></i>
70 method, thus re-enabling recalculations.
71 */
72 void unfreeze();
73
74 protected:
75 /*! This method performs all needed calculations by calling
76 the <i><b>performCalculations</b></i> method.
77
78 \warning Objects cache the results of the previous
79 calculation. Such results will be returned upon
80 later invocations of
81 <i><b>calculate</b></i>. When the results depend
82 on arguments which could change between
83 invocations, the lazy object must register itself
84 as observer of such objects for the calculations
85 to be performed again when they change.
86
87 \warning Should this method be redefined in derived
88 classes, LazyObject::calculate() should be called
89 in the overriding method.
90 */
91 virtual void calculate() const;
92 /*! This method must implement any calculations which must be
93 (re)done in order to calculate the desired results.
94 */
95 virtual void performCalculations() const = 0;
96 //@}
97
98 public:
99 //! \name Notification settings
100 //@{
101 /*! This method causes the object to forward the first notification received,
102 and discard the others until recalculated; the rationale is that observers
103 were already notified, and don't need further notifications until they
104 recalculate, at which point this object would be recalculated too.
105 After recalculation, this object would again forward the first notification
106 received.
107
108 Although not always correct, this behavior is a lot faster
109 and thus is the current default. The default can be
110 changed at compile time, or at at run time by calling
111 `LazyObject::Defaults::instance().alwaysForwardNotifications()`;
112 the run-time change won't affect lazy objects already created.
113 */
115
116 /*! This method causes the object to forward all notifications received.
117
118 Although safer, this behavior is a lot slower and thus
119 usually not the default. The default can be changed at
120 compile time, or at run-time by calling
121 `LazyObject::Defaults::instance().alwaysForwardNotifications()`;
122 the run-time change won't affect lazy objects already
123 created.
124 */
126 //@}
127
128 protected:
129 mutable bool calculated_ = false, frozen_ = false, alwaysForward_;
130 private:
131 bool updating_ = false;
132 class UpdateChecker { // NOLINT(cppcoreguidelines-special-member-functions)
134 public:
135 explicit UpdateChecker(LazyObject* subject) : subject_(subject) {
136 subject_->updating_ = true;
137 }
139 subject_->updating_ = false;
140 }
141 };
142 public:
143 class Defaults;
144 };
145
146 //! Per-session settings for the LazyObject class
147 class LazyObject::Defaults : public Singleton<LazyObject::Defaults> {
148 friend class Singleton<LazyObject::Defaults>;
149 private:
150 Defaults() = default;
151
152 public:
153 /*! by default, lazy objects created after calling this method
154 will only forward the first notification after successful
155 recalculation; see
156 LazyObject::forwardFirstNotificationOnly for details.
157 */
159 forwardsAllNotifications_ = false;
160 }
161
162 /*! by default, lazy objects created after calling this method
163 will always forward notifications; see
164 LazyObject::alwaysForwardNotifications for details.
165 */
168 }
169
170 //! returns the current default
173 }
174
175 private:
176 #ifdef QL_FASTER_LAZY_OBJECTS
177 bool forwardsAllNotifications_ = false;
178 #else
180 #endif
181 };
182
183 // inline definitions
184
186 : alwaysForward_(LazyObject::Defaults::instance().forwardsAllNotifications()) {}
187
188 inline void LazyObject::update() {
189 if (updating_) {
190 #ifdef QL_THROW_IN_CYCLES
191 QL_FAIL("recursive notification loop detected; you probably created an object cycle");
192 #else
193 return;
194 #endif
195 }
196
197 // This sets updating to true (so the above check breaks the
198 // infinite loop if we enter this method recursively) and will
199 // set it back to false when we exit this scope, either
200 // successfully or because of an exception.
201 UpdateChecker checker(this);
202
203 // forwards notifications only the first time
205 // set to false early
206 // 1) to prevent infinite recursion
207 // 2) otherways non-lazy observers would be served obsolete
208 // data because of calculated_ being still true
209 calculated_ = false;
210 // observers don't expect notifications from frozen objects
211 if (!frozen_)
213 // exiting notifyObservers() calculated_ could be
214 // already true because of non-lazy observers
215 }
216 }
217
219 bool wasFrozen = frozen_;
220 calculated_ = frozen_ = false;
221 try {
222 calculate();
223 } catch (...) {
224 frozen_ = wasFrozen;
226 throw;
227 }
228 frozen_ = wasFrozen;
230 }
231
232 inline void LazyObject::freeze() {
233 frozen_ = true;
234 }
235
236 inline void LazyObject::unfreeze() {
237 // send notifications, just in case we lost any,
238 // but only once, i.e. if it was frozen
239 if (frozen_) {
240 frozen_ = false;
242 }
243 }
244
246 alwaysForward_ = false;
247 }
248
250 alwaysForward_ = true;
251 }
252
253 inline void LazyObject::calculate() const {
254 if (!calculated_ && !frozen_) {
255 calculated_ = true; // prevent infinite recursion in
256 // case of bootstrapping
257 try {
259 } catch (...) {
260 calculated_ = false;
261 throw;
262 }
263 }
264 }
265
266 inline bool LazyObject::isCalculated() const {
267 return calculated_;
268 }
269}
270
271#endif
Per-session settings for the LazyObject class.
Definition: lazyobject.hpp:147
bool forwardsAllNotifications() const
returns the current default
Definition: lazyobject.hpp:171
UpdateChecker(LazyObject *subject)
Definition: lazyobject.hpp:135
Framework for calculation on demand and result caching.
Definition: lazyobject.hpp:35
virtual void calculate() const
Definition: lazyobject.hpp:253
void alwaysForwardNotifications()
Definition: lazyobject.hpp:249
void forwardFirstNotificationOnly()
Definition: lazyobject.hpp:245
void update() override
Definition: lazyobject.hpp:188
virtual void performCalculations() const =0
bool isCalculated() const
Definition: lazyobject.hpp:266
~LazyObject() override=default
Object that notifies its changes to a set of observers.
Definition: observable.hpp:62
Object that gets notified when a given observable changes.
Definition: observable.hpp:116
Basic support for the singleton pattern.
Definition: singleton.hpp:58
#define QL_FAIL(message)
throw an error (possibly with file and line information)
Definition: errors.hpp:92
Definition: any.hpp:35
observer/observable pattern
Maps shared_ptr to either the boost or std implementation.