poly.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. #include <array>
  2. #include <cstddef>
  3. #include <cstdint>
  4. #include <utility>
  5. #include <gtest/gtest.h>
  6. #include <entt/core/type_info.hpp>
  7. #include <entt/core/type_traits.hpp>
  8. #include <entt/poly/poly.hpp>
  9. #include "../../common/config.h"
  10. #include "../../common/linter.hpp"
  11. template<typename Base>
  12. struct common_type: Base {
  13. void incr() {
  14. constexpr auto member_index = 0u;
  15. entt::poly_call<member_index>(*this);
  16. }
  17. void set(int iv) {
  18. constexpr auto member_index = 1u;
  19. entt::poly_call<member_index>(*this, iv);
  20. }
  21. [[nodiscard]] int get() const {
  22. constexpr auto member_index = 2u;
  23. return static_cast<int>(entt::poly_call<member_index>(*this));
  24. }
  25. void decr() {
  26. constexpr auto member_index = 3u;
  27. entt::poly_call<member_index>(*this);
  28. }
  29. [[nodiscard]] int mul(int iv) const {
  30. constexpr auto member_index = 4u;
  31. return static_cast<int>(entt::poly_call<member_index>(*this, iv));
  32. }
  33. [[nodiscard]] int rand() const {
  34. constexpr auto member_index = 5u;
  35. return static_cast<int>(entt::poly_call<member_index>(*this));
  36. }
  37. };
  38. template<typename Type>
  39. struct common_members {
  40. static void decr(Type &self) {
  41. self.set(self.get() - 1);
  42. }
  43. [[nodiscard]] static double mul(const Type &self, double dv) {
  44. return dv * self.get();
  45. }
  46. };
  47. namespace {
  48. template<typename... Type>
  49. entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &);
  50. [[nodiscard]] int absolutely_random() {
  51. return 4;
  52. }
  53. [[nodiscard]] int three_is_a_magic_number() {
  54. return 3;
  55. }
  56. } // namespace
  57. template<typename Type>
  58. using common_impl = entt::value_list<
  59. &Type::incr,
  60. &Type::set,
  61. &Type::get,
  62. &common_members<Type>::decr,
  63. &common_members<Type>::mul,
  64. &absolutely_random>;
  65. struct Deduced
  66. : entt::type_list<> {
  67. template<typename Base>
  68. using type = common_type<Base>;
  69. template<typename Type>
  70. using members = common_members<Type>;
  71. template<typename Type>
  72. using impl = common_impl<Type>;
  73. };
  74. struct Defined
  75. : entt::type_list<
  76. void(),
  77. void(int),
  78. int() const,
  79. void(),
  80. int(int) const,
  81. int() const> {
  82. template<typename Base>
  83. using type = common_type<Base>;
  84. template<typename Type>
  85. using members = common_members<Type>;
  86. template<typename Type>
  87. using impl = common_impl<Type>;
  88. };
  89. struct DeducedEmbedded
  90. : entt::type_list<> {
  91. template<typename Base>
  92. struct type: Base {
  93. [[nodiscard]] int get() const {
  94. return entt::poly_call<0>(*this);
  95. }
  96. };
  97. template<typename Type>
  98. using impl = entt::value_list<&Type::get>;
  99. };
  100. struct DefinedEmbedded
  101. : entt::type_list<int()> {
  102. template<typename Base>
  103. struct type: Base {
  104. // non-const get on purpose
  105. [[nodiscard]] int get() {
  106. return entt::poly_call<0>(*this);
  107. }
  108. };
  109. template<typename Type>
  110. using impl = entt::value_list<&Type::get>;
  111. };
  112. struct DeducedDerived
  113. : entt::type_list<> {
  114. template<typename Base>
  115. struct type: Deduced::type<Base> {
  116. static constexpr auto base = Deduced::impl<Deduced::type<entt::poly_inspector>>::size;
  117. int three_is_a_magic_number() {
  118. return entt::poly_call<base + 0>(*this);
  119. }
  120. };
  121. template<typename Type>
  122. using impl = entt::value_list_cat_t<Deduced::impl<Type>, entt::value_list<&three_is_a_magic_number>>;
  123. };
  124. struct DefinedDerived
  125. : entt::type_list_cat_t<
  126. decltype(as_type_list(std::declval<Defined>())),
  127. entt::type_list<int()>> {
  128. template<typename Base>
  129. struct type: Defined::type<Base> {
  130. static constexpr auto base = Defined::impl<Defined::type<entt::poly_inspector>>::size;
  131. int three_is_a_magic_number() {
  132. return entt::poly_call<base + 0>(*this);
  133. }
  134. };
  135. template<typename Type>
  136. using impl = entt::value_list_cat_t<Defined::impl<Type>, entt::value_list<&three_is_a_magic_number>>;
  137. };
  138. struct impl {
  139. impl() = default;
  140. impl(int iv)
  141. : value{iv} {}
  142. void incr() {
  143. ++value;
  144. }
  145. void set(int iv) {
  146. value = iv;
  147. }
  148. [[nodiscard]] int get() const {
  149. return value;
  150. }
  151. int value{};
  152. };
  153. struct alignas(64u) over_aligned: impl {};
  154. template<typename Type>
  155. struct Poly: testing::Test {
  156. template<std::size_t... Args>
  157. using type = entt::basic_poly<Type, Args...>;
  158. };
  159. template<typename Type>
  160. using PolyDeathTest = Poly<Type>;
  161. using PolyTypes = ::testing::Types<Deduced, Defined>;
  162. TYPED_TEST_SUITE(Poly, PolyTypes, );
  163. TYPED_TEST_SUITE(PolyDeathTest, PolyTypes, );
  164. template<typename Type>
  165. struct PolyEmbedded: testing::Test {
  166. using type = entt::basic_poly<Type>;
  167. };
  168. using PolyEmbeddedTypes = ::testing::Types<DeducedEmbedded, DefinedEmbedded>;
  169. TYPED_TEST_SUITE(PolyEmbedded, PolyEmbeddedTypes, );
  170. template<typename Type>
  171. struct PolyDerived: testing::Test {
  172. using type = entt::basic_poly<Type>;
  173. };
  174. using PolyDerivedTypes = ::testing::Types<DeducedDerived, DefinedDerived>;
  175. TYPED_TEST_SUITE(PolyDerived, PolyDerivedTypes, );
  176. TYPED_TEST(Poly, Functionalities) {
  177. using poly_type = TestFixture::template type<>;
  178. impl instance{};
  179. poly_type empty{};
  180. poly_type in_place{std::in_place_type<impl>, 3};
  181. poly_type alias{std::in_place_type<impl &>, instance};
  182. poly_type value{impl{}};
  183. ASSERT_FALSE(empty);
  184. ASSERT_TRUE(in_place);
  185. ASSERT_TRUE(alias);
  186. ASSERT_TRUE(value);
  187. ASSERT_EQ(empty.info(), entt::type_id<void>());
  188. ASSERT_EQ(in_place.info(), entt::type_id<impl>());
  189. ASSERT_EQ(alias.info(), entt::type_id<impl>());
  190. ASSERT_EQ(value.info(), entt::type_id<impl>());
  191. ASSERT_EQ(alias.data(), &instance);
  192. ASSERT_EQ(std::as_const(alias).data(), &instance);
  193. ASSERT_EQ(value->rand(), absolutely_random());
  194. empty = impl{};
  195. ASSERT_TRUE(empty);
  196. ASSERT_NE(empty.data(), nullptr);
  197. ASSERT_NE(std::as_const(empty).data(), nullptr);
  198. ASSERT_EQ(empty.info(), entt::type_id<impl>());
  199. ASSERT_EQ(empty->get(), 0);
  200. empty.template emplace<impl>(3);
  201. ASSERT_TRUE(empty);
  202. ASSERT_EQ(std::as_const(empty)->get(), 3);
  203. poly_type ref = in_place.as_ref();
  204. ASSERT_TRUE(ref);
  205. ASSERT_NE(ref.data(), nullptr);
  206. ASSERT_EQ(ref.data(), in_place.data());
  207. ASSERT_EQ(std::as_const(ref).data(), std::as_const(in_place).data());
  208. ASSERT_EQ(ref.info(), entt::type_id<impl>());
  209. ASSERT_EQ(ref->get(), 3);
  210. poly_type null{};
  211. std::swap(empty, null);
  212. ASSERT_FALSE(empty);
  213. poly_type copy = in_place;
  214. ASSERT_TRUE(copy);
  215. ASSERT_EQ(copy->get(), 3);
  216. poly_type move = std::move(copy);
  217. test::is_initialized(copy);
  218. ASSERT_TRUE(move);
  219. ASSERT_TRUE(copy);
  220. ASSERT_EQ(move->get(), 3);
  221. move.reset();
  222. ASSERT_FALSE(move);
  223. ASSERT_EQ(move.info(), entt::type_id<void>());
  224. }
  225. TYPED_TEST(Poly, Owned) {
  226. using poly_type = TestFixture::template type<>;
  227. poly_type poly{impl{}};
  228. auto *ptr = static_cast<impl *>(poly.data());
  229. ASSERT_TRUE(poly);
  230. ASSERT_NE(poly.data(), nullptr);
  231. ASSERT_NE(std::as_const(poly).data(), nullptr);
  232. ASSERT_EQ(ptr->value, 0);
  233. ASSERT_EQ(poly->get(), 0);
  234. poly->set(1);
  235. poly->incr();
  236. ASSERT_EQ(ptr->value, 2);
  237. ASSERT_EQ(std::as_const(poly)->get(), 2);
  238. ASSERT_EQ(poly->mul(3), 6);
  239. poly->decr();
  240. ASSERT_EQ(ptr->value, 1);
  241. ASSERT_EQ(poly->get(), 1);
  242. ASSERT_EQ(poly->mul(3), 3);
  243. }
  244. TYPED_TEST(Poly, Reference) {
  245. using poly_type = TestFixture::template type<>;
  246. impl instance{};
  247. poly_type poly{std::in_place_type<impl &>, instance};
  248. ASSERT_TRUE(poly);
  249. ASSERT_NE(poly.data(), nullptr);
  250. ASSERT_NE(std::as_const(poly).data(), nullptr);
  251. ASSERT_EQ(instance.value, 0);
  252. ASSERT_EQ(poly->get(), 0);
  253. poly->set(1);
  254. poly->incr();
  255. ASSERT_EQ(instance.value, 2);
  256. ASSERT_EQ(std::as_const(poly)->get(), 2);
  257. ASSERT_EQ(poly->mul(3), 6);
  258. poly->decr();
  259. ASSERT_EQ(instance.value, 1);
  260. ASSERT_EQ(poly->get(), 1);
  261. ASSERT_EQ(poly->mul(3), 3);
  262. }
  263. TYPED_TEST(Poly, ConstReference) {
  264. using poly_type = TestFixture::template type<>;
  265. impl instance{};
  266. poly_type poly{std::in_place_type<const impl &>, instance};
  267. ASSERT_TRUE(poly);
  268. ASSERT_EQ(poly.data(), nullptr);
  269. ASSERT_NE(std::as_const(poly).data(), nullptr);
  270. ASSERT_EQ(instance.value, 0);
  271. ASSERT_EQ(poly->get(), 0);
  272. ASSERT_EQ(instance.value, 0);
  273. ASSERT_EQ(std::as_const(poly)->get(), 0);
  274. ASSERT_EQ(poly->mul(3), 0);
  275. ASSERT_EQ(instance.value, 0);
  276. ASSERT_EQ(poly->get(), 0);
  277. ASSERT_EQ(poly->mul(3), 0);
  278. }
  279. ENTT_DEBUG_TYPED_TEST(PolyDeathTest, ConstReference) {
  280. using poly_type = TestFixture::template type<>;
  281. impl instance{};
  282. poly_type poly{std::in_place_type<const impl &>, instance};
  283. ASSERT_TRUE(poly);
  284. ASSERT_DEATH(poly->set(1), "");
  285. }
  286. TYPED_TEST(Poly, AsRef) {
  287. using poly_type = TestFixture::template type<>;
  288. poly_type poly{impl{}};
  289. auto ref = poly.as_ref();
  290. auto cref = std::as_const(poly).as_ref();
  291. ASSERT_NE(poly.data(), nullptr);
  292. ASSERT_NE(ref.data(), nullptr);
  293. ASSERT_EQ(cref.data(), nullptr);
  294. ASSERT_NE(std::as_const(cref).data(), nullptr);
  295. std::swap(ref, cref);
  296. ASSERT_EQ(ref.data(), nullptr);
  297. ASSERT_NE(std::as_const(ref).data(), nullptr);
  298. ASSERT_NE(cref.data(), nullptr);
  299. ref = ref.as_ref();
  300. cref = std::as_const(cref).as_ref();
  301. ASSERT_EQ(ref.data(), nullptr);
  302. ASSERT_NE(std::as_const(ref).data(), nullptr);
  303. ASSERT_EQ(cref.data(), nullptr);
  304. ASSERT_NE(std::as_const(cref).data(), nullptr);
  305. ref = impl{};
  306. cref = impl{};
  307. ASSERT_NE(ref.data(), nullptr);
  308. ASSERT_NE(cref.data(), nullptr);
  309. }
  310. TYPED_TEST(Poly, SBOVsZeroedSBOSize) {
  311. using poly_type = TestFixture::template type<>;
  312. using zeroed_type = TestFixture::template type<0u>;
  313. poly_type poly{impl{}};
  314. const auto broken = poly.data();
  315. poly_type other = std::move(poly);
  316. ASSERT_NE(broken, other.data());
  317. zeroed_type dyn{impl{}};
  318. const auto valid = dyn.data();
  319. zeroed_type same = std::move(dyn);
  320. ASSERT_EQ(valid, same.data());
  321. // everything works as expected
  322. same->incr();
  323. ASSERT_EQ(same->get(), 1);
  324. }
  325. TYPED_TEST(Poly, SboAlignment) {
  326. constexpr auto alignment = alignof(over_aligned);
  327. using poly_type = TestFixture::template type<alignment, alignment>;
  328. std::array<poly_type, 2u> sbo = {over_aligned{}, over_aligned{}};
  329. const auto *data = sbo[0].data();
  330. ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[0u].data()) % alignment) == 0u);
  331. ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[1u].data()) % alignment) == 0u);
  332. std::swap(sbo[0], sbo[1]);
  333. ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[0u].data()) % alignment) == 0u);
  334. ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[1u].data()) % alignment) == 0u);
  335. ASSERT_NE(data, sbo[1].data());
  336. }
  337. TYPED_TEST(Poly, NoSboAlignment) {
  338. constexpr auto alignment = alignof(over_aligned);
  339. using poly_type = TestFixture::template type<alignment>;
  340. std::array<poly_type, 2u> nosbo = {over_aligned{}, over_aligned{}};
  341. const auto *data = nosbo[0].data();
  342. ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[0u].data()) % alignment) == 0u);
  343. ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[1u].data()) % alignment) == 0u);
  344. std::swap(nosbo[0], nosbo[1]);
  345. ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[0u].data()) % alignment) == 0u);
  346. ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[1u].data()) % alignment) == 0u);
  347. ASSERT_EQ(data, nosbo[1].data());
  348. }
  349. TYPED_TEST(PolyEmbedded, EmbeddedVtable) {
  350. using poly_type = TestFixture::type;
  351. poly_type poly{impl{}};
  352. auto *ptr = static_cast<impl *>(poly.data());
  353. ASSERT_TRUE(poly);
  354. ASSERT_NE(poly.data(), nullptr);
  355. ASSERT_NE(std::as_const(poly).data(), nullptr);
  356. ASSERT_EQ(poly->get(), 0);
  357. ptr->value = 2;
  358. ASSERT_EQ(poly->get(), 2);
  359. }
  360. TYPED_TEST(PolyDerived, InheritanceSupport) {
  361. using poly_type = TestFixture::type;
  362. poly_type poly{impl{}};
  363. ASSERT_EQ(poly->three_is_a_magic_number(), three_is_a_magic_number());
  364. }