Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:54:46

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