Reference documentation for deal.II version 9.6.0
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches

#include <deal.II/base/lazy.h>

Inheritance diagram for Lazy< T >:

Public Member Functions

 Lazy ()
 
 Lazy (const Lazy &other)
 
 Lazy (Lazy &&other) noexcept
 
Lazyoperator= (const Lazy &other)
 
Lazyoperator= (Lazy &&other) noexcept
 
void reset () noexcept
 
template<typename Callable >
void ensure_initialized (const Callable &creator) const
 
bool has_value () const
 
const T & value () const
 
T & value ()
 
template<typename Callable >
const T & value_or_initialize (const Callable &creator) const
 
template<typename Callable >
T & value_or_initialize (const Callable &creator)
 
std::size_t memory_consumption () const
 

Private Attributes

std::optional< T > object
 
std::atomic< boolobject_is_initialized
 
Threads::Mutex initialization_mutex
 

Detailed Description

template<typename T>
class Lazy< T >

This class is a wrapper that provides a convenient mechanism for lazy initialization of the contained object on first use. The class ensures that on-demand initialization of some expensive data structure happens (a) exactly once in a thread-safe manner, and that (b) subsequent checks in hot paths are cheap.

Lazy<T> is closely modeled after the std::optional interface providing a reset() and value() method, but also and extending it with two methods: ensure_initialized(creator) which, as the name suggests, ensures that the contained object is properly initialized. If the Lazy<T> happens happens to contain no value yet, it initializes the wrapped object by calling the creator() function object and storing the return value. In addition a value_or_initialize(creator) function is provided that, similarly, ensures that the object is properly initialized and then returns a reference to the contained value.

Example usage could look like the following, where the FE class stores a matrix that is expensive to compute and so we do not want to do it unless it is actually needed. As a consequence, rather than storing a matrix, we store a Lazy<FullMatrix<double>> that by default is empty; whenever the matrix is first requested, we create it and store it for later reuse:

template<...>
class FE
{
public:
const FullMatrix<double> & get_prolongation_matrix() const
{
prolongation_matrix.ensure_initialized([&](){
// Some expensive operation initializing the prolongation matrix
// that we only want to perform once and when necessary.
});
return prolongation_matrix.value();
}
private:
Lazy<FullMatrix<double>> prolongation_matrix;
};
Definition lazy.h:134
Note
Conceptually, this class is not so different from std::future, which can also be used to represent a possibly-not-yet-available value on which one can wait when used with the "deferred" policy of std::async. In particular, the following code could be used in place of the one above:
template<...>
class FE
{
public:
FE () {
prolongation_matrix = std::async(std::launch::deferred,
[&](){
// Some expensive operation initializing the prolongation matrix
// that we only want to perform once and when necessary.
});
}
FullMatrix<double> get_prolongation_matrix() const
{
return prolongation_matrix.get();
}
private:
std::future<FullMatrix<double>> prolongation_matrix;
};
The difference to what Lazy does is that for Lazy, the action must be specified in the place where we want to access the deferred computation's result. In contrast, in the scheme with std::future and std::async, the action has to be provided at the point where the std::future object is initialized. Both are valid approaches and, depending on context, can usefully be employed. The difference is simply in what kind of information the provided lambda function can capture: Is it the environment available at the time the constructor is run, or the environment available at the time the access function is run. The latter has the advantage that the information captured is always up to date, whereas in the scheme with std::async, one has to be careful not to capture information in the lambda function that could be changed by later calls to member functions but before the lambda function is finally evaluated in the getter function. (There is another difference: std::future::get() can only be called once, as the function returns the computed object by value and may move the object out of its internal storage. As a consequence, the call to FE::get_prolongation_matrix() is only valid the first time around. Lazy does not have this restriction.)
This class, function, or variable is a template, and it can only be instantiated if the following condition is true:
std::is_move_constructible_v<T> &&
std::is_move_assignable_v<T >
If your compiler supports the C++20 standard, then this constraint will be enforced by a C++20 requires clause.

Definition at line 133 of file lazy.h.

Constructor & Destructor Documentation

◆ Lazy() [1/3]

template<typename T >
Lazy< T >::Lazy ( )
inline

Default Constructor.

Definition at line 312 of file lazy.h.

◆ Lazy() [2/3]

template<typename T >
Lazy< T >::Lazy ( const Lazy< T > & other)
inline

Copy constructor. If the other object contains an initialized value, then that value will be copied into the current object. If the other object is uninitialized, then the current object will be as well.

Definition at line 320 of file lazy.h.

◆ Lazy() [3/3]

template<typename T >
Lazy< T >::Lazy ( Lazy< T > && other)
inlinenoexcept

Move constructor. If the other object contains an initialized value, then that value will be moved into the current object, and the other object will end up being empty (as if default initialized). If the other object is uninitialized, then the current object will be as well.

