Rtti.cpp 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/RTTI/RTTI.h>
  9. #include <AzCore/RTTI/ReflectionManager.h>
  10. #include <AzCore/Serialization/SerializeContext.h>
  11. #include <AzCore/UnitTest/TestTypes.h>
  12. #include <AzCore/std/containers/array.h>
  13. #include <AzCore/std/containers/bitset.h>
  14. #include <AzCore/std/containers/fixed_forward_list.h>
  15. #include <AzCore/std/containers/fixed_list.h>
  16. #include <AzCore/std/containers/fixed_unordered_map.h>
  17. #include <AzCore/std/containers/fixed_unordered_set.h>
  18. #include <AzCore/std/containers/fixed_vector.h>
  19. #include <AzCore/std/containers/forward_list.h>
  20. #include <AzCore/std/containers/map.h>
  21. #include <AzCore/std/containers/set.h>
  22. #include <AzCore/std/containers/unordered_map.h>
  23. #include <AzCore/std/containers/unordered_set.h>
  24. #include <AzCore/std/containers/variant.h>
  25. #include <AzCore/std/containers/vector.h>
  26. #include <AzCore/std/functional.h>
  27. #include <AzCore/std/parallel/thread.h>
  28. #include <AzCore/std/smart_ptr/unique_ptr.h>
  29. #include <AzCore/std/string/string.h>
  30. #include <AzCore/std/tuple.h>
  31. #include <AzCore/std/utils.h>
  32. // Compile checks that a primitive type(int) and qualified types have AzTypeInfo
  33. static_assert(AZ::Internal::HasAzTypeInfo_v<int>, "int value type should is missing GetO3deTypeName or GetO3deTypeId overload");
  34. static_assert(AZ::Internal::HasAzTypeInfo_v<int*>, "pointer to int type is missing GetO3deTypeName or GetO3deTypeId overload");
  35. static_assert(AZ::Internal::HasAzTypeInfo_v<int const>, "const int value type is missing GetO3deTypeName or GetO3deTypeId overload");
  36. static_assert(AZ::Internal::HasAzTypeInfo_v<int&>, "lvalue reference to non-const int type is missing GetO3deTypeName or GetO3deTypeId overload");
  37. static_assert(AZ::Internal::HasAzTypeInfo_v<int const&>, "lvalue reference to const int type is missing GetO3deTypeName or GetO3deTypeId overload");
  38. static_assert(AZ::Internal::HasAzTypeInfo_v<int&&>, "rvalue reference to non-const int type is missing GetO3deTypeName or GetO3deTypeId overload");
  39. static_assert(AZ::Internal::HasAzTypeInfo_v<int const&&>, "rvalue reference to const int type is missing GetO3deTypeName or GetO3deTypeId overload");
  40. // Check unique_ptr<T>
  41. static_assert(AZ::Internal::HasAzTypeInfo_v<AZStd::unique_ptr<int>>, "unique_ptr<T> is missing GetO3deTypeName or GetO3deTypeId overload");
  42. static_assert(AZStd::is_same_v<decltype(GetO3deTemplateId(AZ::Adl{},
  43. AZ::AzGenericTypeInfo::Internal::TemplateIdentityTypes<std::unique_ptr>{})), AZ::TemplateId>,
  44. "unique_ptr is missing GetO3deTemplateId overload");
  45. namespace UnitTest
  46. {
  47. template<class T>
  48. struct AliasTest;
  49. }
  50. namespace UnitTestUsing
  51. {
  52. using UnitTest::AliasTest;
  53. }
  54. namespace UnitTestAlias
  55. {
  56. template<class T>
  57. using AliasTest = UnitTest::AliasTest<T>;
  58. }
  59. // The `using` declaration brings the UnitTest::AliasTest type into the UnitTestUsing scope
  60. // So canonically they are treated as the same temp[late
  61. static_assert(AZStd::is_same_v<AZ::AzGenericTypeInfo::Internal::TemplateIdentityTypes<UnitTest::AliasTest>,
  62. AZ::AzGenericTypeInfo::Internal::TemplateIdentityTypes<UnitTestUsing::AliasTest>>);
  63. // An alias template are a separate template from any types it references
  64. // An alias template doesn't actually alias another template, but is its own construct.
  65. // For example `template<class T> void_t = void`, doesn't "reference" another template, it just maps
  66. // any instantiation of the template to void
  67. // Similarly `template<class T> unique_ptr = std::unique_ptr<T>`, is not an alias for the `std::unique_ptr`
  68. // template, the result of it's instantiation results in a class type of `std::unique_ptr<T>`
  69. #if !defined(AZ_COMPILER_GCC)
  70. static_assert(!AZStd::is_same_v<AZ::AzGenericTypeInfo::Internal::TemplateIdentityTypes<UnitTest::AliasTest>,
  71. AZ::AzGenericTypeInfo::Internal::TemplateIdentityTypes<UnitTestAlias::AliasTest>>);
  72. #else
  73. // On GCC the Alias template matches actual template identifier
  74. static_assert(AZStd::is_same_v<AZ::AzGenericTypeInfo::Internal::TemplateIdentityTypes<UnitTest::AliasTest>,
  75. AZ::AzGenericTypeInfo::Internal::TemplateIdentityTypes<UnitTestAlias::AliasTest>>);
  76. #endif
  77. namespace UnitTest
  78. {
  79. // Non intrusive typeinfo for external and integral types
  80. struct ExternalClass
  81. {
  82. };
  83. AZ_TYPE_INFO_SPECIALIZE(ExternalClass, "{38380915-084B-4886-8D3D-B8439E9E987C}");
  84. }
  85. // These 2 types must only EVER be used by the MultiThreadedTypeInfo test, or else
  86. // that test is invalidated because the statics will have been initialized already
  87. struct MTTI {};
  88. struct MTTI2
  89. {
  90. AZ_TYPE_INFO(MTTI2, "{CBC94693-5ECD-4CBF-A8DB-9B122E697E8D}");
  91. };
  92. namespace AZ
  93. {
  94. enum class PlatformID;
  95. AZ_TYPE_INFO_SPECIALIZE(MTTI, "{4876C017-0C26-4D0D-9A1F-2A738BAE6449}");
  96. }
  97. namespace UnitTest
  98. {
  99. class Rtti
  100. : public LeakDetectionFixture
  101. {
  102. };
  103. // Intrusive TypeInfo
  104. struct MyClass
  105. {
  106. AZ_TYPE_INFO(MyClass, "{CADA6BA7-D479-4C20-B7F0-121A1DF4E9CC}");
  107. };
  108. template<class T1, class T2>
  109. struct MyClassTemplate
  110. {
  111. };
  112. template<class... Args>
  113. struct MyClassVariadicTemplate
  114. {
  115. };
  116. }
  117. namespace AZ
  118. {
  119. AZ_TYPE_INFO_TEMPLATE(UnitTest::MyClassTemplate, "{EBFE7ADF-1FCE-47F0-B417-14FE06BAF02D}", AZ_TYPE_INFO_CLASS, AZ_TYPE_INFO_CLASS);
  120. AZ_TYPE_INFO_TEMPLATE(UnitTest::MyClassVariadicTemplate, "{60C1D809-09FA-48EB-A9B7-0BD8DBFF21C8}", AZ_TYPE_INFO_CLASS_VARARGS);
  121. }
  122. namespace UnitTest
  123. {
  124. // Tests if known types maintain their assigned/constructed uuids properly. Changes to this can have significant impact
  125. // various systems such as serialization.
  126. TEST_F(Rtti, KnownTypes)
  127. {
  128. EXPECT_EQ(AZ::Uuid("{3AB0037F-AF8D-48ce-BCA0-A170D18B2C03}"), azrtti_typeid<char>());
  129. EXPECT_EQ(AZ::Uuid("{58422C0E-1E47-4854-98E6-34098F6FE12D}"), azrtti_typeid<AZ::s8>());
  130. EXPECT_EQ(AZ::Uuid("{B8A56D56-A10D-4dce-9F63-405EE243DD3C}"), azrtti_typeid<short>());
  131. EXPECT_EQ(AZ::Uuid("{72039442-EB38-4d42-A1AD-CB68F7E0EEF6}"), azrtti_typeid<int>());
  132. EXPECT_EQ(AZ::Uuid("{8F24B9AD-7C51-46cf-B2F8-277356957325}"), azrtti_typeid<long>());
  133. EXPECT_EQ(AZ::Uuid("{70D8A282-A1EA-462d-9D04-51EDE81FAC2F}"), azrtti_typeid<AZ::s64>());
  134. EXPECT_EQ(AZ::Uuid("{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"), azrtti_typeid<unsigned char>());
  135. EXPECT_EQ(AZ::Uuid("{ECA0B403-C4F8-4b86-95FC-81688D046E40}"), azrtti_typeid<unsigned short>());
  136. EXPECT_EQ(AZ::Uuid("{43DA906B-7DEF-4ca8-9790-854106D3F983}"), azrtti_typeid<unsigned int>());
  137. EXPECT_EQ(AZ::Uuid("{5EC2D6F7-6859-400f-9215-C106F5B10E53}"), azrtti_typeid<unsigned long>());
  138. EXPECT_EQ(AZ::Uuid("{D6597933-47CD-4fc8-B911-63F3E2B0993A}"), azrtti_typeid<AZ::u64>());
  139. EXPECT_EQ(AZ::Uuid("{EA2C3E90-AFBE-44d4-A90D-FAAF79BAF93D}"), azrtti_typeid<float>());
  140. EXPECT_EQ(AZ::Uuid("{110C4B14-11A8-4e9d-8638-5051013A56AC}"), azrtti_typeid<double>());
  141. EXPECT_EQ(AZ::Uuid("{A0CA880C-AFE4-43cb-926C-59AC48496112}"), azrtti_typeid<bool>());
  142. EXPECT_EQ(AZ::Uuid("{E152C105-A133-4d03-BBF8-3D4B2FBA3E2A}"), azrtti_typeid<AZ::Uuid>());
  143. EXPECT_EQ(AZ::Uuid("{C0F1AFAD-5CB3-450E-B0F5-ADB5D46B0E22}"), azrtti_typeid<void>());
  144. EXPECT_EQ(AZ::Uuid("{9F4E062E-06A0-46D4-85DF-E0DA96467D3A}"), azrtti_typeid<AZ::Crc32>());
  145. EXPECT_EQ(AZ::Uuid("{0635D08E-DDD2-48DE-A7AE-73CC563C57C3}"), azrtti_typeid<AZ::PlatformID>());
  146. EXPECT_EQ(AZ::Uuid("{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"), azrtti_typeid<int*>());
  147. EXPECT_EQ(AZ::Uuid("{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"), azrtti_typeid<int&>());
  148. EXPECT_EQ(AZ::Uuid("{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"), azrtti_typeid<int&&>());
  149. EXPECT_EQ(AZ::Uuid("{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"), azrtti_typeid<const int*>());
  150. EXPECT_EQ(AZ::Uuid("{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"), azrtti_typeid<const int&>());
  151. EXPECT_EQ(AZ::Uuid("{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"), azrtti_typeid<const int&&>());
  152. EXPECT_EQ(AZ::Uuid("{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"), azrtti_typeid<const int>());
  153. // TypeId aggregation with template ID as prefix
  154. // Aggregation uses right fold for addition (Id1 + (Id2 + (Id3 + (... + Idn))))
  155. EXPECT_EQ(azrtti_typeid<int>() + azrtti_typeid<char>(), (AZ::Internal::AggregateTypes<int, char>::GetCanonicalTypeId()));
  156. EXPECT_EQ(azrtti_typeid<AZStd::tuple>() + azrtti_typeid<int>(), azrtti_typeid<AZStd::tuple<int>>());
  157. EXPECT_EQ(azrtti_typeid<AZStd::tuple>() + (azrtti_typeid<int>() + azrtti_typeid<char>()), (azrtti_typeid<AZStd::tuple<int, char>>()));
  158. EXPECT_NE(azrtti_typeid<AZStd::tuple>() + azrtti_typeid<int>() + azrtti_typeid<char>(), (azrtti_typeid<AZStd::tuple<int, char>>()));
  159. EXPECT_EQ(AZ::Uuid("{B2F5707A-08FA-566A-BE44-226C634405BE}"), (azrtti_typeid<AZStd::less<int>>()));
  160. EXPECT_EQ(AZ::Uuid("{6D2500BA-EE64-5288-9766-4C7CD8A10476}"), (azrtti_typeid<AZStd::less_equal<int>>()));
  161. EXPECT_EQ(AZ::Uuid("{5959973B-2113-5789-BC8C-2F1E4A917953}"), (azrtti_typeid<AZStd::greater<int>>()));
  162. EXPECT_EQ(AZ::Uuid("{7769141C-BF97-5E9B-B77F-F075FA915905}"), (azrtti_typeid<AZStd::greater_equal<int>>()));
  163. EXPECT_EQ(AZ::Uuid("{39487937-0E1C-5F78-8A7E-B24EFE32F48F}"), (azrtti_typeid<AZStd::equal_to<int>>()));
  164. EXPECT_EQ(AZ::Uuid("{AE785799-21A1-5D89-A083-E4441E1F81A8}"), (azrtti_typeid<AZStd::hash<int>>()));
  165. EXPECT_EQ(AZ::Uuid("{64503325-ECF4-5F02-95F9-E37D00810E59}"), (azrtti_typeid<AZStd::pair<int, int>>()));
  166. EXPECT_EQ(AZ::Uuid("{853CDD8D-12FF-5619-9A42-10178785620A}"), (azrtti_typeid<AZStd::tuple<int, char, float, double>>()));
  167. EXPECT_EQ(AZ::Uuid("{85AFA5E8-AA5C-50A3-9CAB-B8C483DA88C5}"), (azrtti_typeid<AZStd::vector<int>>()));
  168. EXPECT_EQ(AZ::Uuid("{09C2272F-2353-5337-BDCB-B1D0D6A2A778}"), (azrtti_typeid<AZStd::list<int>>()));
  169. EXPECT_EQ(AZ::Uuid("{2D875DAD-A157-5792-AE25-96D909E1BE4C}"), (azrtti_typeid<AZStd::forward_list<int>>()));
  170. EXPECT_EQ(AZ::Uuid("{9DF03CD1-931A-544D-A93B-0546907B70CA}"), (azrtti_typeid<AZStd::set<int>>()));
  171. EXPECT_EQ(AZ::Uuid("{243A34FA-C6F6-51D1-8166-06DED5141370}"), (azrtti_typeid<AZStd::unordered_set<int>>()));
  172. EXPECT_EQ(AZ::Uuid("{79F4B21A-02CD-58C1-9669-FA2E5E7A142A}"), (azrtti_typeid<AZStd::unordered_multiset<int>>()));
  173. EXPECT_EQ(AZ::Uuid("{BB54671F-18E6-5F96-B659-FA236D1B7D31}"), (azrtti_typeid<AZStd::map<int, int>>()));
  174. EXPECT_EQ(AZ::Uuid("{C543E26A-7772-5511-8CE1-A8FA6441CAD3}"), (azrtti_typeid<AZStd::unordered_map<int, int>>()));
  175. EXPECT_EQ(AZ::Uuid("{FD30FBC0-B826-51CF-A75B-E00466FEB0F0}"), (azrtti_typeid<AZStd::unordered_map<AZStd::string, MyClass>>()));
  176. EXPECT_EQ(AZ::Uuid("{64E53B04-DD49-55DB-8299-5B4ED53A5F1C}"), (azrtti_typeid<AZStd::unordered_multimap<int, int>>()));
  177. EXPECT_EQ(AZ::Uuid("{1C213FE1-ED58-5889-8FC9-48D0E11D2E7E}"), (azrtti_typeid<AZStd::unordered_multimap<AZStd::string, MyClass>>()));
  178. EXPECT_EQ(AZ::Uuid("{0BF83553-00B0-5B7C-9BF3-A87C811F0752}"), (azrtti_typeid<AZStd::shared_ptr<int>>()));
  179. EXPECT_EQ(AZ::Uuid("{E91D2018-767D-57D4-AF21-5CBEA51A15EC}"), (azrtti_typeid<AZStd::optional<int>>()));
  180. EXPECT_EQ(AZ::Uuid("{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"), (azrtti_typeid<AZStd::basic_string<char>>()));
  181. EXPECT_EQ(AZ::Uuid("{406E9B16-A89C-5289-B10E-17F338588559}"), (azrtti_typeid<AZStd::char_traits<char>>()));
  182. EXPECT_EQ(AZ::Uuid("{7114E998-A8B4-519B-9342-A86D1587B4F7}"), (azrtti_typeid<AZStd::basic_string_view<char>>()));
  183. EXPECT_EQ(AZ::Uuid("{A3C35B6E-E2DE-58F7-A897-06C64C5BC1E3}"), (azrtti_typeid<AZStd::fixed_vector<int, 4>>()));
  184. EXPECT_EQ(AZ::Uuid("{A3C35B6E-E2DE-58F7-A897-06C64C5BC1E3}"), (azrtti_typeid<AZStd::fixed_vector<int, 4>>()));
  185. EXPECT_EQ(AZ::Uuid("{F670463F-FB3F-5CF3-A1FE-A7CC6DB312E8}"), (azrtti_typeid<AZStd::fixed_list<int, 4>>()));
  186. EXPECT_EQ(AZ::Uuid("{71C90433-74CE-5018-BEFD-FC98F4451AEF}"), (azrtti_typeid<AZStd::fixed_forward_list<int, 4>>()));
  187. EXPECT_EQ(AZ::Uuid("{DD9565F2-A80F-5DD3-B33F-0B0BF1C24A4F}"), (azrtti_typeid<AZStd::array<int, 4>>()));
  188. EXPECT_EQ(AZ::Uuid("{E5848517-FBDC-5D0F-9012-B16951027D9E}"), (azrtti_typeid<AZStd::bitset<8>>()));
  189. EXPECT_EQ(AZ::Uuid("{2962AD0B-AD39-5D19-9548-7AB0E68A1787}"), (azrtti_typeid<AZStd::function<bool(int)>>()));
  190. EXPECT_EQ(AZ::Uuid("{B1E9136B-D77A-4643-BE8E-2ABDA246AE0E}"), (azrtti_typeid<AZStd::monostate>()));
  191. EXPECT_EQ(AZ::Uuid("{7570E0E7-0BA8-5382-BB14-CEB7B1C0DBEB}"), (azrtti_typeid<AZStd::variant<int, char>>()));
  192. }
  193. TEST_F(Rtti, TypeInfoQualifierTest)
  194. {
  195. // The Uuid of a value type should result of a pointer type
  196. EXPECT_EQ(azrtti_typeid<int>(), azrtti_typeid<int*>());
  197. EXPECT_EQ(AZ::AzTypeInfo<int>::Uuid(), AZ::AzTypeInfo<int*>::Uuid());
  198. EXPECT_EQ(AZ::AzTypeInfo<int>::GetCanonicalTypeId(), AZ::AzTypeInfo<int*>::GetPointeeTypeId());
  199. EXPECT_EQ(AZStd::string_view{ AZ::AzTypeInfo<int>::Name() }, AZStd::string_view("int"));
  200. EXPECT_EQ(AZStd::string_view{ AZ::AzTypeInfo<int const>::Name() }, AZStd::string_view("int const"));
  201. EXPECT_EQ(AZStd::string_view{ AZ::AzTypeInfo<int const&>::Name() }, AZStd::string_view("int const&"));
  202. EXPECT_EQ(AZStd::string_view{ AZ::AzTypeInfo<int const&&>::Name() }, AZStd::string_view("int const&&"));
  203. EXPECT_EQ(AZStd::string_view{ AZ::AzTypeInfo<int&>::Name() }, AZStd::string_view("int&"));
  204. EXPECT_EQ(AZStd::string_view{ AZ::AzTypeInfo<int&&>::Name() }, AZStd::string_view("int&&"));
  205. EXPECT_EQ(AZStd::string_view{ AZ::AzTypeInfo<const int*>::Name() }, AZStd::string_view("int const*"));
  206. EXPECT_EQ(AZStd::string_view{ AZ::AzTypeInfo<const int* const>::Name() }, AZStd::string_view("int const* const"));
  207. EXPECT_EQ(AZStd::string_view{ AZ::AzTypeInfo<const int* const&>::Name() }, AZStd::string_view("int const* const&"));
  208. EXPECT_EQ(AZStd::string_view{ AZ::AzTypeInfo<const int* const&&>::Name() }, AZStd::string_view("int const* const&&"));
  209. EXPECT_EQ(AZStd::string_view{ AZ::AzTypeInfo<const int* &>::Name() }, AZStd::string_view("int const*&"));
  210. EXPECT_EQ(AZStd::string_view{ AZ::AzTypeInfo<const int* &&>::Name() }, AZStd::string_view("int const*&&"));
  211. EXPECT_EQ(AZ::AzTypeInfo<const int*>::Uuid(), AZ::AzTypeInfo<int*>::Uuid());
  212. // Validate that a container of value tpyes have a different type id than a container of pointers
  213. EXPECT_NE(azrtti_typeid<AZStd::vector<int>>(), azrtti_typeid<AZStd::vector<int*>>());
  214. }
  215. TEST_F(Rtti, TypeInfoTest)
  216. {
  217. EXPECT_EQ(AZ::Uuid("{CADA6BA7-D479-4C20-B7F0-121A1DF4E9CC}"), AZ::AzTypeInfo<MyClass>::Uuid());
  218. EXPECT_STREQ("MyClass", AZ::AzTypeInfo<MyClass>::Name());
  219. EXPECT_EQ(AZ::Uuid("{38380915-084B-4886-8D3D-B8439E9E987C}"), AZ::AzTypeInfo<ExternalClass>::Uuid());
  220. EXPECT_STREQ("ExternalClass", AZ::AzTypeInfo<ExternalClass>::Name());
  221. // template templates
  222. {
  223. // Check if the correct type id is returned.
  224. AZ::Uuid templateUuid = AZ::Uuid("{EBFE7ADF-1FCE-47F0-B417-14FE06BAF02D}");
  225. EXPECT_EQ(templateUuid, AZ::AzGenericTypeInfo::Uuid<MyClassTemplate>());
  226. // Check that the uuid of the template is returned if AzGenericTypeInfo is used to return the uuid.
  227. EXPECT_EQ(templateUuid, (AZ::AzGenericTypeInfo::Uuid<MyClassTemplate<MyClass, int>>()));
  228. using MyClassTemplateType = MyClassTemplate<MyClass, int>;
  229. EXPECT_EQ(templateUuid, (AZ::AzGenericTypeInfo::Uuid<MyClassTemplateType>()));
  230. // Check all combinations return a valid id.
  231. EXPECT_EQ(AZ::Uuid("{911B2EA8-CCB1-4F0C-A535-540AD00173AE}"), AZ::AzGenericTypeInfo::Uuid<AZStd::array>());
  232. EXPECT_EQ(AZ::Uuid("{6BAE9836-EC49-466A-85F2-F4B1B70839FB}"), AZ::AzGenericTypeInfo::Uuid<AZStd::bitset>());
  233. EXPECT_EQ(AZ::Uuid("{C9F9C644-CCC3-4F77-A792-F5B5DBCA746E}"), AZ::AzGenericTypeInfo::Uuid<AZStd::function>());
  234. EXPECT_EQ(AZ::Uuid("{A60E3E61-1FF6-4982-B6B8-9E4350C4C679}"), AZ::AzGenericTypeInfo::Uuid<AZStd::vector>());
  235. }
  236. // templates
  237. {
  238. AZ::Uuid templateUuid = AZ::Uuid("{EBFE7ADF-1FCE-47F0-B417-14FE06BAF02D}") + AZ::Internal::AggregateTypes<MyClass, int>::GetCanonicalTypeId();
  239. typedef MyClassTemplate<MyClass, int> MyClassTemplateType;
  240. EXPECT_EQ(templateUuid, AZ::AzTypeInfo<MyClassTemplateType>::Uuid());
  241. const char* myClassTemplatename = AZ::AzTypeInfo<MyClassTemplateType>::Name();
  242. EXPECT_NE(nullptr, strstr(myClassTemplatename, "MyClassTemplate"));
  243. EXPECT_NE(nullptr, strstr(myClassTemplatename, "MyClass"));
  244. EXPECT_NE(nullptr, strstr(myClassTemplatename, "int"));
  245. }
  246. // variadic templates
  247. {
  248. AZ::Uuid templateUuid = AZ::Uuid("{60C1D809-09FA-48EB-A9B7-0BD8DBFF21C8}") + AZ::Internal::AggregateTypes<MyClass, int>::GetCanonicalTypeId();
  249. using MyClassVariadicTemplateType = MyClassVariadicTemplate<MyClass, int>;
  250. EXPECT_EQ(templateUuid, AZ::AzTypeInfo<MyClassVariadicTemplateType>::Uuid());
  251. const char* myClassTemplatename = AZ::AzTypeInfo<MyClassVariadicTemplateType>::Name();
  252. EXPECT_NE(nullptr, strstr(myClassTemplatename, "MyClassVariadicTemplate"));
  253. EXPECT_NE(nullptr, strstr(myClassTemplatename, "MyClass"));
  254. EXPECT_NE(nullptr, strstr(myClassTemplatename, "int"));
  255. }
  256. }
  257. class MyBase
  258. {
  259. public:
  260. AZ_TYPE_INFO(MyBase, "{6A0855E5-6899-482B-B470-C3E5C13D13F5}");
  261. virtual ~MyBase() {}
  262. int dataMyBase;
  263. };
  264. class MyBase1
  265. : public MyBase
  266. {
  267. public:
  268. ~MyBase1() override {}
  269. // Event though MyBase doesn't have RTTI we do allow to be noted as a base class, of course it will NOT be
  270. // part of the RTTI chain. The goal is to allow AZ_RTTI to declare any base classes without worry if they have RTTI or not
  271. AZ_RTTI(MyBase1, "{F3F97A32-15D2-48FF-B741-B89EA2DD2280}", MyBase);
  272. int data1MyBase1;
  273. int data2MyBase1;
  274. };
  275. class MyDerived
  276. : public MyBase1
  277. {
  278. public:
  279. ~MyDerived() override {}
  280. AZ_RTTI(MyDerived, "{3BE0590A-F20F-4056-96AF-C2F0565C2EA5}", MyBase1);
  281. int dataMyDerived;
  282. };
  283. class MyDerived1
  284. {
  285. public:
  286. virtual ~MyDerived1() {}
  287. AZ_RTTI(MyDerived1, "{527B6166-1A4F-4782-8D06-F228860B1102}");
  288. int datatypename;
  289. };
  290. class MyDerived2
  291. : public MyDerived
  292. {
  293. public:
  294. ~MyDerived2() override {}
  295. AZ_RTTI(MyDerived2, "{8902C46B-61C5-4294-82A2-06CB61ACA314}", MyDerived);
  296. int dataMyDerived2;
  297. };
  298. class MyClassMix
  299. : public MyDerived2
  300. , public MyDerived1
  301. {
  302. public:
  303. ~MyClassMix() override {}
  304. AZ_RTTI(MyClassMix, "{F6CDCF25-3161-46AE-A46C-0F9B8A1027AF}", MyDerived2, MyDerived1);
  305. int dataMix;
  306. };
  307. class MyClassA
  308. {
  309. public:
  310. virtual ~MyClassA() {}
  311. AZ_RTTI(MyClassA, "{F2D44607-1BB6-4A6D-8D8B-4FDE27B488CF}");
  312. int dataClassA;
  313. };
  314. class MyClassB
  315. {
  316. public:
  317. virtual ~MyClassB() {}
  318. AZ_RTTI(MyClassB, "{E46477C8-4833-4F8C-A57A-02EAFA0C33D8}");
  319. int dataClassB;
  320. };
  321. class MyClassC
  322. {
  323. public:
  324. virtual ~MyClassC() {}
  325. AZ_RTTI(MyClassC, "{614F230F-1AD0-419D-8376-18891112F55D}");
  326. int dataClassC;
  327. };
  328. class MyClassD
  329. : public MyClassA
  330. {
  331. public:
  332. ~MyClassD() override {}
  333. AZ_RTTI(MyClassD, "{8E047831-1445-4D13-8F6F-DD36C871FD05}", MyClassA);
  334. int dataClassD;
  335. };
  336. class MyClassMaxMix
  337. : public MyDerived2
  338. , public MyDerived1
  339. , public MyClassB
  340. , public MyClassC
  341. , public MyClassD
  342. {
  343. public:
  344. ~MyClassMaxMix() override {}
  345. AZ_RTTI(MyClassMaxMix, "{49A7F45B-D039-44ED-A6BF-E500CB84E867}", MyDerived2, MyDerived1, MyClassB, MyClassC, MyClassD);
  346. int dataMaxMix;
  347. };
  348. TEST_F(Rtti, IsTypeOfTest)
  349. {
  350. using TypeIdArray = AZStd::vector<AZ::Uuid>;
  351. auto EnumTypes = [](const AZ::Uuid& id, void* userData)
  352. {
  353. TypeIdArray* idArray = reinterpret_cast<TypeIdArray*>(userData);
  354. idArray->push_back(id);
  355. };
  356. MyBase1 mb1;
  357. MyDerived md;
  358. MyDerived2 md2;
  359. MyClassMix mcm;
  360. MyClassMaxMix mcmm;
  361. EXPECT_FALSE(azrtti_istypeof<MyBase>(mb1));// MyBase has not RTTI enabled, even though it's a base class
  362. EXPECT_FALSE(azrtti_istypeof<MyDerived>(mb1));
  363. EXPECT_TRUE(azrtti_istypeof<MyBase1>(md));
  364. EXPECT_FALSE(azrtti_istypeof<MyBase>(md));
  365. EXPECT_FALSE(azrtti_istypeof<MyDerived2>(md));
  366. EXPECT_TRUE(azrtti_istypeof<MyDerived>(md2));
  367. EXPECT_TRUE(azrtti_istypeof<MyBase1>(md2));
  368. EXPECT_FALSE(azrtti_istypeof<MyBase>(md2));
  369. EXPECT_TRUE(azrtti_istypeof<MyDerived1>(mcm));
  370. EXPECT_TRUE(azrtti_istypeof<MyDerived2>(mcm));
  371. EXPECT_TRUE(azrtti_istypeof<MyDerived>(mcm));
  372. EXPECT_TRUE(azrtti_istypeof<MyBase1>(mcm));
  373. EXPECT_FALSE(azrtti_istypeof<MyBase>(mcm));
  374. EXPECT_TRUE(azrtti_istypeof<MyDerived1>(&mcmm));
  375. EXPECT_TRUE(azrtti_istypeof<MyDerived2>(mcmm));
  376. EXPECT_TRUE(azrtti_istypeof<MyDerived>(mcmm));
  377. EXPECT_TRUE(azrtti_istypeof<MyBase1>(mcmm));
  378. EXPECT_TRUE(azrtti_istypeof<MyClassA>(mcmm));
  379. EXPECT_TRUE(azrtti_istypeof<MyClassB>(mcmm));
  380. EXPECT_TRUE(azrtti_istypeof<MyClassC>(mcmm));
  381. EXPECT_TRUE(azrtti_istypeof<MyClassD>(mcmm));
  382. EXPECT_FALSE(azrtti_istypeof<MyBase>(mcmm));
  383. // type checks
  384. EXPECT_TRUE(azrtti_istypeof<MyBase1&>(md));
  385. EXPECT_TRUE(azrtti_istypeof<const MyBase1&>(md));
  386. EXPECT_TRUE(azrtti_istypeof<const MyBase1>(md));
  387. EXPECT_TRUE(azrtti_istypeof<MyBase1>(&md));
  388. EXPECT_TRUE(azrtti_istypeof<MyBase1&>(&md));
  389. EXPECT_TRUE(azrtti_istypeof<const MyBase1&>(&md));
  390. EXPECT_TRUE(azrtti_istypeof<const MyBase1>(&md));
  391. EXPECT_TRUE(azrtti_istypeof<MyBase1*>(&md));
  392. EXPECT_TRUE(azrtti_istypeof<MyBase1*>(md));
  393. EXPECT_TRUE(azrtti_istypeof<const MyBase1*>(md));
  394. EXPECT_TRUE(azrtti_istypeof<const MyBase1*>(&md));
  395. EXPECT_TRUE(azrtti_istypeof(AZ::AzTypeInfo<const MyBase1>::Uuid(), &md));
  396. EXPECT_TRUE(azrtti_istypeof(AZ::AzTypeInfo<MyBase1>::Uuid(), md));
  397. // template templates
  398. AZStd::vector<int> vector;
  399. AZStd::array<int, 1> array;
  400. AZStd::bitset<8> bitset;
  401. AZStd::function<void()> function;
  402. EXPECT_TRUE(azrtti_istypeof<AZStd::vector>(vector));
  403. EXPECT_TRUE(azrtti_istypeof<AZStd::array>(array));
  404. EXPECT_TRUE(azrtti_istypeof<AZStd::bitset>(bitset));
  405. EXPECT_FALSE(azrtti_istypeof<AZStd::vector>(mb1)); // MyBase has not RTTI enabled, even though it's a base class
  406. EXPECT_FALSE(azrtti_istypeof<AZStd::vector>(md));
  407. // check type enumeration
  408. TypeIdArray typeIds;
  409. // check a single type (no base types)
  410. MyDerived1::RTTI_EnumHierarchy(EnumTypes, &typeIds);
  411. EXPECT_EQ(1, typeIds.size());
  412. EXPECT_EQ(AZ::AzTypeInfo<MyDerived1>::Uuid(), typeIds[0]);
  413. // check a simple inheritance
  414. typeIds.clear();
  415. MyDerived::RTTI_EnumHierarchy(EnumTypes, &typeIds);
  416. EXPECT_EQ(2, typeIds.size());
  417. EXPECT_NE(typeIds.end(), AZStd::find(typeIds.begin(), typeIds.end(), AZ::AzTypeInfo<MyBase1>::Uuid()));
  418. EXPECT_NE(typeIds.end(), AZStd::find(typeIds.begin(), typeIds.end(), AZ::AzTypeInfo<MyDerived>::Uuid()));
  419. // check a little more complicated one
  420. typeIds.clear();
  421. MyClassMix::RTTI_EnumHierarchy(EnumTypes, &typeIds);
  422. EXPECT_EQ(5, typeIds.size());
  423. EXPECT_NE(typeIds.end(), AZStd::find(typeIds.begin(), typeIds.end(), AZ::AzTypeInfo<MyBase1>::Uuid()));
  424. EXPECT_NE(typeIds.end(), AZStd::find(typeIds.begin(), typeIds.end(), AZ::AzTypeInfo<MyDerived>::Uuid()));
  425. EXPECT_NE(typeIds.end(), AZStd::find(typeIds.begin(), typeIds.end(), AZ::AzTypeInfo<MyDerived1>::Uuid()));
  426. EXPECT_NE(typeIds.end(), AZStd::find(typeIds.begin(), typeIds.end(), AZ::AzTypeInfo<MyDerived2>::Uuid()));
  427. EXPECT_NE(typeIds.end(), AZStd::find(typeIds.begin(), typeIds.end(), AZ::AzTypeInfo<MyClassMix>::Uuid()));
  428. // now check the virtual full time selection
  429. MyBase1* mb1Ptr = &mcm;
  430. typeIds.clear();
  431. mb1Ptr->RTTI_EnumTypes(EnumTypes, &typeIds);
  432. EXPECT_EQ(5, typeIds.size());
  433. EXPECT_NE(typeIds.end(), AZStd::find(typeIds.begin(), typeIds.end(), AZ::AzTypeInfo<MyBase1>::Uuid()));
  434. EXPECT_NE(typeIds.end(), AZStd::find(typeIds.begin(), typeIds.end(), AZ::AzTypeInfo<MyDerived>::Uuid()));
  435. EXPECT_NE(typeIds.end(), AZStd::find(typeIds.begin(), typeIds.end(), AZ::AzTypeInfo<MyDerived1>::Uuid()));
  436. EXPECT_NE(typeIds.end(), AZStd::find(typeIds.begin(), typeIds.end(), AZ::AzTypeInfo<MyDerived2>::Uuid()));
  437. EXPECT_NE(typeIds.end(), AZStd::find(typeIds.begin(), typeIds.end(), AZ::AzTypeInfo<MyClassMix>::Uuid()));
  438. }
  439. TEST_F(Rtti, GetGenericTypeIdTest)
  440. {
  441. using IntVector = AZStd::vector<int>;
  442. AZ::IRttiHelper* helper = AZ::GetRttiHelper<IntVector>();
  443. EXPECT_EQ(azrtti_typeid<IntVector>(), helper->GetTypeId());
  444. EXPECT_EQ(azrtti_typeid<AZStd::vector>(), helper->GetGenericTypeId());
  445. EXPECT_EQ(AZ::AzTypeInfo<IntVector>::GetTemplateId(), helper->GetGenericTypeId());
  446. helper = AZ::GetRttiHelper<MyClassMix>();
  447. EXPECT_EQ(azrtti_typeid<MyClassMix>(), helper->GetTypeId());
  448. // MyClassMix isn't a template
  449. EXPECT_TRUE(helper->GetGenericTypeId().IsNull());
  450. // Validate that ADL lookup of Class Template Ids via the AzTypeInfo<T>::GetTemplateId() function
  451. // will work
  452. static_assert(AZ::HasGetO3deTypeName_v<IntVector>);
  453. static_assert(AZ::HasGetO3deTypeId_v<IntVector>);
  454. auto vectorTemplateId = GetO3deClassTemplateId(AZ::Adl{}, AZStd::type_identity<IntVector>{});
  455. EXPECT_FALSE(vectorTemplateId.IsNull());
  456. static_assert(AZ::HasGetO3deClassTemplateId_v<IntVector>);
  457. }
  458. class ExampleAbstractClass
  459. {
  460. public:
  461. AZ_RTTI(ExampleAbstractClass, "{F99EC269-3077-4984-A1B6-FA5656A65AC9}")
  462. virtual void AbstractFunction1() = 0;
  463. virtual void AbstractFunction2() = 0;
  464. };
  465. class ExampleFullImplementationClass : public ExampleAbstractClass
  466. {
  467. public:
  468. AZ_RTTI(ExampleFullImplementationClass, "{81B043ED-3770-414E-8B54-0F623C035926}", ExampleAbstractClass)
  469. void AbstractFunction1() override {}
  470. void AbstractFunction2() override {}
  471. };
  472. class ExamplePartialImplementationClass1
  473. : public ExampleAbstractClass
  474. {
  475. public:
  476. AZ_RTTI(ExamplePartialImplementationClass1, "{049B29D7-0414-4C5F-8FB2-589D0833121B}", ExampleAbstractClass)
  477. void AbstractFunction1() override {}
  478. };
  479. class ExampleCombined
  480. : public ExamplePartialImplementationClass1
  481. {
  482. public:
  483. AZ_RTTI(ExampleCombined, "{0D03E811-F8F1-4AA5-8DA2-4CD6B7FB7080}", ExamplePartialImplementationClass1)
  484. void AbstractFunction2() override {}
  485. };
  486. TEST_F(Rtti, IsAbstract)
  487. {
  488. // compile time proof that the two non-abstract classes are not abstract at compile time:
  489. [[maybe_unused]] ExampleFullImplementationClass one;
  490. [[maybe_unused]] ExampleCombined two;
  491. ASSERT_NE(AZ::GetRttiHelper<ExampleAbstractClass>(), nullptr);
  492. ASSERT_NE(AZ::GetRttiHelper<ExampleFullImplementationClass>(), nullptr);
  493. ASSERT_NE(AZ::GetRttiHelper<ExamplePartialImplementationClass1>(), nullptr);
  494. ASSERT_NE(AZ::GetRttiHelper<ExampleCombined>(), nullptr);
  495. EXPECT_TRUE(AZ::GetRttiHelper<ExampleAbstractClass>()->IsAbstract());
  496. EXPECT_FALSE(AZ::GetRttiHelper<ExampleFullImplementationClass>()->IsAbstract());
  497. EXPECT_TRUE(AZ::GetRttiHelper<ExamplePartialImplementationClass1>()->IsAbstract());
  498. EXPECT_FALSE(AZ::GetRttiHelper<ExampleCombined>()->IsAbstract());
  499. }
  500. TEST_F(Rtti, DynamicCastTest)
  501. {
  502. MyBase1 i_mb1;
  503. MyDerived i_md;
  504. MyDerived2 i_md2;
  505. MyClassMix i_mcm;
  506. MyClassMaxMix i_mcmm;
  507. MyBase1* mb1 = &i_mb1;
  508. MyDerived* md = &i_md;
  509. MyDerived2* md2 = &i_md2;
  510. MyClassMix* mcm = &i_mcm;
  511. MyClassMaxMix* mcmm = &i_mcmm;
  512. // downcast
  513. EXPECT_EQ(nullptr, azdynamic_cast<MyBase*>(mb1));// MyBase has not RTTI enabled, even though it's a base class
  514. EXPECT_EQ(nullptr, azdynamic_cast<MyDerived*>(mb1));
  515. EXPECT_NE(nullptr, azdynamic_cast<MyBase1*>(md));
  516. EXPECT_EQ(nullptr, azdynamic_cast<MyBase*>(md));
  517. EXPECT_EQ(nullptr, azdynamic_cast<MyDerived2*>(md));
  518. EXPECT_NE(nullptr, azdynamic_cast<MyDerived*>(md2));
  519. EXPECT_NE(nullptr, azdynamic_cast<MyBase1*>(md2));
  520. EXPECT_EQ(nullptr, azdynamic_cast<MyBase*>(md2));
  521. EXPECT_NE(nullptr, azdynamic_cast<MyDerived1*>(mcm));
  522. EXPECT_NE(nullptr, azdynamic_cast<MyDerived2*>(mcm));
  523. EXPECT_NE(nullptr, azdynamic_cast<MyDerived*>(mcm));
  524. EXPECT_NE(nullptr, azdynamic_cast<MyBase1*>(mcm));
  525. EXPECT_EQ(nullptr, azdynamic_cast<MyBase*>(mcm));
  526. EXPECT_NE(nullptr, azdynamic_cast<MyDerived1*>(mcmm));
  527. EXPECT_NE(nullptr, azdynamic_cast<MyDerived2*>(mcmm));
  528. EXPECT_NE(nullptr, azdynamic_cast<MyDerived*>(mcmm));
  529. EXPECT_NE(nullptr, azdynamic_cast<MyBase1*>(mcmm));
  530. EXPECT_NE(nullptr, azdynamic_cast<MyClassA*>(mcmm));
  531. EXPECT_NE(nullptr, azdynamic_cast<MyClassB*>(mcmm));
  532. EXPECT_NE(nullptr, azdynamic_cast<MyClassC*>(mcmm));
  533. EXPECT_NE(nullptr, azdynamic_cast<MyClassD*>(mcmm));
  534. EXPECT_EQ(nullptr, azdynamic_cast<MyBase*>(mcmm));
  535. // up cast
  536. mb1 = mcmm;
  537. MyClassA* mca = mcmm;
  538. int i_i;
  539. int* pi = &i_i;
  540. EXPECT_EQ(nullptr, azdynamic_cast<MyBase*>(nullptr));
  541. EXPECT_EQ(nullptr, azdynamic_cast<MyBase*>(pi));
  542. EXPECT_EQ(pi, azdynamic_cast<int*>(pi));
  543. EXPECT_NE(nullptr, azdynamic_cast<MyDerived*>(mb1));
  544. EXPECT_NE(nullptr, azdynamic_cast<MyDerived2*>(mb1));
  545. EXPECT_NE(nullptr, azdynamic_cast<MyClassMaxMix*>(mb1));
  546. EXPECT_NE(nullptr, azdynamic_cast<MyClassD*>(mca));
  547. EXPECT_NE(nullptr, azdynamic_cast<MyClassMaxMix*>(mca));
  548. // type checks
  549. const MyDerived* cmd = md;
  550. EXPECT_NE(nullptr, azdynamic_cast<const MyBase1*>(md));
  551. EXPECT_NE(nullptr, azdynamic_cast<const volatile MyBase1*>(md));
  552. EXPECT_NE(nullptr, azdynamic_cast<const MyBase1*>(cmd));
  553. EXPECT_NE(nullptr, azdynamic_cast<const volatile MyBase1*>(cmd));
  554. // unrelated cast not supported (we can, but why)
  555. //AZ_TEST_ASSERT(azdynamic_cast<MyBase1*>(mca));
  556. md = mcmm;
  557. // serialization helpers
  558. EXPECT_EQ(mcmm, mca->RTTI_AddressOf(AZ::AzTypeInfo<MyClassMaxMix>::Uuid()));
  559. EXPECT_EQ(mcmm, mb1->RTTI_AddressOf(AZ::AzTypeInfo<MyClassMaxMix>::Uuid()));
  560. EXPECT_EQ(mca, mb1->RTTI_AddressOf(AZ::AzTypeInfo<MyClassA>::Uuid()));
  561. EXPECT_EQ(md, mb1->RTTI_AddressOf(AZ::AzTypeInfo<MyDerived>::Uuid()));
  562. EXPECT_EQ(nullptr, md2->RTTI_AddressOf(AZ::AzTypeInfo<MyClassA>::Uuid()));
  563. EXPECT_EQ(mca, mcmm->RTTI_AddressOf(AZ::AzTypeInfo<MyClassA>::Uuid()));
  564. EXPECT_EQ(mb1, mcmm->RTTI_AddressOf(AZ::AzTypeInfo<MyBase1>::Uuid()));
  565. // typeid
  566. EXPECT_EQ(AZ::AzTypeInfo<MyBase>::Uuid(), azrtti_typeid<MyBase>());
  567. EXPECT_EQ(AZ::AzTypeInfo<MyBase1>::Uuid(), azrtti_typeid(i_mb1));
  568. EXPECT_EQ(AZ::AzTypeInfo<MyDerived2>::Uuid(), azrtti_typeid(md2));
  569. EXPECT_EQ(AZ::AzTypeInfo<MyClassMaxMix>::Uuid(), azrtti_typeid(mca));
  570. MyClassA& mcar = i_mcmm;
  571. EXPECT_EQ(AZ::AzTypeInfo<MyClassMaxMix>::Uuid(), azrtti_typeid(mcar));
  572. EXPECT_EQ(AZ::AzTypeInfo<int>::Uuid(), azrtti_typeid<int>());
  573. }
  574. // Struct which uses the Curiously Recuring Template Pattern to provide static
  575. // polymorphism
  576. template <class Derived>
  577. struct RttiVisitorCRTP
  578. {
  579. virtual ~RttiVisitorCRTP() = default;
  580. AZ_RTTI((RttiVisitorCRTP, "{07750558-F992-4F19-A31F-8BF7A2B41AF0}", Derived));
  581. };
  582. template <class T>
  583. struct RttiDerivedVisitor;
  584. AZ_TYPE_INFO_TEMPLATE(RttiDerivedVisitor, "{484C63B4-A9AE-4E82-90A0-C863E9411604}", AZ_TYPE_INFO_CLASS);
  585. template <class T>
  586. struct RttiDerivedVisitor
  587. : RttiVisitorCRTP<RttiDerivedVisitor<T>>
  588. {
  589. using BaseType = RttiVisitorCRTP<RttiDerivedVisitor<T>>;
  590. // Provide declaration for RTTI functions
  591. AZ_RTTI_NO_TYPE_INFO_DECL();
  592. };
  593. // Provide the implemetnation for RTTI functions
  594. AZ_RTTI_NO_TYPE_INFO_IMPL((RttiDerivedVisitor, AZ_TYPE_INFO_CLASS), (RttiVisitorCRTP<RttiDerivedVisitor<T1>>));
  595. TEST_F(Rtti, Rtti_DeclAndImpl_CanWorkaroundCRTP_WithAzTypeInfo_Instantiation)
  596. {
  597. AZStd::unique_ptr<RttiVisitorCRTP<RttiDerivedVisitor<int>>> basePtr = AZStd::make_unique<RttiDerivedVisitor<int>>();
  598. // Check if the instance is of the derived visitor type
  599. EXPECT_TRUE(basePtr->RTTI_IsTypeOf(azrtti_typeid<RttiDerivedVisitor<int>>()));
  600. // Check if the instance is also of the derived base class type
  601. EXPECT_TRUE(basePtr->RTTI_IsTypeOf(azrtti_typeid<RttiVisitorCRTP<RttiDerivedVisitor<int>>>()));
  602. }
  603. TEST_F(Rtti, MultiThreadedTypeInfo)
  604. {
  605. // These must be AZ::Uuids so that they don't engage the AZ::UuidHolder code
  606. const AZ::Uuid expectedMtti("{4876C017-0C26-4D0D-9A1F-2A738BAE6449}");
  607. const AZ::Uuid expectedMtti2("{CBC94693-5ECD-4CBF-A8DB-9B122E697E8D}");
  608. // Create 2x of each of these threads which are doing RTTI ops and
  609. // let the scheduler run them at random. This is attempting to crash
  610. // them into each other as best we can
  611. auto threadFunc1 = [&expectedMtti, &expectedMtti2]()
  612. {
  613. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1));
  614. const AZ::TypeId& mtti = azrtti_typeid<MTTI>();
  615. const AZ::TypeId& mtti2 = azrtti_typeid<MTTI2>();
  616. EXPECT_FALSE(mtti.IsNull());
  617. EXPECT_EQ(expectedMtti, mtti);
  618. EXPECT_FALSE(mtti2.IsNull());
  619. EXPECT_EQ(expectedMtti2, mtti2);
  620. };
  621. auto threadFunc2 = []()
  622. {
  623. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1));
  624. MTTI* mtti = new MTTI();
  625. bool castSucceeded = (azrtti_cast<MTTI2*>(mtti) != nullptr);
  626. EXPECT_FALSE(castSucceeded);
  627. delete mtti;
  628. };
  629. auto threadFunc3 = []()
  630. {
  631. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1));
  632. MTTI2* mtti2 = new MTTI2();
  633. bool castSucceeded = (azrtti_cast<MTTI*>(mtti2) != nullptr);
  634. EXPECT_FALSE(castSucceeded);
  635. delete mtti2;
  636. };
  637. auto threadFunc4 = []()
  638. {
  639. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1));
  640. MTTI* mtti = new MTTI();
  641. bool castSucceeded = (azrtti_cast<MTTI*>(mtti) != nullptr);
  642. EXPECT_TRUE(castSucceeded);
  643. delete mtti;
  644. };
  645. auto threadFunc5 = []()
  646. {
  647. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1));
  648. MTTI2* mtti2 = new MTTI2();
  649. bool castSucceeded = (azrtti_cast<MTTI2*>(mtti2) != nullptr);
  650. EXPECT_TRUE(castSucceeded);
  651. delete mtti2;
  652. };
  653. AZStd::fixed_vector<AZStd::function<void()>, 5> threadFuncs({ threadFunc1, threadFunc2, threadFunc3, threadFunc4, threadFunc5 });
  654. AZStd::thread threads[10];
  655. for (size_t threadIdx = 0; threadIdx < AZ_ARRAY_SIZE(threads); ++threadIdx)
  656. {
  657. auto threadFunc = threadFuncs[threadIdx % threadFuncs.size()];
  658. threads[threadIdx] = AZStd::thread(threadFunc);
  659. }
  660. for (auto& thread : threads)
  661. {
  662. thread.join();
  663. }
  664. }
  665. static void ExternalRttiEnumHeirarchyHelper(const AZ::TypeId&, void* userData)
  666. {
  667. auto totalClassesEnumerated = reinterpret_cast<size_t*>(userData);
  668. ++*totalClassesEnumerated;
  669. }
  670. class MyBaseExternal
  671. {
  672. public:
  673. AZ_TYPE_INFO(MyBaseExternal, "{F0F36BB2-14E6-4C44-B3D5-E0CBFD783C99}");
  674. int32_t m_intValue;
  675. };
  676. class MyDerivedExternal
  677. : public MyBaseExternal
  678. {
  679. public:
  680. AZ_TYPE_INFO(MyDerivedExternal, "{FFD1C3B7-7957-4270-BF10-700CE8BE2B53}");
  681. float m_floatValue;
  682. };
  683. class MyConvertibleExternal
  684. {
  685. public:
  686. AZ_TYPE_INFO(MyConvertibleExternal, "{3962F510-309B-4E32-8CE5-6DEE85F351A9}");
  687. MyConvertibleExternal() = default;
  688. MyConvertibleExternal(const MyBaseExternal& baseExternal)
  689. : m_baseExternal(baseExternal)
  690. {
  691. }
  692. operator MyBaseExternal() const
  693. {
  694. return m_baseExternal;
  695. }
  696. MyBaseExternal m_baseExternal;
  697. };
  698. class MyBaseIntrusive
  699. {
  700. public:
  701. AZ_RTTI(MyBaseIntrusive, "{06D41B30-CEDB-46C9-BD98-B8672A04F71F}");
  702. virtual ~MyBaseIntrusive() = default;
  703. uint64_t m_uintValue;
  704. };
  705. class MyDerivedIntrusive
  706. : public MyBaseIntrusive
  707. {
  708. public:
  709. AZ_RTTI(MyDerivedIntrusive, "{6F3FA2A5-CD05-424F-8E37-1DEDA7CE8816}", MyBaseIntrusive);
  710. ~MyDerivedIntrusive() override = default;
  711. double m_doubleValue;
  712. };
  713. class MyExternalDerivedFromExternalAndIntrusive
  714. : public MyDerivedExternal
  715. , public MyDerivedIntrusive
  716. {
  717. public:
  718. AZ_TYPE_INFO(MyExternalDerivedFromExternalAndIntrusive, "{79DC295D-98C5-4FEB-9DC0-0AC3D5A91855}");
  719. };
  720. }
  721. namespace AZ
  722. {
  723. AZ_EXTERNAL_RTTI_SPECIALIZE(UnitTest::MyBaseExternal);
  724. AZ_EXTERNAL_RTTI_SPECIALIZE(UnitTest::MyDerivedExternal, UnitTest::MyBaseExternal);
  725. AZ_EXTERNAL_RTTI_SPECIALIZE(UnitTest::MyExternalDerivedFromExternalAndIntrusive, UnitTest::MyDerivedExternal, UnitTest::MyDerivedIntrusive);
  726. AZ_EXTERNAL_RTTI_SPECIALIZE(UnitTest::MyConvertibleExternal, UnitTest::MyBaseExternal);
  727. }
  728. namespace UnitTest
  729. {
  730. class MyIntrusiveDerivedFromExternalAndIntrusive
  731. : public MyDerivedExternal
  732. , public MyDerivedIntrusive
  733. {
  734. public:
  735. AZ_RTTI(MyIntrusiveDerivedFromExternalAndIntrusive, "{3822CF8D-6AC7-4B71-B755-5C69B9DF5A3C}", MyDerivedExternal, MyDerivedIntrusive);
  736. ~MyIntrusiveDerivedFromExternalAndIntrusive() override = default;
  737. };
  738. TEST_F(Rtti, ExternalRtti)
  739. {
  740. MyBaseExternal baseInstance{ 7 };
  741. MyDerivedExternal derivedInstance;
  742. derivedInstance.m_intValue = 15;
  743. derivedInstance.m_floatValue = 0.0f;
  744. MyConvertibleExternal convertibleInstance(MyBaseExternal{ 24 });
  745. MyExternalDerivedFromExternalAndIntrusive externalDerivedFromExternalAndIntrusiveInstance;
  746. externalDerivedFromExternalAndIntrusiveInstance.m_intValue = -1;
  747. externalDerivedFromExternalAndIntrusiveInstance.m_uintValue = 2;
  748. externalDerivedFromExternalAndIntrusiveInstance.m_floatValue = 2.0f;
  749. externalDerivedFromExternalAndIntrusiveInstance.m_doubleValue = -32.0;
  750. MyIntrusiveDerivedFromExternalAndIntrusive intrusiveDerivedFromExternalAndIntrusiveInstance;
  751. intrusiveDerivedFromExternalAndIntrusiveInstance.m_intValue = -55;
  752. intrusiveDerivedFromExternalAndIntrusiveInstance.m_uintValue = 256;
  753. intrusiveDerivedFromExternalAndIntrusiveInstance.m_floatValue = -1023.0f;
  754. intrusiveDerivedFromExternalAndIntrusiveInstance.m_doubleValue = .0223;
  755. AZ::IRttiHelper* baseExternal = AZ::GetRttiHelper<MyBaseExternal>();
  756. AZ::IRttiHelper* derivedExternal = AZ::GetRttiHelper<MyDerivedExternal>();
  757. AZ::IRttiHelper* convertibleExternal = AZ::GetRttiHelper<MyConvertibleExternal>();
  758. AZ::IRttiHelper* externalDerivedFromExternalAndIntrusive = AZ::GetRttiHelper<MyExternalDerivedFromExternalAndIntrusive>();
  759. AZ::IRttiHelper* intrusiveDerivedFromExternalAndIntrusive = AZ::GetRttiHelper<MyIntrusiveDerivedFromExternalAndIntrusive>();
  760. ASSERT_NE(nullptr, baseExternal);
  761. ASSERT_NE(nullptr, derivedExternal);
  762. ASSERT_NE(nullptr, convertibleExternal);
  763. ASSERT_NE(nullptr, externalDerivedFromExternalAndIntrusive);
  764. ASSERT_NE(nullptr, intrusiveDerivedFromExternalAndIntrusive);
  765. // Base Class External RTTI
  766. {
  767. EXPECT_EQ(AZ::AzTypeInfo<MyBaseExternal>::Uuid(), baseExternal->GetTypeId());
  768. EXPECT_TRUE(baseExternal->IsTypeOf(AZ::AzTypeInfo<MyBaseExternal>::Uuid()));
  769. size_t enumHierarchyTotalClasses{};
  770. baseExternal->EnumHierarchy(&ExternalRttiEnumHeirarchyHelper, &enumHierarchyTotalClasses);
  771. // MyBaseExternal has no other base classes so this count should be 1
  772. EXPECT_EQ(1, enumHierarchyTotalClasses);
  773. }
  774. // Derived Class External RTTI
  775. {
  776. EXPECT_EQ(AZ::AzTypeInfo<MyDerivedExternal>::Uuid(), derivedExternal->GetTypeId());
  777. EXPECT_TRUE(derivedExternal->IsTypeOf(AZ::AzTypeInfo<MyDerivedExternal>::Uuid()));
  778. EXPECT_TRUE(derivedExternal->IsTypeOf(AZ::AzTypeInfo<MyBaseExternal>::Uuid()));
  779. size_t enumHierarchyTotalClasses{};
  780. derivedExternal->EnumHierarchy(&ExternalRttiEnumHeirarchyHelper, &enumHierarchyTotalClasses);
  781. // MyDerivedExternal has MyBaseExternal as a base classes so this count should be 2
  782. EXPECT_EQ(2, enumHierarchyTotalClasses);
  783. // MyDerivedExternal -> MyDerivedExternal - succeeds
  784. EXPECT_NE(nullptr, derivedExternal->Cast(&derivedInstance, AZ::AzTypeInfo<MyDerivedExternal>::Uuid()));
  785. // MyDerivedExternal -> MyBaseExternal - succeeds
  786. EXPECT_NE(nullptr, derivedExternal->Cast(&derivedInstance, AZ::AzTypeInfo<MyBaseExternal>::Uuid()));
  787. // MyBaseExternal -> MyDerivedExternal - fails
  788. EXPECT_EQ(nullptr, baseExternal->Cast<MyDerivedExternal>(&baseInstance));
  789. // MyBaseExternal -> MyBaseExternal(using derived class RttiHelper)- succeeds
  790. EXPECT_NE(nullptr, derivedExternal->Cast(&baseInstance, AZ::AzTypeInfo<MyBaseExternal>::Uuid()));
  791. // MyBaseExternal -> MyBaseExternal(using RttiCast function which must lookup RTTI information from the derived instance)- fails
  792. // The reason why this fails is because the instance data does not have RTTI on it so it must lookup using
  793. // using the supplied template type id
  794. EXPECT_NE(nullptr, AZ::RttiCast<MyBaseExternal*>(&derivedInstance));
  795. }
  796. // Convertible Class External RTTI
  797. {
  798. EXPECT_EQ(AZ::AzTypeInfo<MyConvertibleExternal>::Uuid(), convertibleExternal->GetTypeId());
  799. EXPECT_TRUE(convertibleExternal->IsTypeOf(AZ::AzTypeInfo<MyConvertibleExternal>::Uuid()));
  800. EXPECT_TRUE(convertibleExternal->IsTypeOf(AZ::AzTypeInfo<MyBaseExternal>::Uuid()));
  801. size_t enumHierarchyTotalClasses{};
  802. convertibleExternal->EnumHierarchy(&ExternalRttiEnumHeirarchyHelper, &enumHierarchyTotalClasses);
  803. // MyConvertibleExternal specifies "MyBaseExternal" as a base classes even though it really is not,
  804. // but EnumHierarchy should still enumerate for the "MyBaseExternal" typeid. Therefore the count should be 2
  805. EXPECT_EQ(2, enumHierarchyTotalClasses);
  806. // MyConvertibleExternal -> MyConvertibleExternal - succeeds
  807. EXPECT_NE(nullptr, convertibleExternal->Cast(&convertibleInstance, AZ::AzTypeInfo<MyConvertibleExternal>::Uuid()));
  808. // MyConvertibleExternal -> MyBaseExternal - succeeds
  809. EXPECT_NE(nullptr, convertibleExternal->Cast(&convertibleInstance, AZ::AzTypeInfo<MyBaseExternal>::Uuid()));
  810. // MyBaseExternal -> MyConvertibleExternal - fails
  811. EXPECT_EQ(nullptr, baseExternal->Cast<MyConvertibleExternal>(&baseInstance));
  812. // MyBaseExternal -> MyBaseExternal(using convertible class RttiHelper)- succeeds
  813. EXPECT_NE(nullptr, convertibleExternal->Cast(&baseInstance, AZ::AzTypeInfo<MyBaseExternal>::Uuid()));
  814. // MyBaseExternal -> MyBaseExternal(using RttiCast function which must lookup RTTI information from the derived instance)- succeeds
  815. EXPECT_NE(nullptr, AZ::RttiCast<MyBaseExternal*>(&derivedInstance));
  816. }
  817. // Derived class with External RTTI which inherits from a class with external RTTI and intrusive RTTI
  818. {
  819. EXPECT_EQ(AZ::AzTypeInfo<MyExternalDerivedFromExternalAndIntrusive>::Uuid(), externalDerivedFromExternalAndIntrusive->GetTypeId());
  820. EXPECT_TRUE(externalDerivedFromExternalAndIntrusive->IsTypeOf(AZ::AzTypeInfo<MyExternalDerivedFromExternalAndIntrusive>::Uuid()));
  821. EXPECT_TRUE(externalDerivedFromExternalAndIntrusive->IsTypeOf(AZ::AzTypeInfo<MyDerivedExternal>::Uuid()));
  822. EXPECT_TRUE(externalDerivedFromExternalAndIntrusive->IsTypeOf(AZ::AzTypeInfo<MyDerivedIntrusive>::Uuid()));
  823. EXPECT_TRUE(externalDerivedFromExternalAndIntrusive->IsTypeOf(AZ::AzTypeInfo<MyBaseExternal>::Uuid()));
  824. EXPECT_TRUE(externalDerivedFromExternalAndIntrusive->IsTypeOf(AZ::AzTypeInfo<MyBaseIntrusive>::Uuid()));
  825. size_t enumHierarchyTotalClasses{};
  826. externalDerivedFromExternalAndIntrusive->EnumHierarchy(&ExternalRttiEnumHeirarchyHelper, &enumHierarchyTotalClasses);
  827. // MyDerivedFromExternalAndIntrusive inherits from MyDerivedExternal which has one base class with external RTTI.
  828. // This adds 2 to the enumeration count.
  829. // MyDerivedFromExternalAndIntrusive also inherits from MyDerivedIntrusive which has one base with intrusive RTTI
  830. // This adds 2 more the enumeration count. Combining these counts with the one for this class the count value should be 5
  831. EXPECT_EQ(5, enumHierarchyTotalClasses);
  832. // MyDerivedFromExternalAndIntrusive -> MyDerivedFromExternalAndIntrusive - succeeds
  833. EXPECT_NE(nullptr, externalDerivedFromExternalAndIntrusive->Cast(&externalDerivedFromExternalAndIntrusiveInstance, AZ::AzTypeInfo<MyExternalDerivedFromExternalAndIntrusive>::Uuid()));
  834. // MyDerivedFromExternalAndIntrusive -> MyDerivedExternal - succeeds
  835. EXPECT_NE(nullptr, externalDerivedFromExternalAndIntrusive->Cast(&externalDerivedFromExternalAndIntrusiveInstance, AZ::AzTypeInfo<MyDerivedExternal>::Uuid()));
  836. // MyDerivedFromExternalAndIntrusive -> MyBaseExternal - succeeds
  837. EXPECT_NE(nullptr, externalDerivedFromExternalAndIntrusive->Cast(&externalDerivedFromExternalAndIntrusiveInstance, AZ::AzTypeInfo<MyBaseExternal>::Uuid()));
  838. // MyDerivedFromExternalAndIntrusive -> MyDerivedIntrusive- succeeds
  839. MyDerivedIntrusive* castedDerivedIntrusiveInstance = externalDerivedFromExternalAndIntrusive->Cast<MyDerivedIntrusive>(&externalDerivedFromExternalAndIntrusiveInstance);
  840. ASSERT_NE(nullptr, castedDerivedIntrusiveInstance);
  841. EXPECT_DOUBLE_EQ(-32.0, castedDerivedIntrusiveInstance->m_doubleValue);
  842. castedDerivedIntrusiveInstance->m_doubleValue = -64.0; // Verify that access doesn't crash due to invalid memory address
  843. // MyDerivedFromExternalAndIntrusive -> MyBaseIntrusive- succeeds
  844. MyBaseIntrusive* castedBaseIntrusiveInstance = externalDerivedFromExternalAndIntrusive->Cast<MyBaseIntrusive>(&externalDerivedFromExternalAndIntrusiveInstance);
  845. ASSERT_NE(nullptr, castedBaseIntrusiveInstance);
  846. EXPECT_EQ(2U, castedBaseIntrusiveInstance->m_uintValue);
  847. castedDerivedIntrusiveInstance->m_uintValue = 4U;
  848. // MyDerivedExternal -> MyDerivedFromExternalAndIntrusive - fails
  849. EXPECT_EQ(nullptr, derivedExternal->Cast<MyExternalDerivedFromExternalAndIntrusive>(&derivedInstance));
  850. // MyBaseExternal -> MyDerivedFromExternalAndIntrusive - fails
  851. EXPECT_EQ(nullptr, baseExternal->Cast<MyExternalDerivedFromExternalAndIntrusive>(&baseInstance));
  852. // MyBaseExternal -> MyBaseExternal(using externalDerivedFromExternalAndIntrusive class RttiHelper)- succeeds
  853. EXPECT_NE(nullptr, externalDerivedFromExternalAndIntrusive->Cast(&baseInstance, AZ::AzTypeInfo<MyBaseExternal>::Uuid()));
  854. // MyDerivedExternal -> MyBaseExternal(using externalDerivedFromExternalAndIntrusive class RttiHelper)- succeeds
  855. EXPECT_NE(nullptr, externalDerivedFromExternalAndIntrusive->Cast(&derivedInstance, AZ::AzTypeInfo<MyBaseExternal>::Uuid()));
  856. // MyBaseIntrusive -> MyBaseIntrusive(using externalDerivedFromExternalAndIntrusive class RttiHelper)- succeeds
  857. MyBaseIntrusive baseIntrusiveInstance;
  858. baseIntrusiveInstance.m_uintValue = 3456893U;
  859. EXPECT_NE(nullptr, externalDerivedFromExternalAndIntrusive->Cast(&baseIntrusiveInstance, AZ::AzTypeInfo<MyBaseIntrusive>::Uuid()));
  860. // MyDerivedIntrusive-> MyBaseIntrusive(using externalDerivedFromExternalAndIntrusive class RttiHelper)- succeeds
  861. MyDerivedIntrusive derivedIntrusiveInstance;
  862. derivedIntrusiveInstance.m_uintValue = 1700U;
  863. derivedIntrusiveInstance.m_doubleValue = 24.0f;
  864. EXPECT_NE(nullptr, externalDerivedFromExternalAndIntrusive->Cast(&derivedIntrusiveInstance, AZ::AzTypeInfo<MyBaseIntrusive>::Uuid()));
  865. // Test Rtti Free functions for External class with external Rtti
  866. enumHierarchyTotalClasses = 0;
  867. AZ::RttiEnumHierarchy<MyExternalDerivedFromExternalAndIntrusive>(&ExternalRttiEnumHeirarchyHelper, &enumHierarchyTotalClasses);
  868. EXPECT_EQ(5, enumHierarchyTotalClasses);
  869. // This should fail
  870. EXPECT_EQ(nullptr, AZ::RttiCast<MyExternalDerivedFromExternalAndIntrusive*>(&derivedIntrusiveInstance));
  871. EXPECT_NE(nullptr, AZ::RttiCast<MyExternalDerivedFromExternalAndIntrusive*>(&externalDerivedFromExternalAndIntrusiveInstance));
  872. void* baseIntrusiveAddress = AZ::RttiAddressOf(&externalDerivedFromExternalAndIntrusiveInstance, AZ::AzTypeInfo<MyBaseIntrusive>::Uuid());
  873. ASSERT_NE(nullptr, baseIntrusiveAddress);
  874. EXPECT_EQ(4U, static_cast<MyBaseIntrusive*>(baseIntrusiveAddress)->m_uintValue);
  875. EXPECT_FALSE(AZ::RttiIsTypeOf(AZ::AzTypeInfo<MyConvertibleExternal>::Uuid(), externalDerivedFromExternalAndIntrusiveInstance));
  876. EXPECT_FALSE(AZ::RttiIsTypeOf<MyConvertibleExternal>(externalDerivedFromExternalAndIntrusiveInstance));
  877. EXPECT_TRUE(AZ::RttiIsTypeOf(AZ::AzTypeInfo<MyDerivedIntrusive>::Uuid(), externalDerivedFromExternalAndIntrusiveInstance));
  878. EXPECT_TRUE(AZ::RttiIsTypeOf<MyDerivedExternal>(externalDerivedFromExternalAndIntrusiveInstance));
  879. // Check pointer case template specializations for RttiIsTypeOf
  880. EXPECT_TRUE(AZ::RttiIsTypeOf(AZ::AzTypeInfo<MyBaseExternal>::Uuid(), &externalDerivedFromExternalAndIntrusiveInstance));
  881. EXPECT_TRUE(AZ::RttiIsTypeOf<MyBaseExternal>(&externalDerivedFromExternalAndIntrusiveInstance));
  882. EXPECT_EQ(AZ::AzTypeInfo<MyExternalDerivedFromExternalAndIntrusive>::Uuid(), AZ::RttiTypeId(externalDerivedFromExternalAndIntrusiveInstance));
  883. // Check pointer case template specializations for RttiTypeId
  884. EXPECT_EQ(AZ::AzTypeInfo<MyExternalDerivedFromExternalAndIntrusive>::Uuid(), AZ::RttiTypeId(&externalDerivedFromExternalAndIntrusiveInstance));
  885. }
  886. // Derived class with Intrusive RTTI which inherits from a class with external RTTI and intrusive RTTI
  887. {
  888. EXPECT_EQ(AZ::AzTypeInfo<MyIntrusiveDerivedFromExternalAndIntrusive>::Uuid(), intrusiveDerivedFromExternalAndIntrusive->GetTypeId());
  889. EXPECT_TRUE(intrusiveDerivedFromExternalAndIntrusive->IsTypeOf(AZ::AzTypeInfo<MyIntrusiveDerivedFromExternalAndIntrusive>::Uuid()));
  890. EXPECT_TRUE(intrusiveDerivedFromExternalAndIntrusive->IsTypeOf(AZ::AzTypeInfo<MyDerivedExternal>::Uuid()));
  891. EXPECT_TRUE(intrusiveDerivedFromExternalAndIntrusive->IsTypeOf(AZ::AzTypeInfo<MyDerivedIntrusive>::Uuid()));
  892. EXPECT_TRUE(intrusiveDerivedFromExternalAndIntrusive->IsTypeOf(AZ::AzTypeInfo<MyBaseExternal>::Uuid()));
  893. EXPECT_TRUE(intrusiveDerivedFromExternalAndIntrusive->IsTypeOf(AZ::AzTypeInfo<MyBaseIntrusive>::Uuid()));
  894. size_t enumHierarchyTotalClasses{};
  895. intrusiveDerivedFromExternalAndIntrusive->EnumHierarchy(&ExternalRttiEnumHeirarchyHelper, &enumHierarchyTotalClasses);
  896. // MyIntrusiveDerivedFromExternalAndIntrusive inherits from MyDerivedExternal which has one base class with intrusive RTTI.
  897. // This adds 2 to the enumeration count.
  898. // MyIntrusiveDerivedFromExternalAndIntrusive also inherits from MyDerivedIntrusive which has one base with intrusive RTTI
  899. // This adds 2 more the enumeration count. Combining these counts with the one for this class the count value should be 5
  900. EXPECT_EQ(5, enumHierarchyTotalClasses);
  901. // MyIntrusiveDerivedFromExternalAndIntrusive -> MyIntrusiveDerivedFromExternalAndIntrusive - succeeds
  902. EXPECT_NE(nullptr, intrusiveDerivedFromExternalAndIntrusive->Cast(&intrusiveDerivedFromExternalAndIntrusiveInstance, AZ::AzTypeInfo<MyIntrusiveDerivedFromExternalAndIntrusive>::Uuid()));
  903. // MyIntrusiveDerivedFromExternalAndIntrusive -> MyDerivedExternal - succeeds
  904. EXPECT_NE(nullptr, intrusiveDerivedFromExternalAndIntrusive->Cast(&intrusiveDerivedFromExternalAndIntrusiveInstance, AZ::AzTypeInfo<MyDerivedExternal>::Uuid()));
  905. // MyIntrusiveDerivedFromExternalAndIntrusive -> MyBaseExternal - succeeds
  906. EXPECT_NE(nullptr, intrusiveDerivedFromExternalAndIntrusive->Cast(&intrusiveDerivedFromExternalAndIntrusiveInstance, AZ::AzTypeInfo<MyBaseExternal>::Uuid()));
  907. // MyIntrusiveDerivedFromExternalAndIntrusive -> MyDerivedIntrusive- succeeds
  908. MyDerivedIntrusive* castedDerivedIntrusiveInstance = intrusiveDerivedFromExternalAndIntrusive->Cast<MyDerivedIntrusive>(&intrusiveDerivedFromExternalAndIntrusiveInstance);
  909. ASSERT_NE(nullptr, castedDerivedIntrusiveInstance);
  910. EXPECT_DOUBLE_EQ(.0223, castedDerivedIntrusiveInstance->m_doubleValue);
  911. castedDerivedIntrusiveInstance->m_doubleValue = -64.0; // Verify that access doesn't crash due to invalid memory address
  912. // MyIntrusiveDerivedFromExternalAndIntrusive -> MyBaseIntrusive- succeeds
  913. MyBaseIntrusive* castedBaseIntrusiveInstance = intrusiveDerivedFromExternalAndIntrusive->Cast<MyBaseIntrusive>(&intrusiveDerivedFromExternalAndIntrusiveInstance);
  914. ASSERT_NE(nullptr, castedBaseIntrusiveInstance);
  915. EXPECT_EQ(256U, castedBaseIntrusiveInstance->m_uintValue);
  916. castedDerivedIntrusiveInstance->m_uintValue = 71U;
  917. // MyDerivedExternal -> MyIntrusiveDerivedFromExternalAndIntrusive - fails
  918. EXPECT_EQ(nullptr, derivedExternal->Cast<MyIntrusiveDerivedFromExternalAndIntrusive>(&derivedInstance));
  919. // MyBaseExternal -> MyIntrusiveDerivedFromExternalAndIntrusive - fails
  920. EXPECT_EQ(nullptr, baseExternal->Cast<MyIntrusiveDerivedFromExternalAndIntrusive>(&baseInstance));
  921. // MyBaseIntrusive -> MyBaseIntrusive(using intrusiveDerivedFromExternalAndIntrusive class RttiHelper)- succeeds
  922. MyBaseIntrusive baseIntrusiveInstance;
  923. baseIntrusiveInstance.m_uintValue = 3456893U;
  924. EXPECT_NE(nullptr, intrusiveDerivedFromExternalAndIntrusive->Cast(&baseIntrusiveInstance, AZ::AzTypeInfo<MyBaseIntrusive>::Uuid()));
  925. // MyDerivedIntrusive-> MyBaseIntrusive(using intrusiveDerivedFromExternalAndIntrusive class RttiHelper)- succeeds
  926. MyDerivedIntrusive derivedIntrusiveInstance;
  927. derivedIntrusiveInstance.m_uintValue = 1700U;
  928. derivedIntrusiveInstance.m_doubleValue = 24.0f;
  929. EXPECT_NE(nullptr, intrusiveDerivedFromExternalAndIntrusive->Cast(&derivedIntrusiveInstance, AZ::AzTypeInfo<MyBaseIntrusive>::Uuid()));
  930. // Test Rtti Free functions for class with intrusive Rtti
  931. enumHierarchyTotalClasses = 0;
  932. AZ::RttiEnumHierarchy<MyIntrusiveDerivedFromExternalAndIntrusive>(&ExternalRttiEnumHeirarchyHelper, &enumHierarchyTotalClasses);
  933. EXPECT_EQ(5, enumHierarchyTotalClasses);
  934. // This should fail
  935. EXPECT_EQ(nullptr, AZ::RttiCast<MyIntrusiveDerivedFromExternalAndIntrusive*>(&derivedIntrusiveInstance));
  936. EXPECT_NE(nullptr, AZ::RttiCast<MyIntrusiveDerivedFromExternalAndIntrusive*>(&intrusiveDerivedFromExternalAndIntrusiveInstance));
  937. void* baseIntrusiveAddress = AZ::RttiAddressOf(&intrusiveDerivedFromExternalAndIntrusiveInstance, AZ::AzTypeInfo<MyBaseIntrusive>::Uuid());
  938. ASSERT_NE(nullptr, baseIntrusiveAddress);
  939. EXPECT_EQ(71U, static_cast<MyBaseIntrusive*>(baseIntrusiveAddress)->m_uintValue);
  940. EXPECT_FALSE(AZ::RttiIsTypeOf(AZ::AzTypeInfo<MyConvertibleExternal>::Uuid(), intrusiveDerivedFromExternalAndIntrusiveInstance));
  941. EXPECT_FALSE(AZ::RttiIsTypeOf<MyConvertibleExternal>(intrusiveDerivedFromExternalAndIntrusiveInstance));
  942. EXPECT_TRUE(AZ::RttiIsTypeOf(AZ::AzTypeInfo<MyDerivedIntrusive>::Uuid(), intrusiveDerivedFromExternalAndIntrusiveInstance));
  943. EXPECT_TRUE(AZ::RttiIsTypeOf<MyDerivedExternal>(intrusiveDerivedFromExternalAndIntrusiveInstance));
  944. // Check pointer case template specializations for RttiIsTypeOf
  945. EXPECT_TRUE(AZ::RttiIsTypeOf(AZ::AzTypeInfo<MyBaseExternal>::Uuid(), &intrusiveDerivedFromExternalAndIntrusiveInstance));
  946. EXPECT_TRUE(AZ::RttiIsTypeOf<MyBaseExternal>(&intrusiveDerivedFromExternalAndIntrusiveInstance));
  947. EXPECT_EQ(AZ::AzTypeInfo<MyIntrusiveDerivedFromExternalAndIntrusive>::Uuid(), AZ::RttiTypeId(intrusiveDerivedFromExternalAndIntrusiveInstance));
  948. // Check pointer case template specializations for RttiTypeId
  949. EXPECT_EQ(AZ::AzTypeInfo<MyIntrusiveDerivedFromExternalAndIntrusive>::Uuid(), AZ::RttiTypeId(&intrusiveDerivedFromExternalAndIntrusiveInstance));
  950. }
  951. }
  952. TEST_F(Rtti, ExternalRttiStoresTypeTraits)
  953. {
  954. AZ::IRttiHelper* externalRtti = AZ::GetRttiHelper<UnitTest::MyExternalDerivedFromExternalAndIntrusive>();
  955. ASSERT_NE(nullptr, externalRtti);
  956. EXPECT_NE(AZ::TypeTraits::is_signed, externalRtti->GetTypeTraits() & AZ::TypeTraits::is_signed);
  957. EXPECT_NE(AZ::TypeTraits::is_unsigned, externalRtti->GetTypeTraits() & AZ::TypeTraits::is_unsigned);
  958. }
  959. TEST_F(Rtti, InternalRttiStoresTypeTraits)
  960. {
  961. AZ::IRttiHelper* internalRtti = AZ::GetRttiHelper<UnitTest::ExampleCombined>();
  962. ASSERT_NE(nullptr, internalRtti);
  963. EXPECT_NE(AZ::TypeTraits::is_signed, internalRtti->GetTypeTraits() & AZ::TypeTraits::is_signed);
  964. EXPECT_NE(AZ::TypeTraits::is_unsigned, internalRtti->GetTypeTraits() & AZ::TypeTraits::is_unsigned);
  965. }
  966. enum TestEnumWithTypeInfo : uint16_t
  967. {};
  968. }
  969. namespace AZ
  970. {
  971. AZ_TYPE_INFO_SPECIALIZE(UnitTest::TestEnumWithTypeInfo, "{6C2F6697-4E32-4E54-8A9E-AF2FB3F77C69}");
  972. }
  973. namespace UnitTest
  974. {
  975. TEST_F(Rtti, TypeInfoStoresTypeTraits)
  976. {
  977. AZ::IRttiHelper* internalRtti = AZ::GetRttiHelper<int>();
  978. ASSERT_NE(nullptr, internalRtti);
  979. EXPECT_EQ(AZ::TypeTraits::is_signed, internalRtti->GetTypeTraits() & AZ::TypeTraits::is_signed);
  980. EXPECT_NE(AZ::TypeTraits::is_unsigned, internalRtti->GetTypeTraits() & AZ::TypeTraits::is_unsigned);
  981. }
  982. class ReflectionManagerTest
  983. : public LeakDetectionFixture
  984. {
  985. public:
  986. void SetUp() override
  987. {
  988. LeakDetectionFixture::SetUp();
  989. m_reflection = AZStd::make_unique<AZ::ReflectionManager>();
  990. }
  991. void TearDown() override
  992. {
  993. m_reflection.reset();
  994. LeakDetectionFixture::TearDown();
  995. }
  996. protected:
  997. AZStd::unique_ptr<AZ::ReflectionManager> m_reflection;
  998. };
  999. class TestReflectedClass
  1000. {
  1001. public:
  1002. static bool s_isReflected;
  1003. static void Reflect(AZ::ReflectContext* context)
  1004. {
  1005. s_isReflected = !context->IsRemovingReflection();
  1006. }
  1007. };
  1008. bool TestReflectedClass::s_isReflected = false;
  1009. TEST_F(ReflectionManagerTest, AddContext_AddClass)
  1010. {
  1011. m_reflection->AddReflectContext<AZ::SerializeContext>();
  1012. m_reflection->Reflect(&TestReflectedClass::Reflect);
  1013. EXPECT_TRUE(TestReflectedClass::s_isReflected);
  1014. m_reflection->RemoveReflectContext<AZ::SerializeContext>();
  1015. EXPECT_FALSE(TestReflectedClass::s_isReflected);
  1016. }
  1017. TEST_F(ReflectionManagerTest, AddClass_AddContext)
  1018. {
  1019. m_reflection->Reflect(&TestReflectedClass::Reflect);
  1020. m_reflection->AddReflectContext<AZ::SerializeContext>();
  1021. EXPECT_TRUE(TestReflectedClass::s_isReflected);
  1022. m_reflection->Unreflect(&TestReflectedClass::Reflect);
  1023. EXPECT_FALSE(TestReflectedClass::s_isReflected);
  1024. }
  1025. TEST_F(ReflectionManagerTest, UnreflectOnDestruct)
  1026. {
  1027. m_reflection->Reflect(&TestReflectedClass::Reflect);
  1028. m_reflection->AddReflectContext<AZ::SerializeContext>();
  1029. EXPECT_TRUE(TestReflectedClass::s_isReflected);
  1030. m_reflection.reset();
  1031. EXPECT_FALSE(TestReflectedClass::s_isReflected);
  1032. }
  1033. TEST_F(ReflectionManagerTest, UnreflectReReflect)
  1034. {
  1035. m_reflection->AddReflectContext<AZ::SerializeContext>();
  1036. m_reflection->Reflect(&TestReflectedClass::Reflect);
  1037. EXPECT_TRUE(TestReflectedClass::s_isReflected);
  1038. m_reflection->Unreflect(&TestReflectedClass::Reflect);
  1039. EXPECT_FALSE(TestReflectedClass::s_isReflected);
  1040. m_reflection->Reflect(&TestReflectedClass::Reflect);
  1041. EXPECT_TRUE(TestReflectedClass::s_isReflected);
  1042. m_reflection->RemoveReflectContext<AZ::SerializeContext>();
  1043. EXPECT_FALSE(TestReflectedClass::s_isReflected);
  1044. m_reflection->AddReflectContext<AZ::SerializeContext>();
  1045. EXPECT_TRUE(TestReflectedClass::s_isReflected);
  1046. m_reflection.reset();
  1047. EXPECT_FALSE(TestReflectedClass::s_isReflected);
  1048. }
  1049. }