Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-18 09:09:36

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file corecel/cont/InitializedValue.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <type_traits>
0010 #include <utility>
0011 
0012 namespace celeritas
0013 {
0014 namespace detail
0015 {
0016 //---------------------------------------------------------------------------//
0017 template<class T>
0018 struct DefaultFinalize
0019 {
0020     void operator()(T&) const noexcept {}
0021 };
0022 //---------------------------------------------------------------------------//
0023 }  // namespace detail
0024 
0025 //---------------------------------------------------------------------------//
0026 /*!
0027  * Clear the value of the object on initialization and moving.
0028  *
0029  * This helper class is used to simplify the "rule of 5" for classes that have
0030  * to treat one member data specially but can use default assign/construct for
0031  * the other elements. The default behavior is just to default-initialize when
0032  * assigning and clearing the RHS when moving; this is useful for handling
0033  * managed memory. The *finalizer* is useful when the type has a
0034  * destructor-type method that has to be called before clearing it.
0035  */
0036 template<class T, class Finalizer = detail::DefaultFinalize<T>>
0037 class InitializedValue
0038 {
0039   private:
0040     static constexpr bool ne_finalize_
0041         = noexcept(std::declval<Finalizer>()(std::declval<T&>()));
0042 
0043   public:
0044     //!@{
0045     //! \name Constructors
0046 
0047     //! Construct implicitly with default value
0048     InitializedValue() = default;
0049     //! Implicit construct from lvalue
0050     InitializedValue(T const& value) : value_(value) {}
0051     //! Implicit construct from lvalue and finalizer
0052     InitializedValue(T const& value, Finalizer fin)
0053         : value_(value), fin_(std::move(fin))
0054     {
0055     }
0056     //! Implicit construct from rvalue
0057     InitializedValue(T&& value) : value_(std::move(value)) {}
0058     //! Implicit construct from value type and finalizer
0059     InitializedValue(T&& value, Finalizer fin)
0060         : value_(std::move(value)), fin_(std::move(fin))
0061     {
0062     }
0063 
0064     //! Default copy constructor
0065     InitializedValue(InitializedValue const&) noexcept(
0066         std::is_nothrow_copy_constructible_v<T>)
0067         = default;
0068 
0069     //! Clear other value on move construct
0070     InitializedValue(InitializedValue&& other) noexcept(
0071         std::is_nothrow_move_constructible_v<T>)
0072         : value_(std::exchange(other.value_, {}))
0073     {
0074     }
0075 
0076     ~InitializedValue() = default;
0077 
0078     //!@}
0079     //!@{
0080     //! \name Assignment
0081 
0082     //! Finalize our value when assigning
0083     InitializedValue& operator=(InitializedValue const& other) noexcept(
0084         ne_finalize_ && std::is_nothrow_copy_assignable_v<T>)
0085     {
0086         fin_(value_);
0087         value_ = other.value_;
0088         fin_ = other.fin_;
0089         return *this;
0090     }
0091 
0092     //! Clear other value on move assign
0093     InitializedValue& operator=(InitializedValue&& other) noexcept(
0094         ne_finalize_ && std::is_nothrow_move_assignable_v<T>)
0095     {
0096         fin_(value_);
0097         value_ = std::exchange(other.value_, {});
0098         fin_ = std::exchange(other.fin_, {});
0099         return *this;
0100     }
0101 
0102     //! Implicit assign from type
0103     InitializedValue&
0104     operator=(T const& value) noexcept(ne_finalize_
0105                                        && std::is_nothrow_copy_assignable_v<T>)
0106     {
0107         fin_(value_);
0108         value_ = value;
0109         return *this;
0110     }
0111 
0112     //! Swap with another value
0113     void swap(InitializedValue& other) noexcept
0114     {
0115         using std::swap;
0116         swap(other.value_, value_);
0117         swap(other.fin_, fin_);
0118     }
0119 
0120     //!@}
0121     //!@{
0122     //! \name Conversion
0123 
0124     //! Implicit reference to stored value
0125     operator T const&() const noexcept { return value_; }
0126     operator T&() noexcept { return value_; }
0127 
0128     //! Explicit reference to stored value
0129     T const& value() const& { return value_; }
0130     T& value() & { return value_; }
0131     T&& value() && { return value_; }
0132 
0133     //!@}
0134 
0135     //!@{
0136     //! Access finalizer
0137     Finalizer const& finalizer() const { return fin_; }
0138     void finalizer(Finalizer fin) { fin_ = std::move(fin); }
0139     //!@}
0140 
0141   private:
0142     T value_{};
0143     Finalizer fin_{};  // TODO: if C++20, [[no_unique_address]]
0144 };
0145 
0146 //---------------------------------------------------------------------------//
0147 }  // namespace celeritas