Definition at line 330 of file lazy.h.

Member Function Documentation

◆ operator=() [1/2]

template<typename T >
Lazy< T > & Lazy< T >::operator= ( const Lazy< T > & other)
inline

Copy assignment. If the other object contains an initialized value, then that value will be copied into the current object. If the other object is uninitialized, then the current object will be as well.

Any content of the current object is lost in the assignment.

Definition at line 348 of file lazy.h.

◆ operator=() [2/2]

template<typename T >
Lazy< T > & Lazy< T >::operator= ( Lazy< T > && other)
inlinenoexcept

Move assignment. If the other object contains an initialized value, then that value will be moved into the current object, and the other object will end up being empty (as if default initialized). If the other object is uninitialized, then the current object will be as well.

Any content of the current object is lost in the move assignment.

Definition at line 359 of file lazy.h.

◆ reset()

template<typename T >
void Lazy< T >::reset ( )
inlinenoexcept

Reset the Lazy<T> object to an uninitialized state.

Definition at line 379 of file lazy.h.

◆ ensure_initialized()

template<typename T >
template<typename Callable >
void Lazy< T >::ensure_initialized ( const Callable & creator) const
inline

Initialize the wrapped object.

If the contained object is already initialized this function simply returns and does nothing.

If, instead, the object has not yet been initialized then the creator function object (oftentimes a lambda function) is called to initialize the contained object.

This operation is thread safe: The ensure_initialized() method guarantees that the creator function object is only called once on one of the calling threads and that after completion the initialization result (which is stored in the std::optional) is visible on all threads.

Note
This class, function, or variable is a template, and it can only be instantiated if the following condition is true:
std::is_invocable_r_v<T, Callable>
If your compiler supports the C++20 standard, then this constraint will be enforced by a C++20 requires clause.

Definition at line 391 of file lazy.h.

◆ has_value()

template<typename T >
bool Lazy< T >::has_value ( ) const
inline

Returns true if the contained object has been initialized, otherwise false.

Definition at line 454 of file lazy.h.

◆ value() [1/2]

template<typename T >
const T & Lazy< T >::value ( ) const
inline

Return a const reference to the contained object.

Precondition
The object has been initialized with a call to ensure_initialized() or value_or_initialized().

Definition at line 469 of file lazy.h.

◆ value() [2/2]

template<typename T >
T & Lazy< T >::value ( )
inline

Return a reference to the contained object.

Precondition
The object has been initialized with a call to ensure_initialized() or value_or_initialize().

Definition at line 484 of file lazy.h.

◆ value_or_initialize() [1/2]

template<typename T >
template<typename Callable >
const T & Lazy< T >::value_or_initialize ( const Callable & creator) const
inline

If the underlying object is initialized the function simply returns a const reference to the contained value. Otherwise, the creator() function object is called to initialize the object first.

This function mimics the syntax of the std::optional<T> interface and is functionally equivalent to calling ensure_initialized() followed by value().

Note
This method can be called from a context where the Lazy<T> wrapper itself is marked const. FIXME
Postcondition
The underlying object is initialized, meaning, has_value() returns true.
Note
This class, function, or variable is a template, and it can only be instantiated if the following condition is true:
std::is_invocable_r_v<T, Callable>
If your compiler supports the C++20 standard, then this constraint will be enforced by a C++20 requires clause.

Definition at line 500 of file lazy.h.

◆ value_or_initialize() [2/2]

template<typename T >
template<typename Callable >
T & Lazy< T >::value_or_initialize ( const Callable & creator)
inline

Variant of above function that returns a non-const reference.

Note
This class, function, or variable is a template, and it can only be instantiated if the following condition is true:
std::is_invocable_r_v<T, Callable>
If your compiler supports the C++20 standard, then this constraint will be enforced by a C++20 requires clause.

Definition at line 513 of file lazy.h.

◆ memory_consumption()

template<typename T >
std::size_t Lazy< T >::memory_consumption ( ) const

Compute the memory consumption of this structure.

Definition at line 525 of file lazy.h.

Member Data Documentation

◆ object

template<typename T >
std::optional<T> Lazy< T >::object
mutableprivate

The lazily initialized object stored as a std::optional<T>.

Definition at line 285 of file lazy.h.

◆ object_is_initialized

template<typename T >
std::atomic<bool> Lazy< T >::object_is_initialized
mutableprivate

An atomic bool used for checking whether the object is initialized in a thread-safe manner.

Definition at line 292 of file lazy.h.

◆ initialization_mutex

template<typename T >
Threads::Mutex Lazy< T >::initialization_mutex
mutableprivate

A mutex used for protecting the initialization of the object.

Definition at line 298 of file lazy.h.


The documentation for this class was generated from the following file: