LCOV - code coverage report
Current view: top level - elsa/core/Utilities - TypeCasts.hpp (source / functions) Hit Total Coverage
Test: coverage-all.lcov Lines: 76 76 100.0 %
Date: 2024-05-16 04:22:26 Functions: 594 716 83.0 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <type_traits>
       4             : #include <typeinfo>
       5             : #include <limits>
       6             : #include <cassert>
       7             : #include <memory>
       8             : 
       9             : #include "elsaDefines.h"
      10             : #include "Error.h"
      11             : 
      12             : namespace elsa
      13             : {
      14             :     namespace detail
      15             :     {
      16             :         /// Type of CopyConst_t is: 'const Dst' if Src is 'const', else it's 'Dst'
      17             :         template <typename Src, typename Dst>
      18             :         using CopyConst_t =
      19             :             typename std::conditional_t<std::is_const_v<Src>, std::add_const_t<Dst>, Dst>;
      20             :     } // namespace detail
      21             : 
      22             :     /// Check if a type can be dynamically casted to another
      23             :     template <typename Derived, typename Base>
      24             :     bool is(Base& input)
      25      154278 :     {
      26      154278 :         try {
      27             :             // we can't convert to pointer and check for nullptr,
      28             :             // because the reference can never be nullptr and the compiler knows this :)
      29      154278 :             [[maybe_unused]] const auto& r =
      30      154278 :                 dynamic_cast<detail::CopyConst_t<Base, Derived>&>(input);
      31      154278 :             return true;
      32      154278 :         } catch (const std::bad_cast&) {
      33          46 :             return false;
      34          46 :         }
      35      154278 :     }
      36             : 
      37             :     /// Overload to check for pointer types directly
      38             :     template <typename Derived, typename Base>
      39             :     bool is(Base* input)
      40       73111 :     {
      41       73153 :         return input && is<Derived>(*input);
      42       73111 :     }
      43             : 
      44             :     /// Overload to check for unique_ptr directly.
      45             :     /// Usually passing a const reference to a unique_ptr is really dumb, but for this case it's
      46             :     /// what is needed.
      47             :     template <typename Derived, typename Base, typename Deleter>
      48             :     bool is(std::unique_ptr<Base, Deleter>& input)
      49          29 :     {
      50          29 :         return input && is<Derived>(*input);
      51          29 :     }
      52             : 
      53             :     /// Downcast pointer, assumes that type is known (i.e. checked by is(...))
      54             :     template <typename Derived, typename Base>
      55             :     auto downcast(Base* input) -> detail::CopyConst_t<Base, Derived>*
      56       74262 :     {
      57       74262 :         static_assert(std::is_base_of_v<Base, Derived>, "To downcast, types needs to be derived");
      58             : 
      59       74262 :         assert(!input || is<Derived>(*input));
      60             : 
      61       74262 :         return static_cast<detail::CopyConst_t<Base, Derived>*>(input);
      62       74262 :     }
      63             : 
      64             :     /// Downcast reference, assumes that type is known (i.e. checked by is(...))
      65             :     template <typename Derived, typename Base>
      66             :     auto downcast(Base& input) -> detail::CopyConst_t<Base, Derived>&
      67        6309 :     {
      68        6309 :         static_assert(std::is_base_of_v<Base, Derived>, "To downcast, types needs to be derived");
      69             : 
      70        6309 :         assert(is<Derived>(input));
      71             : 
      72        6309 :         return static_cast<detail::CopyConst_t<Base, Derived>&>(input);
      73        6309 :     }
      74             : 
      75             :     /// Downcast reference, assumes that type is known (i.e. checked by is(...))
      76             :     /// Note: This version only works with the default deleter, if you need a fancy deleter,
      77             :     /// this function might need's some overloading and SFINAE to get it working
      78             :     template <typename Derived, typename Base>
      79             :     std::unique_ptr<Derived> downcast(std::unique_ptr<Base>&& input)
      80          22 :     {
      81          22 :         static_assert(std::is_base_of_v<Base, Derived>, "To downcast, types needs to be derived");
      82             : 
      83          22 :         assert(is<Derived>(input));
      84             : 
      85          22 :         auto d = static_cast<Derived*>(input.release());
      86          22 :         return std::unique_ptr<Derived>(d);
      87          22 :     }
      88             : 
      89             :     /// Try to downcast pointer to Base to Derived, return a nullptr if it fails
      90             :     template <typename Derived, typename Base>
      91             :     auto downcast_safe(Base* input) -> detail::CopyConst_t<Base, Derived>*
      92       72924 :     {
      93       72924 :         static_assert(std::is_base_of_v<Base, Derived>, "To downcast, types needs to be derived");
      94             : 
      95       74404 :         if (is<Derived>(input)) {
      96       74404 :             return downcast<Derived>(input);
      97       74404 :         }
      98             : 
      99 >1844*10^16 :         return nullptr;
     100 >1844*10^16 :     }
     101             : 
     102             :     /// Try to downcast reference to Base to Derived, Will throw std::bad_cast if it can't
     103             :     /// dynamically cast to Derived
     104             :     template <typename Derived, typename Base>
     105             :     auto downcast_safe(Base& input) -> detail::CopyConst_t<Base, Derived>&
     106         622 :     {
     107         622 :         static_assert(std::is_base_of_v<Base, Derived>, "To downcast, types needs to be derived");
     108             : 
     109         622 :         if (is<Derived>(input)) {
     110         619 :             return downcast<Derived>(input);
     111         619 :         }
     112             : 
     113           3 :         throw BadCastError("Could not cast given reference to wanted type");
     114           3 :     }
     115             : 
     116             :     /// Try to downcast a unique_ptr to Base to Derived, return a nullptr if it fails
     117             :     /// Note: that if the downcast can't be performed the callees unique_ptr is not touched
     118             :     /// But once the cast is done, the callees unique_ptr is not safe to use anymore
     119             :     /// Also This version only works with the default deleter, if you need a fancy deleter,
     120             :     /// this function might need's some overloading and SFINAE to get it working
     121             :     template <typename Derived, typename Base>
     122             :     std::unique_ptr<Derived> downcast_safe(std::unique_ptr<Base>&& input)
     123          17 :     {
     124          17 :         static_assert(std::is_base_of_v<Base, Derived>, "To downcast, types needs to be derived");
     125             : 
     126          17 :         if (Derived* result = downcast_safe<Derived>(input.get())) {
     127          16 :             input.release();
     128          16 :             return std::unique_ptr<Derived>(result);
     129          16 :         }
     130             : 
     131           1 :         return std::unique_ptr<Derived>(nullptr);
     132           1 :     }
     133             : 
     134             :     namespace detail
     135             :     {
     136             :         /// Alias to remove const, volatile and reference qualifiers of type
     137             :         template <typename T>
     138             :         using remove_crv_t = std::remove_reference_t<std::remove_cv_t<T>>;
     139             :     } // namespace detail
     140             : 
     141             :     /// Cast from one type to another without any checks, use with care
     142             :     template <typename To, typename From>
     143             :     auto as(From&& from) noexcept
     144   108152907 :     {
     145   108152907 :         return static_cast<To>(std::forward<From>(from));
     146   108152907 :     }
     147             : 
     148             :     /// Convert a signed value to an unsigned. Check for underflow only
     149             :     template <typename From>
     150             :     auto asUnsigned(From&& v) noexcept
     151    49160739 :     {
     152    49160739 :         static_assert(std::is_arithmetic_v<detail::remove_crv_t<From>>,
     153    49160739 :                       "Expect arithmetic type (use as() instead)");
     154             : 
     155    49160739 :         if constexpr (std::is_unsigned_v<From>) {
     156    49160739 :             return std::forward<From>(v);
     157    49160739 :         }
     158             : 
     159    49160739 :         using To = std::make_unsigned_t<detail::remove_crv_t<From>>;
     160             : 
     161    49160739 :         assert(v >= 0 && "Only convert positive numbers to an unsigned");
     162    49160739 :         return as<To>(std::forward<From>(v));
     163    49160739 :     }
     164             : 
     165             :     /// Convert an unsigned value to an signed. Check for overflow only
     166             :     template <typename From>
     167             :     auto asSigned(From&& v) noexcept
     168    58912607 :     {
     169    58912607 :         static_assert(std::is_arithmetic_v<detail::remove_crv_t<From>>,
     170    58912607 :                       "Expect arithmetic type (use as() instead)");
     171             : 
     172    58912607 :         if constexpr (std::is_signed_v<From>) {
     173    58912607 :             return std::forward<From>(v);
     174    58912607 :         }
     175             : 
     176    58912607 :         using To = std::make_signed_t<detail::remove_crv_t<From>>;
     177             : 
     178    58912607 :         assert(v <= std::numeric_limits<To>::max() && "Converted value is overflown");
     179    58912607 :         return as<To>(std::forward<From>(v));
     180    58912607 :     }
     181             : } // namespace elsa

Generated by: LCOV version 1.14