//============================================================================ // Copyright (c) Kitware, Inc. // All rights reserved. // See LICENSE.txt for details. // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //============================================================================ #include #include #include #include namespace test_variant { template struct TypePlaceholder { vtkm::IdComponent Value = Index; }; // A class that is trivially copiable but not totally trivial. struct TrivialCopy { vtkm::Id Value = 0; }; static vtkm::Id g_NonTrivialCount; // A class that must is not trivial to copy nor construct. struct NonTrivial { vtkm::Id Value; NonTrivial* Self; void CheckState() const { VTKM_TEST_ASSERT(this->Value == 12345); VTKM_TEST_ASSERT(this->Self == this); } NonTrivial() : Value(12345) , Self(this) { this->CheckState(); ++g_NonTrivialCount; } NonTrivial(const NonTrivial& src) : Value(src.Value) { src.CheckState(); this->Self = this; this->CheckState(); ++g_NonTrivialCount; } NonTrivial& operator=(const NonTrivial& src) { this->CheckState(); src.CheckState(); return *this; } ~NonTrivial() { if ((this->Value == 12345) && (this->Self == this)) { // Normal destruction this->Value = -1; this->Self = nullptr; --g_NonTrivialCount; } else { // Normally we would use VTKM_TEST_ASSERT or VTKM_TEST_FAIL, but it's not good to throw // exceptions from destructors (especially since Variant marks these calls as noexcept). // Instead, just check and terminate the program. std::cout << "ERROR at " << __FILE__ << ":" << __LINE__ << ":\n"; std::cout << "Destroying a class that was not properly constructed." << std::endl; std::exit(1); } } }; void TestSize() { std::cout << "Test size" << std::endl; using VariantType = vtkm::exec::Variant; constexpr size_t variantSize = sizeof(VariantType); VTKM_TEST_ASSERT(variantSize <= 16, "Size of variant should not be larger than biggest type plus and index. ", variantSize); } void TestIndexing() { std::cout << "Test indexing" << std::endl; using VariantType = vtkm::exec::Variant, TypePlaceholder<1>, TypePlaceholder<2>, TypePlaceholder<3>, TypePlaceholder<4>, TypePlaceholder<5>, TypePlaceholder<6>, TypePlaceholder<7>, TypePlaceholder<8>, TypePlaceholder<9>, TypePlaceholder<10>, TypePlaceholder<11>, TypePlaceholder<12>, TypePlaceholder<13>, TypePlaceholder<14>, TypePlaceholder<15>, TypePlaceholder<16>, TypePlaceholder<17>, TypePlaceholder<18>, TypePlaceholder<19>, TypePlaceholder<20>, TypePlaceholder<21>, TypePlaceholder<22>, TypePlaceholder<23>, TypePlaceholder<24>, TypePlaceholder<25>, TypePlaceholder<26>, TypePlaceholder<27>, TypePlaceholder<28>, TypePlaceholder<29>>; VariantType variant; VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 0); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 1); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 2); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 3); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 4); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 5); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 6); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 7); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 8); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 9); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 10); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 11); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 12); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 13); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 14); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 15); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 16); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 17); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 18); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 19); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 20); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 21); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 22); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 23); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 24); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 25); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 26); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 27); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 28); VTKM_TEST_ASSERT(VariantType::IndexOf>::value == 29); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 0); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 1); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 2); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 3); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 4); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 5); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 6); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 7); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 8); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 9); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 10); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 11); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 12); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 13); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 14); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 15); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 16); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 17); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 18); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 19); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 20); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 21); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 22); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 23); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 24); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 25); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 26); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 27); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 28); VTKM_TEST_ASSERT(variant.GetIndexOf>() == 29); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<0>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<1>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<2>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<3>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<4>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<5>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<6>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<7>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<8>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<9>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<10>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<11>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<12>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<13>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<14>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<15>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<16>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<17>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<18>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<19>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<20>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<21>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<22>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<23>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<24>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<25>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<26>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<27>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<28>>::value)); VTKM_STATIC_ASSERT((std::is_same, TypePlaceholder<29>>::value)); VTKM_TEST_ASSERT(VariantType::CanStore>::value); VTKM_TEST_ASSERT(!VariantType::CanStore>::value); VTKM_TEST_ASSERT(variant.GetCanStore>()); VTKM_TEST_ASSERT(!variant.GetCanStore>()); } void TestTriviallyCopyable() { #ifdef VTKM_USE_STD_IS_TRIVIAL // Make sure base types are behaving as expected VTKM_STATIC_ASSERT(vtkmstd::is_trivially_constructible::value); VTKM_STATIC_ASSERT(vtkmstd::is_trivially_copyable::value); VTKM_STATIC_ASSERT(vtkmstd::is_trivial::value); VTKM_STATIC_ASSERT(vtkmstd::is_trivially_constructible::value); VTKM_STATIC_ASSERT(vtkmstd::is_trivially_copyable::value); VTKM_STATIC_ASSERT(vtkmstd::is_trivial::value); VTKM_STATIC_ASSERT(!vtkmstd::is_trivially_constructible::value); VTKM_STATIC_ASSERT(!vtkmstd::is_trivially_copyable::value); VTKM_STATIC_ASSERT(!vtkmstd::is_trivial::value); VTKM_STATIC_ASSERT(!vtkmstd::is_trivially_constructible::value); VTKM_STATIC_ASSERT(vtkmstd::is_trivially_copyable::value); VTKM_STATIC_ASSERT(!vtkmstd::is_trivial::value); // A variant of trivially constructable things should be trivially constructable VTKM_STATIC_ASSERT( (vtkmstd::is_trivially_constructible>::value)); VTKM_STATIC_ASSERT((vtkmstd::is_trivially_constructible>::value)); // A variant of trivially copyable things should be trivially copyable VTKM_STATIC_ASSERT((vtkmstd::is_trivially_copyable< vtkm::exec::detail::VariantUnion>::value)); VTKM_STATIC_ASSERT( (vtkmstd::is_trivially_copyable>::value)); // A variant of any non-trivially constructable things is not trivially copyable VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_constructible< vtkm::exec::detail::VariantUnion>::value)); VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_constructible< vtkm::exec::detail::VariantUnion>::value)); VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_constructible< vtkm::exec::detail::VariantUnion>::value)); VTKM_STATIC_ASSERT( (!vtkmstd::is_trivially_constructible>::value)); VTKM_STATIC_ASSERT( (!vtkmstd::is_trivially_constructible>::value)); VTKM_STATIC_ASSERT( (!vtkmstd::is_trivially_constructible>::value)); // A variant of any non-trivially copyable things is not trivially copyable VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_copyable< vtkm::exec::detail::VariantUnion>::value)); VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_copyable< vtkm::exec::detail::VariantUnion>::value)); VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_copyable< vtkm::exec::detail::VariantUnion>::value)); VTKM_STATIC_ASSERT( (!vtkmstd::is_trivially_copyable>::value)); VTKM_STATIC_ASSERT( (!vtkmstd::is_trivially_copyable>::value)); VTKM_STATIC_ASSERT( (!vtkmstd::is_trivially_copyable>::value)); // A variant of trivial things should be trivial VTKM_STATIC_ASSERT((vtkmstd::is_trivial>::value)); VTKM_STATIC_ASSERT((!vtkmstd::is_trivial>::value)); VTKM_STATIC_ASSERT((!vtkmstd::is_trivial>::value)); #endif // VTKM_USE_STD_IS_TRIVIAL } struct TestFunctor { template vtkm::FloatDefault operator()(TypePlaceholder, vtkm::Id expectedValue) { VTKM_TEST_ASSERT(Index == expectedValue, "Index = ", Index, ", expected = ", expectedValue); return TestValue(expectedValue, vtkm::FloatDefault{}); } }; struct TestFunctorModify { template void operator()(TypePlaceholder& object, vtkm::Id expectedValue) { VTKM_TEST_ASSERT(Index == expectedValue, "Index = ", Index, ", expected = ", expectedValue); VTKM_TEST_ASSERT(object.Value == expectedValue); ++object.Value; } }; void TestGet() { std::cout << "Test Get" << std::endl; using VariantType = vtkm::exec::Variant, TypePlaceholder<1>, vtkm::Id, TypePlaceholder<3>, TypePlaceholder<4>, TypePlaceholder<5>, TypePlaceholder<6>, TypePlaceholder<7>, TypePlaceholder<8>, TypePlaceholder<9>, TypePlaceholder<10>, TypePlaceholder<11>, TypePlaceholder<12>, TypePlaceholder<13>, TypePlaceholder<14>, TypePlaceholder<15>, TypePlaceholder<16>, TypePlaceholder<17>, TypePlaceholder<18>, TypePlaceholder<19>, TypePlaceholder<20>, TypePlaceholder<21>, TypePlaceholder<22>, TypePlaceholder<23>, TypePlaceholder<24>, TypePlaceholder<25>, TypePlaceholder<26>, vtkm::Float32, TypePlaceholder<28>, TypePlaceholder<29>>; { const vtkm::Id expectedValue = TestValue(3, vtkm::Id{}); VariantType variant = expectedValue; VTKM_TEST_ASSERT(variant.GetIndex() == 2); VTKM_TEST_ASSERT(variant.IsType()); VTKM_TEST_ASSERT(!variant.IsType()); VTKM_TEST_ASSERT(variant.Get<2>() == expectedValue); VTKM_TEST_ASSERT(variant.Get() == expectedValue); // This line should compile, but will assert if you actually try to run it. //variant.Get>(); } { const vtkm::Float32 expectedValue = TestValue(4, vtkm::Float32{}); VariantType variant = expectedValue; VTKM_TEST_ASSERT(variant.GetIndex() == 27); VTKM_TEST_ASSERT(variant.Get<27>() == expectedValue); VTKM_TEST_ASSERT(variant.Get() == expectedValue); } } void TestCastAndCall() { std::cout << "Test CastAndCall" << std::endl; using VariantType = vtkm::exec::Variant, TypePlaceholder<1>, TypePlaceholder<2>, TypePlaceholder<3>, TypePlaceholder<4>, TypePlaceholder<5>, TypePlaceholder<6>, TypePlaceholder<7>, TypePlaceholder<8>, TypePlaceholder<9>, TypePlaceholder<10>, TypePlaceholder<11>, TypePlaceholder<12>, TypePlaceholder<13>, TypePlaceholder<14>, TypePlaceholder<15>, TypePlaceholder<16>, TypePlaceholder<17>, TypePlaceholder<18>, TypePlaceholder<19>, TypePlaceholder<20>, TypePlaceholder<21>, TypePlaceholder<22>, TypePlaceholder<23>, TypePlaceholder<24>, TypePlaceholder<25>, TypePlaceholder<26>, TypePlaceholder<27>, TypePlaceholder<28>, TypePlaceholder<29>>; vtkm::FloatDefault result; VariantType variant0{ TypePlaceholder<0>{} }; result = variant0.CastAndCall(TestFunctor(), 0); VTKM_TEST_ASSERT(test_equal(result, TestValue(0, vtkm::FloatDefault{}))); VariantType variant1{ TypePlaceholder<1>{} }; result = variant1.CastAndCall(TestFunctor(), 1); VTKM_TEST_ASSERT(test_equal(result, TestValue(1, vtkm::FloatDefault{}))); const VariantType variant2{ TypePlaceholder<2>{} }; result = variant2.CastAndCall(TestFunctor(), 2); VTKM_TEST_ASSERT(test_equal(result, TestValue(2, vtkm::FloatDefault{}))); VariantType variant3{ TypePlaceholder<3>{} }; result = variant3.CastAndCall(TestFunctor(), 3); VTKM_TEST_ASSERT(test_equal(result, TestValue(3, vtkm::FloatDefault{}))); VariantType variant26{ TypePlaceholder<26>{} }; result = variant26.CastAndCall(TestFunctor(), 26); VTKM_TEST_ASSERT(test_equal(result, TestValue(26, vtkm::FloatDefault{}))); variant1.CastAndCall(TestFunctorModify{}, 1); VTKM_TEST_ASSERT(variant1.Get<1>().Value == 2, "Variant object not updated."); variant1.CastAndCall([](auto& object) { ++object.Value; }); } void TestCopyInvalid() { std::cout << "Test copy invalid variant" << std::endl; using VariantType = vtkm::exec::Variant, NonTrivial>; VariantType source; source.Reset(); VariantType destination1(source); VTKM_TEST_ASSERT(!destination1.IsValid()); VariantType destination2(TypePlaceholder<0>{}); destination2 = source; VTKM_TEST_ASSERT(!destination2.IsValid()); } struct CountConstructDestruct { vtkm::Id* Count; CountConstructDestruct(vtkm::Id* count) : Count(count) { ++(*this->Count); } CountConstructDestruct(const CountConstructDestruct& src) : Count(src.Count) { ++(*this->Count); } ~CountConstructDestruct() { --(*this->Count); } CountConstructDestruct& operator=(const CountConstructDestruct&) = delete; }; void TestCopyDestroy() { std::cout << "Test copy destroy" << std::endl; using VariantType = vtkm::exec::Variant, TypePlaceholder<1>, CountConstructDestruct, TypePlaceholder<3>, TypePlaceholder<4>>; #ifdef VTKM_USE_STD_IS_TRIVIAL VTKM_STATIC_ASSERT(!vtkmstd::is_trivially_copyable::value); #endif // VTKM_USE_STD_IS_TRIVIAL vtkm::Id count = 0; VariantType variant1 = CountConstructDestruct(&count); VTKM_TEST_ASSERT(count == 1, count); VTKM_TEST_ASSERT(*variant1.Get<2>().Count == 1); { VariantType variant2{ variant1 }; VTKM_TEST_ASSERT(count == 2, count); VTKM_TEST_ASSERT(*variant1.Get<2>().Count == 2); VTKM_TEST_ASSERT(*variant2.Get<2>().Count == 2); } VTKM_TEST_ASSERT(count == 1, count); VTKM_TEST_ASSERT(*variant1.Get<2>().Count == 1); { VariantType variant3{ VariantType(CountConstructDestruct(&count)) }; VTKM_TEST_ASSERT(count == 2, count); VTKM_TEST_ASSERT(*variant1.Get<2>().Count == 2); VTKM_TEST_ASSERT(*variant3.Get<2>().Count == 2); } VTKM_TEST_ASSERT(count == 1, count); VTKM_TEST_ASSERT(*variant1.Get<2>().Count == 1); { VariantType variant4{ variant1 }; VTKM_TEST_ASSERT(count == 2, count); VTKM_TEST_ASSERT(*variant1.Get<2>().Count == 2); VTKM_TEST_ASSERT(*variant4.Get<2>().Count == 2); variant4 = TypePlaceholder<0>{}; VTKM_TEST_ASSERT(count == 1, count); VTKM_TEST_ASSERT(*variant1.Get<2>().Count == 1); variant4 = VariantType{ TypePlaceholder<1>{} }; VTKM_TEST_ASSERT(count == 1, count); VTKM_TEST_ASSERT(*variant1.Get<2>().Count == 1); variant4 = variant1; VTKM_TEST_ASSERT(count == 2, count); VTKM_TEST_ASSERT(*variant1.Get<2>().Count == 2); VTKM_TEST_ASSERT(*variant4.Get<2>().Count == 2); } } void TestEmplace() { std::cout << "Test Emplace" << std::endl; using VariantType = vtkm::exec::Variant>; VariantType variant; variant.Emplace(TestValue(0, vtkm::Id{})); VTKM_TEST_ASSERT(variant.GetIndex() == 0); VTKM_TEST_ASSERT(variant.Get() == TestValue(0, vtkm::Id{})); variant.Emplace<1>(TestValue(1, vtkm::Id{})); VTKM_TEST_ASSERT(variant.GetIndex() == 1); VTKM_TEST_ASSERT(variant.Get() == vtkm::Id3{ TestValue(1, vtkm::Id{}) }); variant.Emplace<1>(TestValue(2, vtkm::Id{}), TestValue(3, vtkm::Id{}), TestValue(4, vtkm::Id{})); VTKM_TEST_ASSERT(variant.GetIndex() == 1); VTKM_TEST_ASSERT( variant.Get() == vtkm::Id3{ TestValue(2, vtkm::Id{}), TestValue(3, vtkm::Id{}), TestValue(4, vtkm::Id{}) }); variant.Emplace<2>( { TestValue(5, vtkm::Id{}), TestValue(6, vtkm::Id{}), TestValue(7, vtkm::Id{}) }); VTKM_TEST_ASSERT(variant.GetIndex() == 2); VTKM_TEST_ASSERT(variant.Get>() == std::vector{ TestValue(5, vtkm::Id{}), TestValue(6, vtkm::Id{}), TestValue(7, vtkm::Id{}) }); } void TestConstructDestruct() { std::cout << "Make sure constructors and destructors are called correctly" << std::endl; g_NonTrivialCount = 0; using VariantType = vtkm::exec::Variant; { VariantType variant1 = NonTrivial{}; VariantType variant2 = variant1; variant2 = NonTrivial{}; NonTrivial nonTrivial; VariantType variant3 = nonTrivial; VariantType variant4; variant4.Emplace(); VariantType variant5(VariantType(NonTrivial{})); } VTKM_TEST_ASSERT(g_NonTrivialCount == 0); } void TestCopySelf() { std::cout << "Make sure copying a Variant to itself works" << std::endl; using VariantType = vtkm::exec::Variant, NonTrivial, TypePlaceholder<2>>; VariantType variant{ NonTrivial{} }; VariantType& variantRef = variant; variant = variantRef; variant = variant.Get(); } void RunTest() { TestSize(); TestIndexing(); TestTriviallyCopyable(); TestGet(); TestCastAndCall(); TestCopyInvalid(); TestCopyDestroy(); TestEmplace(); TestConstructDestruct(); TestCopySelf(); } } // namespace test_variant int UnitTestVariant(int argc, char* argv[]) { return vtkm::testing::Testing::Run(test_variant::RunTest, argc, argv); }