1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_toolchain/internal/sibling_cast.h"
16
17 #include "pw_compilation_testing/negative_compilation.h"
18 #include "pw_unit_test/framework.h"
19
20 namespace pw::internal {
21 namespace {
22
23 class Base {
24 public:
Base(char id)25 constexpr Base(char id) : lowercase_id_{id} {}
26
27 protected:
28 char lowercase_id_;
29 };
30
31 class DerivedA : public Base {
32 public:
DerivedA()33 constexpr DerivedA() : Base('a') {}
34
35 // Lowercase version of the ID.
Id() const36 char Id() const { return lowercase_id_; }
37 };
38
39 class DerivedB : public Base {
40 public:
DerivedB()41 constexpr DerivedB() : Base('b') {}
42
43 // Capitalized version of the ID.
Id() const44 char Id() const { return lowercase_id_ - ('a' - 'A'); }
45 };
46
47 constexpr DerivedA kInstanceA;
48 constexpr DerivedB kInstanceB;
49
TEST(SiblingCast,Reference)50 TEST(SiblingCast, Reference) {
51 DerivedA instance_a;
52 DerivedB instance_b;
53
54 EXPECT_EQ(instance_a.Id(), 'a');
55 DerivedB& b_ref = SiblingCast<DerivedB&, Base>(instance_a);
56 EXPECT_EQ(b_ref.Id(), 'A');
57
58 EXPECT_EQ(instance_b.Id(), 'B');
59 DerivedA& a_ref = SiblingCast<DerivedA&, Base>(instance_b);
60 EXPECT_EQ(a_ref.Id(), 'b');
61 }
62
TEST(SiblingCast,RvalueReference)63 TEST(SiblingCast, RvalueReference) {
64 DerivedA instance_a;
65 DerivedB instance_b;
66
67 EXPECT_EQ(instance_a.Id(), 'a');
68 DerivedB&& b_ref = SiblingCast<DerivedB&&, Base>(std::move(instance_a));
69 EXPECT_EQ(b_ref.Id(), 'A');
70
71 EXPECT_EQ(instance_b.Id(), 'B');
72 DerivedA&& a_ref = SiblingCast<DerivedA&&, Base>(std::move(instance_b));
73 EXPECT_EQ(a_ref.Id(), 'b');
74 }
75
TEST(SiblingCast,ConstReference)76 TEST(SiblingCast, ConstReference) {
77 EXPECT_EQ(kInstanceA.Id(), 'a');
78 const DerivedB& b_ref = SiblingCast<const DerivedB&, Base>(kInstanceA);
79 EXPECT_EQ(b_ref.Id(), 'A');
80
81 EXPECT_EQ(kInstanceB.Id(), 'B');
82 const DerivedA& a_ref = SiblingCast<const DerivedA&, Base>(kInstanceB);
83 EXPECT_EQ(a_ref.Id(), 'b');
84 }
85
TEST(SiblingCast,ConstRvalueReference)86 TEST(SiblingCast, ConstRvalueReference) {
87 EXPECT_EQ(kInstanceA.Id(), 'a');
88 const DerivedB&& b_ref =
89 SiblingCast<const DerivedB&&, Base>(std::move(kInstanceA));
90 EXPECT_EQ(b_ref.Id(), 'A');
91
92 EXPECT_EQ(kInstanceB.Id(), 'B');
93 const DerivedA&& a_ref =
94 SiblingCast<const DerivedA&&, Base>(std::move(kInstanceB));
95 EXPECT_EQ(a_ref.Id(), 'b');
96 }
97
TEST(SiblingCast,NonConstToConstReference)98 TEST(SiblingCast, NonConstToConstReference) {
99 DerivedA instance_a;
100 DerivedB instance_b;
101
102 EXPECT_EQ(instance_a.Id(), 'a');
103 const DerivedB& b_ref = SiblingCast<const DerivedB&, Base>(instance_a);
104 EXPECT_EQ(b_ref.Id(), 'A');
105
106 EXPECT_EQ(instance_b.Id(), 'B');
107 const DerivedA& a_ref = SiblingCast<const DerivedA&, Base>(instance_b);
108 EXPECT_EQ(a_ref.Id(), 'b');
109 }
110
TEST(SiblingCast,Pointer)111 TEST(SiblingCast, Pointer) {
112 DerivedA instance_a;
113 DerivedB instance_b;
114
115 EXPECT_EQ(instance_a.Id(), 'a');
116 DerivedB* b_ptr = SiblingCast<DerivedB*, Base>(&instance_a);
117 EXPECT_EQ(b_ptr->Id(), 'A');
118
119 EXPECT_EQ(instance_b.Id(), 'B');
120 DerivedA* a_ptr = SiblingCast<DerivedA*, Base>(&instance_b);
121 EXPECT_EQ(a_ptr->Id(), 'b');
122 }
123
TEST(SiblingCast,ConstPointer)124 TEST(SiblingCast, ConstPointer) {
125 EXPECT_EQ(kInstanceA.Id(), 'a');
126 const DerivedB* b_ptr = SiblingCast<const DerivedB*, Base>(&kInstanceA);
127 EXPECT_EQ(b_ptr->Id(), 'A');
128
129 EXPECT_EQ(kInstanceB.Id(), 'B');
130 const DerivedA* a_ptr = SiblingCast<const DerivedA*, Base>(&kInstanceB);
131 EXPECT_EQ(a_ptr->Id(), 'b');
132 }
133
TEST(SiblingCast,NonConstToConstPointer)134 TEST(SiblingCast, NonConstToConstPointer) {
135 DerivedA instance_a;
136 DerivedB instance_b;
137
138 EXPECT_EQ(instance_a.Id(), 'a');
139 const DerivedB* b_ptr = SiblingCast<const DerivedB*, Base>(&instance_a);
140 EXPECT_EQ(b_ptr->Id(), 'A');
141
142 EXPECT_EQ(instance_b.Id(), 'B');
143 const DerivedA* a_ptr = SiblingCast<const DerivedA*, Base>(&instance_b);
144 EXPECT_EQ(a_ptr->Id(), 'b');
145 }
146
147 class DerivedExtra : public Base {
148 public:
DerivedExtra()149 DerivedExtra() : Base('e') {}
150
member() const151 int member() const { return member_; }
152
153 private:
154 int member_ = 0;
155 };
156
157 class DerivedMultiple : public DerivedA, DerivedB {
158 public:
DerivedMultiple()159 DerivedMultiple() {}
160 };
161
TEST(SiblingCast,NegativeCompilationTests)162 TEST(SiblingCast, NegativeCompilationTests) {
163 DerivedMultiple multiple;
164 DerivedExtra extra;
165 #if PW_NC_TEST(AmbiguousBase)
166 PW_NC_EXPECT("unambiguously derive from the base");
167 [[maybe_unused]] DerivedB& b_ref = SiblingCast<DerivedB&, Base>(multiple);
168 #elif PW_NC_TEST(SourceTypeCannotAddMembers)
169 PW_NC_EXPECT("source type cannot add any members");
170 [[maybe_unused]] DerivedB& b_ref = SiblingCast<DerivedB&, Base>(extra);
171 #elif PW_NC_TEST(DestinationTypeCannotAddMembers)
172 PW_NC_EXPECT("destination type cannot add any members");
173 [[maybe_unused]] auto& e = SiblingCast<const DerivedExtra&, Base>(kInstanceA);
174 #endif // PW_NC_TEST
175 }
176
177 } // namespace
178 } // namespace pw::internal
179