base: Strengthen safe_cast and make it work for reference types

safe_cast now supports the exact same types as dynamic_cast would. In
particular, it now supports l-value references and rejects r-value
references.

The non-debug version has also been updated to make it build only in
the same cases as the debug version of safe_cast would.

Change-Id: I86692561c169b1ad063000c990a52ea80c6637ca
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/67453
Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Gabriel Busnot
2023-01-24 09:59:30 +00:00
committed by Gabriel B.
parent 1b949e9759
commit 3bdbe482c2

View File

@@ -30,6 +30,8 @@
#define __BASE_CAST_HH__
#include <cassert>
#include <type_traits>
#include "base/logging.hh"
namespace gem5
{
@@ -44,10 +46,20 @@ namespace gem5
template <class T, class U>
inline T
safe_cast(U ptr)
safe_cast(U&& ref_or_ptr)
{
T ret = dynamic_cast<T>(ptr);
assert(ret);
/*
* srd::forward used in conjunction with forwarding references (template T
* + T&&) ensures that dynamic_cast will see the exact same type that was
* passed to safe_cast (a.k.a., perfect forwarding).
*
* Not using std::forward would make safe_cast compile with references to
* temporary objects and thus return a dangling reference.
*/
T ret = dynamic_cast<T>(std::forward<U>(ref_or_ptr));
if constexpr (std::is_pointer_v<T>) {
gem5_assert(ret);
}
return ret;
}
@@ -59,9 +71,19 @@ safe_cast(U ptr)
template <class T, class U>
inline T
safe_cast(U ptr)
safe_cast(U&& ref_or_ptr)
{
return static_cast<T>(ptr);
/*
* safe_cast should be reserved to polymorphic types while static_cast is
* also allowed for non-polymorphic types. It could make safe_cast able to
* compile in a non-debug build and fail in a debug build.
*/
static_assert(std::is_polymorphic_v<
std::remove_pointer_t<
std::remove_reference_t<
U>>
>);
return static_cast<T>(std::forward<U>(ref_or_ptr));
}
#endif