casts.hh 3.86 KB
Newer Older
1
// -*- coding: utf-8 -*-
2
// Copyright (C) 2011, 2015-2017 Laboratoire de Recherche et Développement
3 4 5 6 7 8
// de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
9
// the Free Software Foundation; either version 3 of the License, or
10 11 12 13 14 15 16 17
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
18
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
19

20
#pragma once
21

22 23 24
#include <memory>
#include <type_traits>

25
// We usually write code like
26
//   subtypename* i = down_cast<subtypename*>(m);
27
//   ... use i ...
28
// When NDEBUG is set, the down_cast is a fast static_cast.
29
// Otherwise, the down_cast is a dynamic_cast and may return 0
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
// on error, which is caught by an assert in the down_cast function.
//
// NB: It is valid to use down_cast with non-pointer template argument:
//    subtypename& i = down_cast<subtypename&>(m);
// If an error occurs during the cast, an exception is thrown.
//
// NB: down_cast can also be used on shared_ptr.

namespace
{
  // A helper struct to check that downcasts are performed down an inheritance
  // hierarchy, not between unrelated types.
  template<typename Base, typename Derived>
  struct is_base_of : std::is_base_of<Base, Derived>
  {};
  template<typename Base, typename Derived>
  struct is_base_of<Base*, Derived*> : is_base_of<Base, Derived>
  {};
  // Also handle smart pointers.
  template<typename Base, typename Derived>
  struct is_base_of<std::shared_ptr<Base>, std::shared_ptr<Derived>>
    : is_base_of<Base, Derived>
  {};
53

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
  // std::is_pointer does not detect smart pointers.
  // Make our own version that detects pointer, plain or smart.
  template<typename T>
  struct is_pointer : std::is_pointer<T>
  {};
  template<typename T>
  struct is_pointer<std::shared_ptr<T>> : std::true_type
  {};

  template<typename T, typename U, bool check>
  struct _downcast;

  // A down-cast on non-pointer type is legitimate, e.g. down_cast<Derived&>(m);
  // An error in the dynamic_cast will throw an exception.
  template<typename T, typename U>
  struct _downcast<T, U, false>
  {
    static_assert(is_base_of<U, T>::value, "invalid downcast");

    static
    inline
    T
    cast(U u)
#ifdef NDEBUG
    noexcept
79
#else
80
    noexcept(is_pointer<T>::value)
81
#endif
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
    {
#ifdef NDEBUG
      return static_cast<T>(u);
#else
      return dynamic_cast<T>(u);
#endif
    }
  };

  // A specialization for smart pointer, so that down_cast can be used
  // uniformly.
  // NB: Use
  //    auto d = down_cast<std::shared_ptr<T>>(b);
  // instead of
  //    auto d = std::dynamic_pointer_cast<T>(b);
  template<typename T, typename U>
  struct _downcast<std::shared_ptr<T>, U, false>
  {
    static_assert(is_base_of<U, std::shared_ptr<T>>::value, "invalid downcast");
101

102 103 104 105 106 107 108
    static
    inline
    std::shared_ptr<T>
    cast(U u) noexcept
    {
#ifdef NDEBUG
      return std::static_pointer_cast<T>(u);
109
#else
110
      return std::dynamic_pointer_cast<T>(u);
111
#endif
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
    }
  };

  // Pointer type specialization.
  // Cast errors are caught by an assertion, no exception is thrown.
  template<typename T, typename U>
  struct _downcast<T, U, true>
  {
    static
    inline
    T
    cast(U u) noexcept
    {
      T t = _downcast<T, U, false>::cast(u);
      SPOT_ASSERT(t);
      return t;
    }
  };
} // anonymous namespace

// The actual function to call.
template<typename T, typename U>
inline
T
down_cast(U u)
#ifdef NDEBUG
    noexcept
#else
    noexcept(is_pointer<T>::value)
#endif
{
  return _downcast<T, U, is_pointer<T>::value>::cast(u);
}