// Copyright 2024 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. #include "pw_toolchain/internal/sibling_cast.h" #include "pw_compilation_testing/negative_compilation.h" #include "pw_unit_test/framework.h" namespace pw::internal { namespace { class Base { public: constexpr Base(char id) : lowercase_id_{id} {} protected: char lowercase_id_; }; class DerivedA : public Base { public: constexpr DerivedA() : Base('a') {} // Lowercase version of the ID. char Id() const { return lowercase_id_; } }; class DerivedB : public Base { public: constexpr DerivedB() : Base('b') {} // Capitalized version of the ID. char Id() const { return lowercase_id_ - ('a' - 'A'); } }; constexpr DerivedA kInstanceA; constexpr DerivedB kInstanceB; TEST(SiblingCast, Reference) { DerivedA instance_a; DerivedB instance_b; EXPECT_EQ(instance_a.Id(), 'a'); DerivedB& b_ref = SiblingCast(instance_a); EXPECT_EQ(b_ref.Id(), 'A'); EXPECT_EQ(instance_b.Id(), 'B'); DerivedA& a_ref = SiblingCast(instance_b); EXPECT_EQ(a_ref.Id(), 'b'); } TEST(SiblingCast, RvalueReference) { DerivedA instance_a; DerivedB instance_b; EXPECT_EQ(instance_a.Id(), 'a'); DerivedB&& b_ref = SiblingCast(std::move(instance_a)); EXPECT_EQ(b_ref.Id(), 'A'); EXPECT_EQ(instance_b.Id(), 'B'); DerivedA&& a_ref = SiblingCast(std::move(instance_b)); EXPECT_EQ(a_ref.Id(), 'b'); } TEST(SiblingCast, ConstReference) { EXPECT_EQ(kInstanceA.Id(), 'a'); const DerivedB& b_ref = SiblingCast(kInstanceA); EXPECT_EQ(b_ref.Id(), 'A'); EXPECT_EQ(kInstanceB.Id(), 'B'); const DerivedA& a_ref = SiblingCast(kInstanceB); EXPECT_EQ(a_ref.Id(), 'b'); } TEST(SiblingCast, ConstRvalueReference) { EXPECT_EQ(kInstanceA.Id(), 'a'); const DerivedB&& b_ref = SiblingCast(std::move(kInstanceA)); EXPECT_EQ(b_ref.Id(), 'A'); EXPECT_EQ(kInstanceB.Id(), 'B'); const DerivedA&& a_ref = SiblingCast(std::move(kInstanceB)); EXPECT_EQ(a_ref.Id(), 'b'); } TEST(SiblingCast, NonConstToConstReference) { DerivedA instance_a; DerivedB instance_b; EXPECT_EQ(instance_a.Id(), 'a'); const DerivedB& b_ref = SiblingCast(instance_a); EXPECT_EQ(b_ref.Id(), 'A'); EXPECT_EQ(instance_b.Id(), 'B'); const DerivedA& a_ref = SiblingCast(instance_b); EXPECT_EQ(a_ref.Id(), 'b'); } TEST(SiblingCast, Pointer) { DerivedA instance_a; DerivedB instance_b; EXPECT_EQ(instance_a.Id(), 'a'); DerivedB* b_ptr = SiblingCast(&instance_a); EXPECT_EQ(b_ptr->Id(), 'A'); EXPECT_EQ(instance_b.Id(), 'B'); DerivedA* a_ptr = SiblingCast(&instance_b); EXPECT_EQ(a_ptr->Id(), 'b'); } TEST(SiblingCast, ConstPointer) { EXPECT_EQ(kInstanceA.Id(), 'a'); const DerivedB* b_ptr = SiblingCast(&kInstanceA); EXPECT_EQ(b_ptr->Id(), 'A'); EXPECT_EQ(kInstanceB.Id(), 'B'); const DerivedA* a_ptr = SiblingCast(&kInstanceB); EXPECT_EQ(a_ptr->Id(), 'b'); } TEST(SiblingCast, NonConstToConstPointer) { DerivedA instance_a; DerivedB instance_b; EXPECT_EQ(instance_a.Id(), 'a'); const DerivedB* b_ptr = SiblingCast(&instance_a); EXPECT_EQ(b_ptr->Id(), 'A'); EXPECT_EQ(instance_b.Id(), 'B'); const DerivedA* a_ptr = SiblingCast(&instance_b); EXPECT_EQ(a_ptr->Id(), 'b'); } class DerivedExtra : public Base { public: DerivedExtra() : Base('e') {} int member() const { return member_; } private: int member_ = 0; }; class DerivedMultiple : public DerivedA, DerivedB { public: DerivedMultiple() {} }; TEST(SiblingCast, NegativeCompilationTests) { DerivedMultiple multiple; DerivedExtra extra; #if PW_NC_TEST(AmbiguousBase) PW_NC_EXPECT("unambiguously derive from the base"); [[maybe_unused]] DerivedB& b_ref = SiblingCast(multiple); #elif PW_NC_TEST(SourceTypeCannotAddMembers) PW_NC_EXPECT("source type cannot add any members"); [[maybe_unused]] DerivedB& b_ref = SiblingCast(extra); #elif PW_NC_TEST(DestinationTypeCannotAddMembers) PW_NC_EXPECT("destination type cannot add any members"); [[maybe_unused]] auto& e = SiblingCast(kInstanceA); #endif // PW_NC_TEST } } // namespace } // namespace pw::internal