differential code coverage report with master
Current view: top level - include/xlnt/utils - optional.hpp (source / functions) Coverage Total Hit UBC CBC
Current: coverage.info Lines: 98.9 % 88 87 1 87
Current Date: 2025-12-15 23:01:28 Functions: 91.7 % 544 499 45 499
Baseline: coverage_master.info Branches: 69.2 % 39 27 24 54
Baseline Date: 2025-12-15 23:01:27

             Branch data    TLA  Line data    Source code
       1                 :                : // Copyright (c) 2016-2022 Thomas Fussell
       2                 :                : // Copyright (c) 2024-2025 xlnt-community
       3                 :                : //
       4                 :                : // Permission is hereby granted, free of charge, to any person obtaining a copy
       5                 :                : // of this software and associated documentation files (the "Software"), to deal
       6                 :                : // in the Software without restriction, including without limitation the rights
       7                 :                : // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8                 :                : // copies of the Software, and to permit persons to whom the Software is
       9                 :                : // furnished to do so, subject to the following conditions:
      10                 :                : //
      11                 :                : // The above copyright notice and this permission notice shall be included in
      12                 :                : // all copies or substantial portions of the Software.
      13                 :                : //
      14                 :                : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15                 :                : // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16                 :                : // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17                 :                : // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18                 :                : // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19                 :                : // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      20                 :                : // THE SOFTWARE
      21                 :                : //
      22                 :                : // @license: http://www.opensource.org/licenses/mit-license.php
      23                 :                : // @author: see AUTHORS file
      24                 :                : 
      25                 :                : #pragma once
      26                 :                : 
      27                 :                : #include "xlnt/utils/exceptions.hpp"
      28                 :                : #include "xlnt/utils/numeric.hpp"
      29                 :                : #include <type_traits>
      30                 :                : #include <cstring>
      31                 :                : 
      32                 :                : namespace xlnt {
      33                 :                : 
      34                 :                : /// <summary>
      35                 :                : /// Many settings in xlnt are allowed to not have a value set. This class
      36                 :                : /// encapsulates a value which may or may not be set. Memory is allocated
      37                 :                : /// within the optional class.
      38                 :                : /// </summary>
      39                 :                : template <typename T>
      40                 :                : class optional
      41                 :                : {
      42                 :                : #if ((defined(_MSC_VER) && _MSC_VER <= 1900) || (defined(__GNUC__) && __GNUC__ < 5))
      43                 :                : // Disable enhanced type checking on Visual Studio <= 2015 and GCC <5
      44                 :                : #define XLNT_NOEXCEPT_VALUE_COMPAT(...) (false)
      45                 :                : #else
      46                 :                : #define XLNT_NOEXCEPT_VALUE_COMPAT(...) (__VA_ARGS__)
      47                 :                :     using ctor_copy_T_noexcept = typename std::conditional<std::is_nothrow_copy_constructible<T>{}, std::true_type, std::false_type>::type;
      48                 :                :     using ctor_move_T_noexcept = typename std::conditional<std::is_nothrow_move_constructible<T>{}, std::true_type, std::false_type>::type;
      49                 :                :     using copy_ctor_noexcept = ctor_copy_T_noexcept;
      50                 :                :     using move_ctor_noexcept = ctor_move_T_noexcept;
      51                 :                :     using set_copy_noexcept_t = typename std::conditional<std::is_nothrow_copy_constructible<T>{} && std::is_nothrow_assignable<T, T>{}, std::true_type, std::false_type>::type;
      52                 :                :     using set_move_noexcept_t = typename std::conditional<std::is_nothrow_move_constructible<T>{} && std::is_nothrow_move_assignable<T>{}, std::true_type, std::false_type>::type;
      53                 :                :     using clear_noexcept_t = typename std::conditional<std::is_nothrow_destructible<T>{}, std::true_type, std::false_type>::type;
      54                 :                : #endif
      55                 :                :     /// <summary>
      56                 :                :     /// Default equality operation, just uses operator==
      57                 :                :     /// </summary>
      58                 :                :     template <typename U = T, typename std::enable_if<!std::is_floating_point<U>::value>::type * = nullptr>
      59                 :CBC        9680 :     constexpr bool compare_equal(const U &lhs, const U &rhs) const
      60                 :                :     {
      61                 :           9680 :         return lhs == rhs;
      62                 :                :     }
      63                 :                : 
      64                 :                :     /// <summary>
      65                 :                :     /// equality operation for floating point numbers. Provides "fuzzy" equality
      66                 :                :     /// </summary>
      67                 :                :     template <typename U = T, typename std::enable_if<std::is_floating_point<U>::value>::type * = nullptr>
      68                 :           6186 :     constexpr bool compare_equal(const U &lhs, const U &rhs) const
      69                 :                :     {
      70                 :           6186 :         return detail::float_equals(lhs, rhs);
      71                 :                :     }
      72                 :                : 
      73                 :                : public:
      74                 :                :     /// <summary>
      75                 :                :     /// Default contructor. is_set() will be false initially.
      76                 :                :     /// </summary>
      77                 :         494660 :     optional() noexcept
      78                 :         494660 :         : has_value_(false),
      79                 :         494660 :         storage_{}
      80                 :                :     {
      81                 :         494660 :     }
      82                 :                : 
      83                 :                :     /// <summary>
      84                 :                :     /// Constructs this optional with a value.
      85                 :                :     /// noexcept if T copy ctor is noexcept
      86                 :                :     /// </summary>
      87                 :             26 :     optional(const T &value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(ctor_copy_T_noexcept{}))
      88                 :             26 :         : has_value_(true)
      89                 :                :     {
      90      [ +  -  - ]:             26 :         new (&storage_) T(value);
      91                 :             26 :     }
      92                 :                : 
      93                 :                :     /// <summary>
      94                 :                :     /// Constructs this optional with a value.
      95                 :                :     /// noexcept if T move ctor is noexcept
      96                 :                :     /// </summary>
      97                 :           5427 :     optional(T &&value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(ctor_move_T_noexcept{}))
      98                 :           5427 :         : has_value_(true)
      99                 :                :     {
     100                 :           5427 :         new (&storage_) T(std::move(value));
     101                 :           5427 :     }
     102                 :                : 
     103                 :                :     /// <summary>
     104                 :                :     /// Copy constructs this optional from other
     105                 :                :     /// noexcept if T copy ctor is noexcept
     106                 :                :     /// </summary>
     107                 :         139989 :     optional(const optional &other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(copy_ctor_noexcept{}))
     108                 :         139989 :         : has_value_(other.has_value_)
     109                 :                :     {
     110         [ +  + ]:         139989 :         if (has_value_)
     111                 :                :         {
     112      [ +  -  - ]:          35001 :             new (&storage_) T(other.value_ref());
     113                 :                :         }
     114                 :                :         else
     115                 :                :         {
     116                 :         104988 :             std::memset(storage_, 0, sizeof(T));
     117                 :                :         }
     118                 :         139989 :     }
     119                 :                : 
     120                 :                :     /// <summary>
     121                 :                :     /// Move constructs this optional from other. Clears the value from other if set
     122                 :                :     /// noexcept if T move ctor is noexcept
     123                 :                :     /// </summary>
     124                 :          79218 :     optional(optional &&other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(move_ctor_noexcept{}))
     125                 :          79218 :         : has_value_(other.has_value_)
     126                 :                :     {
     127         [ +  + ]:          79218 :         if (has_value_)
     128                 :                :         {
     129      [ +  -  - ]:          16130 :             new (&storage_) T(std::move(other.value_ref()));
     130                 :          16130 :             other.clear();
     131                 :                :         }
     132                 :                :         else
     133                 :                :         {
     134                 :          63088 :             std::memset(storage_, 0, sizeof(T));
     135                 :                :         }
     136                 :          79218 :     }
     137                 :                : 
     138                 :                :     /// <summary>
     139                 :                :     /// Copy assignment of this optional from other
     140                 :                :     /// noexcept if set and clear are noexcept for T&
     141                 :                :     /// </summary>
     142                 :         228518 :     optional &operator=(const optional &other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{} && clear_noexcept_t{}))
     143                 :                :     {
     144         [ +  + ]:         228518 :         if (other.has_value_)
     145                 :                :         {
     146                 :         196283 :             set(other.value_ref());
     147                 :                :         }
     148                 :                :         else
     149                 :                :         {
     150                 :          32235 :             clear();
     151                 :                :         }
     152                 :         228518 :         return *this;
     153                 :                :     }
     154                 :                : 
     155                 :                :     /// <summary>
     156                 :                :     /// Move assignment of this optional from other
     157                 :                :     /// noexcept if set and clear are noexcept for T&&
     158                 :                :     /// </summary>
     159                 :           7694 :     optional &operator=(optional &&other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{} && clear_noexcept_t{}))
     160                 :                :     {
     161         [ +  + ]:           7694 :         if (other.has_value_)
     162                 :                :         {
     163                 :           5780 :             set(std::move(other.value_ref()));
     164                 :           5780 :             other.clear();
     165                 :                :         }
     166                 :                :         else
     167                 :                :         {
     168                 :           1914 :             clear();
     169                 :                :         }
     170                 :           7694 :         return *this;
     171                 :                :     }
     172                 :                : 
     173                 :                :     /// <summary>
     174                 :                :     /// Destructor cleans up the T instance if set
     175                 :                :     /// </summary>
     176                 :         719320 :     ~optional() noexcept // note:: unconditional because msvc freaks out otherwise
     177                 :                :     {
     178                 :         719320 :         clear();
     179                 :         719320 :     }
     180                 :                : 
     181                 :                :     /// <summary>
     182                 :                :     /// Returns true if this object currently has a value set. This should
     183                 :                :     /// be called before accessing the value with optional::get().
     184                 :                :     /// </summary>
     185                 :         265331 :     bool is_set() const noexcept
     186                 :                :     {
     187                 :         265331 :         return has_value_;
     188                 :                :     }
     189                 :                : 
     190                 :                :     /// <summary>
     191                 :                :     /// Copies the value into the stored value
     192                 :                :     /// </summary>
     193                 :         395223 :     void set(const T &value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{}))
     194                 :                :     {
     195         [ +  + ]:         395223 :         if (has_value_)
     196                 :                :         {
     197                 :           5014 :             value_ref() = value;
     198                 :                :         }
     199                 :                :         else
     200                 :                :         {
     201      [ +  -  - ]:         390209 :             new (&storage_) T(value);
     202                 :         390209 :             has_value_ = true;
     203                 :                :         }
     204                 :         395223 :     }
     205                 :                : 
     206                 :                :     /// <summary>
     207                 :                :     /// Moves the value into the stored value
     208                 :                :     /// </summary>
     209                 :          23607 :     void set(T &&value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{}))
     210                 :                :     {
     211                 :                :         // note seperate overload for two reasons (as opposed to perfect forwarding)
     212                 :                :         // 1. have to deal with implicit conversions internally with perfect forwarding
     213                 :                :         // 2. have to deal with the noexcept specfiers for all the different variations
     214                 :                :         // overload is just far and away the simpler solution
     215         [ +  + ]:          23607 :         if (has_value_)
     216                 :                :         {
     217                 :           1225 :             value_ref() = std::move(value);
     218                 :                :         }
     219                 :                :         else
     220                 :                :         {
     221      [ +  -  - ]:          22382 :             new (&storage_) T(std::move(value));
     222                 :          22382 :             has_value_ = true;
     223                 :                :         }
     224                 :          23607 :     }
     225                 :                : 
     226                 :                :     /// <summary>
     227                 :                :     /// Assignment operator overload. Equivalent to setting the value using optional::set.
     228                 :                :     /// </summary>
     229                 :         198729 :     optional &operator=(const T &rhs) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{}))
     230                 :                :     {
     231                 :         198729 :         set(rhs);
     232                 :         198729 :         return *this;
     233                 :                :     }
     234                 :                : 
     235                 :                :     /// <summary>
     236                 :                :     /// Assignment operator overload. Equivalent to setting the value using optional::set.
     237                 :                :     /// </summary>
     238                 :          17215 :     optional &operator=(T &&rhs) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{}))
     239                 :                :     {
     240                 :          17215 :         set(std::move(rhs));
     241                 :          17215 :         return *this;
     242                 :                :     }
     243                 :                : 
     244                 :                :     /// <summary>
     245                 :                :     /// After this is called, is_set() will return false until a new value is provided.
     246                 :                :     /// </summary>
     247                 :         784570 :     void clear() noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(clear_noexcept_t{}))
     248                 :                :     {
     249         [ +  + ]:         784570 :         if (has_value_)
     250                 :                :         {
     251                 :         469175 :             reinterpret_cast<T *>(&storage_)->~T();
     252                 :                :         }
     253                 :         784570 :         has_value_ = false;
     254                 :         784570 :     }
     255                 :                : 
     256                 :                :     /// <summary>
     257                 :                :     /// Gets the value. If no value has been initialized in this object,
     258                 :                :     /// an xlnt::invalid_attribute exception will be thrown.
     259                 :                :     /// </summary>
     260                 :         197683 :     T &get()
     261                 :                :     {
     262         [ +  + ]:         197683 :         if (!has_value_)
     263                 :                :         {
     264            [ + ]:              3 :             throw invalid_attribute();
     265                 :                :         }
     266                 :                : 
     267                 :         197680 :         return value_ref();
     268                 :                :     }
     269                 :                : 
     270                 :                :     /// <summary>
     271                 :                :     /// Gets the value. If no value has been initialized in this object,
     272                 :                :     /// an xlnt::invalid_attribute exception will be thrown.
     273                 :                :     /// </summary>
     274                 :          16471 :     const T &get() const
     275                 :                :     {
     276         [ -  + ]:          16471 :         if (!has_value_)
     277                 :                :         {
     278            [ # ]:UBC           0 :             throw invalid_attribute();
     279                 :                :         }
     280                 :                : 
     281                 :CBC       16471 :         return value_ref();
     282                 :                :     }
     283                 :                : 
     284                 :                :     /// <summary>
     285                 :                :     /// Returns true if neither this nor other have a value
     286                 :                :     /// or both have a value and those values are equal according to
     287                 :                :     /// their equality operator.
     288                 :                :     /// </summary>
     289                 :          28705 :     bool operator==(const optional<T> &other) const noexcept
     290                 :                :     {
     291         [ +  + ]:          28705 :         if (has_value_ != other.has_value_)
     292                 :                :         {
     293                 :             80 :             return false;
     294                 :                :         }
     295         [ +  + ]:          28625 :         if (!has_value_)
     296                 :                :         {
     297                 :          12759 :             return true;
     298                 :                :         }
     299                 :                :         // equality is overloaded to provide fuzzy equality when T is a fp number
     300                 :          15866 :         return compare_equal(value_ref(), other.value_ref());
     301                 :                :     }
     302                 :                : 
     303                 :                :     /// <summary>
     304                 :                :     /// Returns false if neither this nor other have a value
     305                 :                :     /// or both have a value and those values are equal according to
     306                 :                :     /// their equality operator.
     307                 :                :     /// </summary>
     308                 :             80 :     bool operator!=(const optional<T> &other) const noexcept
     309                 :                :     {
     310                 :             80 :         return !operator==(other);
     311                 :                :     }
     312                 :                : 
     313                 :                : private:
     314                 :                :     // helpers for getting a T out of storage
     315                 :         225829 :     T &value_ref() noexcept
     316                 :                :     {
     317                 :         225829 :         return *reinterpret_cast<T *>(&storage_);
     318                 :                :     }
     319                 :                : 
     320                 :         279487 :     const T &value_ref() const noexcept
     321                 :                :     {
     322                 :         279487 :         return *reinterpret_cast<const T *>(&storage_);
     323                 :                :     }
     324                 :                : 
     325                 :                :     bool has_value_;
     326                 :                :     alignas(T) unsigned char storage_[sizeof(T)];
     327                 :                : };
     328                 :                : 
     329                 :                : #ifdef XLNT_NOEXCEPT_VALUE_COMPAT
     330                 :                : #undef XLNT_NOEXCEPT_VALUE_COMPAT
     331                 :                : #endif
     332                 :                : 
     333                 :                : } // namespace xlnt
        

Generated by: LCOV version 2.3.1-beta