forked from RobotLocomotion/drake
-
Notifications
You must be signed in to change notification settings - Fork 0
/
is_dynamic_castable.h
75 lines (67 loc) · 2.82 KB
/
is_dynamic_castable.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#pragma once
#include <memory>
#include <string>
#include <gtest/gtest.h>
#include "drake/common/nice_type_name.h"
namespace drake {
/// Checks if @p ptr, a pointer to `FromType` class, can be safely converted to
/// a pointer to `ToType` class. Our motivation is to provide a good diagnostic
/// for what @p ptr _actually_ was when the test fails.
///
/// Here is an illustrative code snippet. We assume that `Prius` and `Camry` are
/// derived classes of `Car`.
///
/// @code
/// const Car* prius = new Prius{};
///
/// // The following assertion fails without diagnostic info.
/// EXPECT_TRUE(dynamic_cast<Camry>(ptr) != nullptr)
///
/// // The following assertion returns `::testing::AssertionFailure()` with
/// // diagnostic info attached.
/// EXPECT_TRUE(is_dynamic_castable<Camry>(prius));
/// // Output:
/// // Value of: is_dynamic_castable<Camry>(prius)
/// // Actual: false (is_dynamic_castable<Camry>(Car* ptr) failed
/// // because ptr is of dynamic type Prius.)
/// // Expected: true
/// @endcode
template <typename ToType, typename FromType>
[[nodiscard]] ::testing::AssertionResult is_dynamic_castable(
const FromType* ptr) {
if (ptr == nullptr) {
const std::string from_name{NiceTypeName::Get<FromType>()};
const std::string to_name{NiceTypeName::Get<ToType>()};
return ::testing::AssertionFailure()
<< "is_dynamic_castable<" << to_name << ">(" << from_name << "* ptr)"
<< " failed because ptr was already nullptr.";
}
if (dynamic_cast<const ToType* const>(ptr) == nullptr) {
const std::string from_name{NiceTypeName::Get<FromType>()};
const std::string to_name{NiceTypeName::Get<ToType>()};
const std::string dynamic_name{NiceTypeName::Get(*ptr)};
return ::testing::AssertionFailure()
<< "is_dynamic_castable<" << to_name << ">(" << from_name << "* ptr)"
<< " failed because ptr is of dynamic type " << dynamic_name << ".";
}
return ::testing::AssertionSuccess();
}
/// Checks if @p ptr, a shared pointer to `FromType` class, can be safely
/// converted to a shared pointer to `ToType` class. Our motivation is to
/// provide a good diagnostic for what @p ptr _actually_ was when the test
/// fails.
template <typename ToType, typename FromType>
[[nodiscard]] ::testing::AssertionResult is_dynamic_castable(
std::shared_ptr<FromType> ptr) {
return is_dynamic_castable<ToType, FromType>(ptr.get());
}
/// Checks if @p ptr, a shared pointer to `FromType` class, can be safely
/// converted to a shared pointer to `ToType` class. Our motivation is to
/// provide a good diagnostic for what @p ptr _actually_ was when the test
/// fails.
template <typename ToType, typename FromType>
[[nodiscard]] ::testing::AssertionResult is_dynamic_castable(
const std::unique_ptr<FromType>& ptr) {
return is_dynamic_castable<ToType, FromType>(ptr.get());
}
} // namespace drake