Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-24 09:01:49

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/OpaqueId.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <cstddef>
0010 #include <functional>
0011 #include <type_traits>
0012 
0013 #include "Assert.hh"
0014 #include "Macros.hh"
0015 #include "Types.hh"
0016 
0017 namespace celeritas
0018 {
0019 //---------------------------------------------------------------------------//
0020 /*!
0021  * Type-safe index for accessing an array or collection of data.
0022  *
0023  * \tparam ValueT Type of each item in an array
0024  * \tparam SizeT Unsigned integer index
0025  *
0026  * It's common for classes and functions to take multiple indices, especially
0027  * for O(1) indexing for performance. By annotating these values with a type,
0028  * we give them semantic meaning, and we gain compile-time type safety.
0029  *
0030  * If this class is used for indexing into an array, then \c ValueT argument
0031  * should usually be the value type of the array:
0032  * <code>Foo operator[](OpaqueId<Foo>)</code>
0033  *
0034  * An \c OpaqueId object evaluates to \c true if it has a value, or \c false if
0035  * it does not (i.e., it has an "invalid" value).
0036  *
0037  * See also \c id_cast below for checked construction of OpaqueIds from generic
0038  * integer values (avoid compile-time warnings or errors from signed/truncated
0039  * integers).
0040  */
0041 template<class ValueT, class SizeT = ::celeritas::size_type>
0042 class OpaqueId
0043 {
0044     static_assert(std::is_unsigned_v<SizeT> && !std::is_same_v<SizeT, bool>,
0045                   "SizeT must be unsigned.");
0046 
0047   public:
0048     //!@{
0049     //! \name Type aliases
0050     using value_type = ValueT;
0051     using size_type = SizeT;
0052     //!@}
0053 
0054   public:
0055     //! Default to invalid state
0056     CELER_CONSTEXPR_FUNCTION OpaqueId() : value_(OpaqueId::invalid_value()) {}
0057 
0058     //! Construct explicitly with stored value
0059     explicit CELER_CONSTEXPR_FUNCTION OpaqueId(size_type index) : value_(index)
0060     {
0061     }
0062 
0063     //! Whether this ID is in a valid (assigned) state
0064     explicit CELER_CONSTEXPR_FUNCTION operator bool() const
0065     {
0066         return value_ != invalid_value();
0067     }
0068 
0069     //! Pre-increment of the ID
0070     CELER_FUNCTION OpaqueId& operator++()
0071     {
0072         CELER_EXPECT(*this);
0073         value_ += 1;
0074         return *this;
0075     }
0076 
0077     //! Post-increment of the ID
0078     CELER_FUNCTION OpaqueId operator++(int)
0079     {
0080         OpaqueId old{*this};
0081         ++*this;
0082         return old;
0083     }
0084 
0085     //! Pre-decrement of the ID
0086     CELER_FUNCTION OpaqueId& operator--()
0087     {
0088         CELER_EXPECT(*this && value_ > 0);
0089         value_ -= 1;
0090         return *this;
0091     }
0092 
0093     //! Post-decrement of the ID
0094     CELER_FUNCTION OpaqueId operator--(int)
0095     {
0096         OpaqueId old{*this};
0097         --*this;
0098         return old;
0099     }
0100 
0101     //! Get the ID's value
0102     CELER_FORCEINLINE_FUNCTION size_type get() const
0103     {
0104         CELER_EXPECT(*this);
0105         return value_;
0106     }
0107 
0108     //! Get the value without checking for validity (atypical)
0109     CELER_CONSTEXPR_FUNCTION size_type unchecked_get() const { return value_; }
0110 
0111     //! Access the underlying data for more efficient loading from memory
0112     CELER_CONSTEXPR_FUNCTION size_type const* data() const { return &value_; }
0113 
0114   private:
0115     size_type value_;
0116 
0117     //// IMPLEMENTATION FUNCTIONS ////
0118 
0119     //! Value indicating the ID is not assigned
0120     static CELER_CONSTEXPR_FUNCTION size_type invalid_value()
0121     {
0122         return static_cast<size_type>(-1);
0123     }
0124 };
0125 
0126 //---------------------------------------------------------------------------//
0127 // FREE FUNCTIONS
0128 //---------------------------------------------------------------------------//
0129 /*!
0130  * Safely create an OpaqueId from an integer of any type.
0131  *
0132  * This asserts that the integer is in the \em valid range of the target ID
0133  * type, and casts to it.
0134  *
0135  * \note The value cannot be the underlying "invalid" value, i.e.
0136  * <code> static_cast<FooId>(FooId{}.unchecked_get()) </code> will not work.
0137  */
0138 template<class IdT, class T>
0139 inline CELER_FUNCTION IdT id_cast(T value) noexcept(!CELERITAS_DEBUG)
0140 {
0141     static_assert(std::is_integral_v<T>);
0142     if constexpr (!std::is_unsigned_v<T>)
0143     {
0144         CELER_EXPECT(value >= 0);
0145     }
0146 
0147     using IdSize = typename IdT::size_type;
0148     if constexpr (!std::is_same_v<T, IdSize>)
0149     {
0150         // Check that value is within the integer range [0, N-1)
0151         using U = std::common_type_t<IdSize, std::make_unsigned_t<T>>;
0152         CELER_EXPECT(static_cast<U>(value)
0153                      < static_cast<U>(IdT{}.unchecked_get()));
0154     }
0155     else
0156     {
0157         // Check that value is *not* the invalid value
0158         CELER_EXPECT(value != IdT{}.unchecked_get());
0159     }
0160 
0161     return IdT{static_cast<IdSize>(value)};
0162 }
0163 
0164 //---------------------------------------------------------------------------//
0165 #define CELER_DEFINE_OPAQUEID_CMP(TOKEN)                             \
0166     template<class V, class S>                                       \
0167     CELER_CONSTEXPR_FUNCTION bool operator TOKEN(OpaqueId<V, S> lhs, \
0168                                                  OpaqueId<V, S> rhs) \
0169     {                                                                \
0170         return lhs.unchecked_get() TOKEN rhs.unchecked_get();        \
0171     }
0172 
0173 //!@{
0174 //! Comparison for OpaqueId
0175 CELER_DEFINE_OPAQUEID_CMP(==)
0176 CELER_DEFINE_OPAQUEID_CMP(!=)
0177 CELER_DEFINE_OPAQUEID_CMP(<)
0178 CELER_DEFINE_OPAQUEID_CMP(>)
0179 CELER_DEFINE_OPAQUEID_CMP(<=)
0180 CELER_DEFINE_OPAQUEID_CMP(>=)
0181 //!@}
0182 
0183 #undef CELER_DEFINE_OPAQUEID_CMP
0184 
0185 //---------------------------------------------------------------------------//
0186 //! Allow less-than comparison with *integer* for container comparison
0187 template<class V, class S, class U>
0188 CELER_CONSTEXPR_FUNCTION bool operator<(OpaqueId<V, S> lhs, U rhs)
0189 {
0190     // Cast to RHS
0191     return lhs && (U(lhs.unchecked_get()) < rhs);
0192 }
0193 
0194 //---------------------------------------------------------------------------//
0195 //! Allow less-than-equal comparison with *integer* for container comparison
0196 template<class V, class S, class U>
0197 CELER_CONSTEXPR_FUNCTION bool operator<=(OpaqueId<V, S> lhs, U rhs)
0198 {
0199     // Cast to RHS
0200     return lhs && (U(lhs.unchecked_get()) <= rhs);
0201 }
0202 
0203 //---------------------------------------------------------------------------//
0204 //! Get the distance between two opaque IDs
0205 template<class V, class S>
0206 inline CELER_FUNCTION S operator-(OpaqueId<V, S> self, OpaqueId<V, S> other)
0207 {
0208     CELER_EXPECT(self);
0209     CELER_EXPECT(other);
0210     return self.unchecked_get() - other.unchecked_get();
0211 }
0212 
0213 //---------------------------------------------------------------------------//
0214 //! Increment an opaque ID by an offset
0215 template<class V, class S>
0216 inline CELER_FUNCTION OpaqueId<V, S>
0217 operator+(OpaqueId<V, S> id, std::make_signed_t<S> offset)
0218 {
0219     CELER_EXPECT(id);
0220     CELER_EXPECT(offset >= 0 || static_cast<S>(-offset) <= id.unchecked_get());
0221     return OpaqueId<V, S>{id.unchecked_get() + static_cast<S>(offset)};
0222 }
0223 
0224 //---------------------------------------------------------------------------//
0225 //! Decrement an opaque ID by an offset
0226 template<class V, class S>
0227 inline CELER_FUNCTION OpaqueId<V, S>
0228 operator-(OpaqueId<V, S> id, std::make_signed_t<S> offset)
0229 {
0230     CELER_EXPECT(id);
0231     CELER_EXPECT(offset <= 0 || static_cast<S>(offset) <= id.unchecked_get());
0232     return OpaqueId<V, S>{id.unchecked_get() - static_cast<S>(offset)};
0233 }
0234 
0235 //---------------------------------------------------------------------------//
0236 }  // namespace celeritas
0237 
0238 //---------------------------------------------------------------------------//
0239 //! \cond
0240 namespace std
0241 {
0242 //! Specialization for std::hash for unordered storage.
0243 template<class V, class S>
0244 struct hash<celeritas::OpaqueId<V, S>>
0245 {
0246     std::size_t operator()(celeritas::OpaqueId<V, S> const& id) const noexcept
0247     {
0248         return std::hash<S>()(id.unchecked_get());
0249     }
0250 };
0251 }  // namespace std
0252 //! \endcond