LCOV - code coverage report
Current view: top level - lib/support - Variant.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 46 56 82.1 %
Date: 2024-02-15 08:20:41 Functions: 62 130 47.7 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2021 Project CHIP Authors
       4             :  *    All rights reserved.
       5             :  *
       6             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7             :  *    you may not use this file except in compliance with the License.
       8             :  *    You may obtain a copy of the License at
       9             :  *
      10             :  *        http://www.apache.org/licenses/LICENSE-2.0
      11             :  *
      12             :  *    Unless required by applicable law or agreed to in writing, software
      13             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15             :  *    See the License for the specific language governing permissions and
      16             :  *    limitations under the License.
      17             :  */
      18             : 
      19             : #pragma once
      20             : 
      21             : #include <lib/core/CHIPCore.h>
      22             : 
      23             : #include <algorithm>
      24             : #include <new>
      25             : #include <tuple>
      26             : #include <type_traits>
      27             : #include <typeinfo>
      28             : #include <utility>
      29             : 
      30             : #include <lib/core/InPlace.h>
      31             : 
      32             : namespace chip {
      33             : 
      34             : namespace VariantInternal {
      35             : 
      36             : template <std::size_t Index, typename... Ts>
      37             : struct VariantCurry;
      38             : 
      39             : template <std::size_t Index, typename T, typename... Ts>
      40             : struct VariantCurry<Index, T, Ts...>
      41             : {
      42        7211 :     inline static void Destroy(std::size_t id, void * data)
      43             :     {
      44        7211 :         if (id == Index)
      45          53 :             reinterpret_cast<T *>(data)->~T();
      46             :         else
      47        7158 :             VariantCurry<Index + 1, Ts...>::Destroy(id, data);
      48        7211 :     }
      49             : 
      50        1977 :     inline static void Move(std::size_t that_t, void * that_v, void * this_v)
      51             :     {
      52        1977 :         if (that_t == Index)
      53         115 :             new (this_v) T(std::move(*reinterpret_cast<T *>(that_v)));
      54             :         else
      55        1862 :             VariantCurry<Index + 1, Ts...>::Move(that_t, that_v, this_v);
      56        1977 :     }
      57             : 
      58         116 :     inline static void Copy(std::size_t that_t, const void * that_v, void * this_v)
      59             :     {
      60         116 :         if (that_t == Index)
      61             :         {
      62          67 :             new (this_v) T(*reinterpret_cast<const T *>(that_v));
      63             :         }
      64             :         else
      65             :         {
      66          49 :             VariantCurry<Index + 1, Ts...>::Copy(that_t, that_v, this_v);
      67             :         }
      68         116 :     }
      69             : 
      70             :     inline static bool Equal(std::size_t type_t, const void * that_v, const void * this_v)
      71             :     {
      72             :         if (type_t == Index)
      73             :         {
      74             :             return *reinterpret_cast<const T *>(this_v) == *reinterpret_cast<const T *>(that_v);
      75             :         }
      76             : 
      77             :         return VariantCurry<Index + 1, Ts...>::Equal(type_t, that_v, this_v);
      78             :     }
      79             : };
      80             : 
      81             : template <std::size_t Index>
      82             : struct VariantCurry<Index>
      83             : {
      84        3235 :     inline static void Destroy(std::size_t id, void * data) {}
      85         827 :     inline static void Move(std::size_t that_t, void * that_v, void * this_v) {}
      86           0 :     inline static void Copy(std::size_t that_t, const void * that_v, void * this_v) {}
      87             :     inline static bool Equal(std::size_t type_t, const void * that_v, const void * this_v)
      88             :     {
      89             :         VerifyOrDie(false);
      90             :         return false;
      91             :     }
      92             : };
      93             : 
      94             : template <typename T, typename TupleType>
      95             : class TupleIndexOfType
      96             : {
      97             : private:
      98             :     template <std::size_t Index>
      99             :     static constexpr
     100             :         typename std::enable_if<std::is_same<T, typename std::tuple_element<Index, TupleType>::type>::value, std::size_t>::type
     101             :         calculate()
     102             :     {
     103             :         return Index;
     104             :     }
     105             : 
     106             :     template <std::size_t Index>
     107             :     static constexpr
     108             :         typename std::enable_if<!std::is_same<T, typename std::tuple_element<Index, TupleType>::type>::value, std::size_t>::type
     109             :         calculate()
     110             :     {
     111             :         return calculate<Index + 1>();
     112             :     }
     113             : 
     114             : public:
     115             :     static constexpr std::size_t value = calculate<0>();
     116             : };
     117             : 
     118             : } // namespace VariantInternal
     119             : 
     120             : /**
     121             :  * @brief
     122             :  *   Represents a type-safe union. An instance of Variant at any given time either holds a value of one of its
     123             :  *   alternative types, or no value.
     124             :  *
     125             :  *   Example:
     126             :  *     struct Type1 {};
     127             :  *
     128             :  *     struct Type2 {};
     129             :  *
     130             :  *     Variant<Type1, Type2> v;
     131             :  *     v.Set<Type1>(); // v contains Type1
     132             :  *     Type1 o1 = v.Get<Type1>();
     133             :  */
     134             : template <typename... Ts>
     135             : struct Variant
     136             : {
     137             : private:
     138             :     static constexpr std::size_t kDataSize    = std::max({ sizeof(Ts)... });
     139             :     static constexpr std::size_t kDataAlign   = std::max({ alignof(Ts)... });
     140             :     static constexpr std::size_t kInvalidType = SIZE_MAX;
     141             : 
     142             :     using Data  = typename std::aligned_storage<kDataSize, kDataAlign>::type;
     143             :     using Curry = VariantInternal::VariantCurry<0, Ts...>;
     144             : 
     145             :     std::size_t mTypeId;
     146             :     Data mData;
     147             : 
     148             : public:
     149        1773 :     Variant() : mTypeId(kInvalidType) {}
     150             : 
     151             :     template <typename T, class... Args>
     152           0 :     constexpr explicit Variant(InPlaceTemplateType<T>, Args &&... args) :
     153           0 :         mTypeId(VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value)
     154             :     {
     155           0 :         new (&mData) T(std::forward<Args>(args)...);
     156           0 :     }
     157             : 
     158          43 :     Variant(const Variant<Ts...> & that) : mTypeId(that.mTypeId) { Curry::Copy(that.mTypeId, &that.mData, &mData); }
     159             : 
     160           0 :     Variant(Variant<Ts...> && that) : mTypeId(that.mTypeId)
     161             :     {
     162           0 :         Curry::Move(that.mTypeId, &that.mData, &mData);
     163           0 :         Curry::Destroy(that.mTypeId, &that.mData);
     164           0 :         that.mTypeId = kInvalidType;
     165           0 :     }
     166             : 
     167          24 :     Variant<Ts...> & operator=(const Variant<Ts...> & that)
     168             :     {
     169          24 :         Curry::Destroy(mTypeId, &mData);
     170          24 :         mTypeId = that.mTypeId;
     171          24 :         Curry::Copy(that.mTypeId, &that.mData, &mData);
     172          24 :         return *this;
     173             :     }
     174             : 
     175         980 :     Variant<Ts...> & operator=(Variant<Ts...> && that)
     176             :     {
     177         980 :         Curry::Destroy(mTypeId, &mData);
     178         980 :         mTypeId = that.mTypeId;
     179         980 :         Curry::Move(that.mTypeId, &that.mData, &mData);
     180         980 :         Curry::Destroy(that.mTypeId, &that.mData);
     181         980 :         that.mTypeId = kInvalidType;
     182         980 :         return *this;
     183             :     }
     184             : 
     185             :     bool operator==(const Variant & other) const
     186             :     {
     187             :         return GetType() == other.GetType() && (!Valid() || Curry::Equal(mTypeId, &other.mData, &mData));
     188             :     }
     189             : 
     190             :     template <typename T>
     191        6877 :     bool Is() const
     192             :     {
     193        6877 :         return (mTypeId == VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value);
     194             :     }
     195             : 
     196             :     std::size_t GetType() const { return mTypeId; }
     197             : 
     198        1184 :     bool Valid() const { return (mTypeId != kInvalidType); }
     199             : 
     200             :     template <typename T, typename... Args>
     201             :     static Variant<Ts...> Create(Args &&... args)
     202             :     {
     203             :         return Variant<Ts...>(InPlaceTemplate<T>, std::forward<Args>(args)...);
     204             :     }
     205             : 
     206             :     template <typename T, typename... Args>
     207         166 :     void Set(Args &&... args)
     208             :     {
     209         166 :         Curry::Destroy(mTypeId, &mData);
     210         166 :         new (&mData) T(std::forward<Args>(args)...);
     211         166 :         mTypeId = VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value;
     212         166 :     }
     213             : 
     214             :     template <typename T>
     215         641 :     T & Get()
     216             :     {
     217         641 :         VerifyOrDie((mTypeId == VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value));
     218         641 :         return *reinterpret_cast<T *>(&mData);
     219             :     }
     220             : 
     221             :     template <typename T>
     222        1496 :     const T & Get() const
     223             :     {
     224        1496 :         VerifyOrDie((mTypeId == VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value));
     225        1496 :         return *reinterpret_cast<const T *>(&mData);
     226             :     }
     227             : 
     228             :     // Ideally we would suppress this from within Optional.h, where this false positive is coming from. That said suppressing
     229             :     // here is okay since mTypeId would seemingly only be uninitialized when Variant is in a union.
     230             :     //
     231             :     // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage): Only in a false positive is mTypeId uninitialized.
     232        1683 :     ~Variant() { Curry::Destroy(mTypeId, &mData); }
     233             : };
     234             : 
     235             : } // namespace chip

Generated by: LCOV version 1.14