File indexing completed on 2026-08-15 08:22:21
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/unit_test.hpp>
0010
0011 #include "Acts/Utilities/Any.hpp"
0012
0013 #include <any>
0014 #include <array>
0015 #include <cstddef>
0016 #include <memory>
0017 #include <stdexcept>
0018 #include <type_traits>
0019 #include <utility>
0020
0021 using namespace Acts;
0022
0023 #if defined(_ACTS_ANY_ENABLE_TRACK_ALLOCATIONS)
0024 #define CHECK_ANY_ALLOCATIONS() \
0025 do { \
0026 detail::_AnyAllocationReporter::checkAllocations(); \
0027 } while (0)
0028 #else
0029 #define CHECK_ANY_ALLOCATIONS() \
0030 do { \
0031 } while (0)
0032 #endif
0033
0034 namespace ActsTests {
0035
0036 BOOST_AUTO_TEST_SUITE(UtilitiesSuite)
0037
0038 BOOST_AUTO_TEST_CASE(AnyConstructPrimitive) {
0039 {
0040
0041 Any a;
0042 BOOST_CHECK(!a);
0043
0044 int v = 5;
0045 a = Any{v};
0046 BOOST_CHECK(!!a);
0047
0048 BOOST_CHECK_EQUAL(a.as<int>(), v);
0049 BOOST_CHECK_NE(a.as<int>(), v + 1);
0050
0051 BOOST_CHECK_THROW(a.as<float>(), std::bad_any_cast);
0052 }
0053 CHECK_ANY_ALLOCATIONS();
0054
0055 {
0056
0057 Any a;
0058 BOOST_CHECK(!a);
0059
0060 std::array<int, 2> v{1, 2};
0061 a = Any{v};
0062 BOOST_CHECK(!!a);
0063
0064 BOOST_CHECK_EQUAL_COLLECTIONS(a.as<decltype(v)>().begin(),
0065 a.as<decltype(v)>().end(), v.begin(),
0066 v.end());
0067 BOOST_CHECK_THROW(a.as<float>(), std::bad_any_cast);
0068 }
0069 CHECK_ANY_ALLOCATIONS();
0070
0071 {
0072
0073 Any a;
0074 BOOST_CHECK(!a);
0075
0076 std::array<unsigned long, 5> v{1, 2, 3, 4, 5};
0077 a = Any{v};
0078 BOOST_CHECK(!!a);
0079
0080 BOOST_CHECK_EQUAL_COLLECTIONS(a.as<decltype(v)>().begin(),
0081 a.as<decltype(v)>().end(), v.begin(),
0082 v.end());
0083 BOOST_CHECK_THROW(a.as<float>(), std::bad_any_cast);
0084 }
0085 CHECK_ANY_ALLOCATIONS();
0086 }
0087
0088 BOOST_AUTO_TEST_CASE(AnyAsPtr) {
0089 {
0090
0091 Any a{42};
0092 int* p = a.asPtr<int>();
0093 BOOST_REQUIRE_NE(p, static_cast<int*>(nullptr));
0094 BOOST_CHECK_EQUAL(*p, 42);
0095
0096
0097 BOOST_CHECK_EQUAL(a.asPtr<float>(), static_cast<float*>(nullptr));
0098 BOOST_CHECK_EQUAL(a.asPtr<double>(), static_cast<double*>(nullptr));
0099
0100
0101 *p = 99;
0102 BOOST_CHECK_EQUAL(a.as<int>(), 99);
0103 }
0104 CHECK_ANY_ALLOCATIONS();
0105
0106 {
0107
0108 std::array<unsigned long, 5> v{10, 20, 30, 40, 50};
0109 Any a{v};
0110 auto* p = a.asPtr<std::array<unsigned long, 5>>();
0111 BOOST_REQUIRE_NE(p, static_cast<decltype(p)>(nullptr));
0112 BOOST_CHECK_EQUAL_COLLECTIONS(p->begin(), p->end(), v.begin(), v.end());
0113
0114
0115 BOOST_CHECK_EQUAL(a.asPtr<int>(), static_cast<int*>(nullptr));
0116 }
0117 CHECK_ANY_ALLOCATIONS();
0118
0119 {
0120
0121 Any a;
0122 BOOST_CHECK_EQUAL(a.asPtr<int>(), static_cast<int*>(nullptr));
0123 BOOST_CHECK_EQUAL(a.asPtr<float>(), static_cast<float*>(nullptr));
0124 }
0125 CHECK_ANY_ALLOCATIONS();
0126
0127 {
0128
0129 const Any a{3.14f};
0130 const float* p = a.asPtr<float>();
0131 BOOST_REQUIRE_NE(p, static_cast<const float*>(nullptr));
0132 BOOST_CHECK_EQUAL(*p, 3.14f);
0133
0134
0135 BOOST_CHECK_EQUAL(a.asPtr<int>(), static_cast<const int*>(nullptr));
0136 }
0137 CHECK_ANY_ALLOCATIONS();
0138
0139 {
0140
0141 const Any a;
0142 BOOST_CHECK_EQUAL(a.asPtr<int>(), static_cast<const int*>(nullptr));
0143 }
0144 CHECK_ANY_ALLOCATIONS();
0145
0146 {
0147
0148 std::array<int, 64> v{};
0149 v.fill(7);
0150 const Any a{v};
0151 const auto* p = a.asPtr<std::array<int, 64>>();
0152 BOOST_REQUIRE_NE(p, static_cast<decltype(p)>(nullptr));
0153 for (const auto& elem : *p) {
0154 BOOST_CHECK_EQUAL(elem, 7);
0155 }
0156 BOOST_CHECK_EQUAL(a.asPtr<float>(), static_cast<const float*>(nullptr));
0157 }
0158 CHECK_ANY_ALLOCATIONS();
0159 }
0160
0161 BOOST_AUTO_TEST_CASE(AnyAssignConstructEmpty) {
0162 Any a;
0163 Any b;
0164 a = b;
0165 Any c{a};
0166 a = std::move(b);
0167 Any d{std::move(a)};
0168
0169 BOOST_CHECK(!a);
0170 BOOST_CHECK(!b);
0171 BOOST_CHECK(!c);
0172 BOOST_CHECK(!d);
0173
0174 CHECK_ANY_ALLOCATIONS();
0175 }
0176
0177 BOOST_AUTO_TEST_CASE(AnyConstructCustom) {
0178 struct A {
0179 int value;
0180 A() { value = 76; }
0181 };
0182
0183 Any a;
0184 BOOST_CHECK(!a);
0185 a = Any{A{}};
0186
0187 BOOST_CHECK(!!a);
0188
0189 BOOST_CHECK_EQUAL(a.as<A>().value, 76);
0190
0191 CHECK_ANY_ALLOCATIONS();
0192 }
0193
0194 BOOST_AUTO_TEST_CASE(AnyConstructCustomInPlace) {
0195 struct A {
0196 int value;
0197 explicit A(int v) { value = v; }
0198 };
0199
0200 Any a{std::in_place_type<A>, 42};
0201 BOOST_CHECK(!!a);
0202 BOOST_CHECK_EQUAL(a.as<A>().value, 42);
0203
0204 CHECK_ANY_ALLOCATIONS();
0205 }
0206
0207 BOOST_AUTO_TEST_CASE(AnyMove) {
0208 {
0209
0210 Any a;
0211 BOOST_CHECK(!a);
0212
0213 int v = 5;
0214 a = Any{v};
0215 BOOST_CHECK(!!a);
0216
0217 Any b = std::move(a);
0218 BOOST_CHECK(!!b);
0219 BOOST_CHECK_EQUAL(b.as<int>(), 5);
0220
0221 Any c;
0222 c = std::move(b);
0223 BOOST_CHECK(!!c);
0224 BOOST_CHECK_EQUAL(c.as<int>(), 5);
0225 }
0226
0227 CHECK_ANY_ALLOCATIONS();
0228 }
0229
0230 BOOST_AUTO_TEST_CASE(AnyCopy) {
0231 {
0232
0233 Any a;
0234 BOOST_CHECK(!a);
0235
0236 int v = 5;
0237 a = Any{v};
0238 BOOST_CHECK(!!a);
0239
0240 Any b = a;
0241 BOOST_CHECK(!!b);
0242 BOOST_CHECK_EQUAL(b.as<int>(), 5);
0243
0244 Any c;
0245 c = a;
0246 BOOST_CHECK(!!c);
0247 BOOST_CHECK_EQUAL(c.as<int>(), 5);
0248 }
0249 CHECK_ANY_ALLOCATIONS();
0250 }
0251
0252 struct D {
0253 bool* destroyed;
0254 explicit D(bool* d) : destroyed{d} {}
0255 ~D() { *destroyed = true; }
0256 };
0257
0258 struct D2 {
0259 bool* destroyed{nullptr};
0260 std::array<char, 512> blob{};
0261
0262 explicit D2(bool* d) : destroyed{d} {}
0263
0264 ~D2() { *destroyed = true; }
0265 };
0266
0267 BOOST_AUTO_TEST_CASE(AnyEmplace) {
0268 {
0269 Any a;
0270 auto& value = a.emplace<int>(42);
0271 BOOST_CHECK_EQUAL(value, 42);
0272 BOOST_CHECK_EQUAL(a.as<int>(), 42);
0273 value = 84;
0274 BOOST_CHECK_EQUAL(a.as<int>(), 84);
0275 }
0276 CHECK_ANY_ALLOCATIONS();
0277
0278 {
0279 bool destroyed = false;
0280 Any a{std::in_place_type<D>, &destroyed};
0281 BOOST_CHECK(!destroyed);
0282 a.emplace<int>(7);
0283 BOOST_CHECK(destroyed);
0284 BOOST_CHECK_EQUAL(a.as<int>(), 7);
0285 }
0286 CHECK_ANY_ALLOCATIONS();
0287
0288 {
0289 bool destroyed = false;
0290 Any a{std::in_place_type<D2>, &destroyed};
0291 BOOST_CHECK(!destroyed);
0292 bool destroyed2 = false;
0293 auto& ref = a.emplace<D2>(&destroyed2);
0294 BOOST_CHECK(destroyed);
0295 BOOST_CHECK(!destroyed2);
0296 BOOST_CHECK_EQUAL(ref.destroyed, &destroyed2);
0297 BOOST_CHECK_EQUAL(a.as<D2>().destroyed, &destroyed2);
0298 }
0299 CHECK_ANY_ALLOCATIONS();
0300 }
0301
0302 BOOST_AUTO_TEST_CASE(AnyMoveTypeChange) {
0303 BOOST_TEST_CONTEXT("Small type") {
0304 bool destroyed = false;
0305 D d{&destroyed};
0306 Any a{std::move(d)};
0307 BOOST_CHECK(!destroyed);
0308
0309 int value = 5;
0310 Any b{value};
0311 a = std::move(b);
0312 BOOST_CHECK(destroyed);
0313 BOOST_CHECK_EQUAL(a.as<int>(), value);
0314 }
0315
0316 bool destroyed = false;
0317 BOOST_TEST_CONTEXT("Large type") {
0318 D2 d{&destroyed};
0319 Any a{std::move(d)};
0320 BOOST_CHECK(!destroyed);
0321
0322 int value = 5;
0323 Any b{value};
0324 a = std::move(b);
0325 BOOST_CHECK(destroyed);
0326 BOOST_CHECK_EQUAL(a.as<int>(), value);
0327 }
0328 }
0329
0330 BOOST_AUTO_TEST_CASE(AnyCopyTypeChange) {
0331 BOOST_TEST_CONTEXT("Small type") {
0332 bool destroyed = false;
0333 D d{&destroyed};
0334 Any a{std::move(d)};
0335 BOOST_CHECK(!destroyed);
0336
0337 int value = 5;
0338 Any b{value};
0339 a = b;
0340 BOOST_CHECK(destroyed);
0341 BOOST_CHECK_EQUAL(a.as<int>(), value);
0342 }
0343
0344 bool destroyed = false;
0345 BOOST_TEST_CONTEXT("Large type") {
0346 D2 d{&destroyed};
0347 Any a{std::move(d)};
0348 BOOST_CHECK(!destroyed);
0349
0350 int value = 5;
0351 Any b{value};
0352 a = b;
0353 BOOST_CHECK(destroyed);
0354 BOOST_CHECK_EQUAL(a.as<int>(), value);
0355 }
0356 }
0357
0358 BOOST_AUTO_TEST_CASE(AnyCopyTypeChangeToHeap) {
0359
0360
0361
0362
0363
0364 using Large = std::array<unsigned long, 5>;
0365
0366 BOOST_TEST_CONTEXT("local -> heap") {
0367 Any a{42};
0368 Large v{1, 2, 3, 4, 5};
0369 Any b{v};
0370 a = b;
0371 BOOST_CHECK_EQUAL_COLLECTIONS(a.as<Large>().begin(), a.as<Large>().end(),
0372 v.begin(), v.end());
0373
0374 BOOST_CHECK_EQUAL_COLLECTIONS(b.as<Large>().begin(), b.as<Large>().end(),
0375 v.begin(), v.end());
0376 }
0377 CHECK_ANY_ALLOCATIONS();
0378
0379 BOOST_TEST_CONTEXT("heap -> heap (different type)") {
0380 std::array<int, 4> v1{1, 2, 3, 4};
0381 Large v2{6, 7, 8, 9, 10};
0382 Any a{v1};
0383 Any b{v2};
0384 a = b;
0385 BOOST_CHECK_EQUAL_COLLECTIONS(a.as<Large>().begin(), a.as<Large>().end(),
0386 v2.begin(), v2.end());
0387 }
0388 CHECK_ANY_ALLOCATIONS();
0389 }
0390
0391 BOOST_AUTO_TEST_CASE(AnyDestroy) {
0392 {
0393 bool destroyed = false;
0394 D d{&destroyed};
0395 BOOST_CHECK(!destroyed);
0396 {
0397 Any a{std::move(d)};
0398 BOOST_CHECK(!destroyed);
0399 }
0400 BOOST_CHECK(destroyed);
0401 }
0402 CHECK_ANY_ALLOCATIONS();
0403
0404 {
0405 bool destroyed = false;
0406 D2 d{&destroyed};
0407 BOOST_CHECK(!destroyed);
0408 {
0409 Any a{std::move(d)};
0410 BOOST_CHECK(!destroyed);
0411 }
0412 BOOST_CHECK(destroyed);
0413 }
0414 CHECK_ANY_ALLOCATIONS();
0415 }
0416
0417 BOOST_AUTO_TEST_CASE(AnyDestroyCopy) {
0418 {
0419 bool destroyed = false;
0420
0421 {
0422 Any b;
0423 {
0424 Any a{std::in_place_type<D>, &destroyed};
0425 BOOST_CHECK(!destroyed);
0426 b = a;
0427 BOOST_CHECK(!destroyed);
0428 }
0429 BOOST_CHECK(destroyed);
0430 destroyed = false;
0431 BOOST_CHECK(!destroyed);
0432 }
0433 BOOST_CHECK(destroyed);
0434 }
0435 CHECK_ANY_ALLOCATIONS();
0436
0437 {
0438 bool destroyed = false;
0439
0440 {
0441 Any b;
0442 {
0443 Any a{std::in_place_type<D2>, &destroyed};
0444 BOOST_CHECK(!destroyed);
0445 b = a;
0446 BOOST_CHECK(!destroyed);
0447 }
0448 BOOST_CHECK(destroyed);
0449 destroyed = false;
0450 BOOST_CHECK(!destroyed);
0451 }
0452 BOOST_CHECK(destroyed);
0453 }
0454 CHECK_ANY_ALLOCATIONS();
0455 }
0456
0457 BOOST_AUTO_TEST_CASE(AnyDestroyInPlace) {
0458 {
0459 bool destroyed = false;
0460 BOOST_CHECK(!destroyed);
0461 {
0462 Any a{std::in_place_type<D>, &destroyed};
0463 BOOST_CHECK(!destroyed);
0464 }
0465 BOOST_CHECK(destroyed);
0466 }
0467 CHECK_ANY_ALLOCATIONS();
0468
0469 {
0470 bool destroyed = false;
0471 BOOST_CHECK(!destroyed);
0472 {
0473 Any a{std::in_place_type<D2>, &destroyed};
0474 BOOST_CHECK(!destroyed);
0475 }
0476 BOOST_CHECK(destroyed);
0477 }
0478 CHECK_ANY_ALLOCATIONS();
0479 }
0480
0481 struct D3 {
0482 std::size_t* destroyed{nullptr};
0483 std::array<char, 512> blob{};
0484
0485 explicit D3(std::size_t* d) : destroyed{d} {}
0486
0487 ~D3() { (*destroyed)++; }
0488 };
0489
0490 BOOST_AUTO_TEST_CASE(LeakCheck) {
0491 std::size_t destroyed = 0;
0492 for (std::size_t i = 0; i < 10000; i++) {
0493 {
0494 BOOST_CHECK_EQUAL(destroyed, i);
0495 Any a;
0496 BOOST_CHECK_EQUAL(destroyed, i);
0497 a = Any{std::in_place_type<D3>, &destroyed};
0498 BOOST_CHECK_EQUAL(destroyed, i);
0499 }
0500 BOOST_CHECK_EQUAL(destroyed, i + 1);
0501 }
0502 CHECK_ANY_ALLOCATIONS();
0503 }
0504
0505 struct LifecycleCounters {
0506 std::size_t nDestroy = 0;
0507 std::size_t nCopyConstruct = 0;
0508 std::size_t nCopy = 0;
0509 std::size_t nMoveConstruct = 0;
0510 std::size_t nMove = 0;
0511 };
0512
0513 template <std::size_t PADDING>
0514 struct Lifecycle;
0515
0516 template <>
0517 struct Lifecycle<0> {
0518 LifecycleCounters* counters;
0519
0520 explicit Lifecycle(LifecycleCounters* _counters) : counters{_counters} {}
0521
0522 Lifecycle(Lifecycle&& o) {
0523 counters = o.counters;
0524 counters->nMoveConstruct++;
0525 }
0526
0527 Lifecycle& operator=(Lifecycle&& o) {
0528 counters = o.counters;
0529 counters->nMove++;
0530 return *this;
0531 }
0532
0533 Lifecycle(const Lifecycle& o) {
0534 counters = o.counters;
0535 counters->nCopyConstruct++;
0536 }
0537
0538 Lifecycle& operator=(const Lifecycle& o) {
0539 counters = o.counters;
0540 counters->nCopy++;
0541 return *this;
0542 }
0543
0544 ~Lifecycle() { counters->nDestroy++; }
0545 };
0546
0547 template <std::size_t PADDING>
0548 struct Lifecycle : public Lifecycle<0> {
0549 std::array<char, PADDING> m_padding{};
0550
0551 explicit Lifecycle(LifecycleCounters* _counters) : Lifecycle<0>(_counters) {}
0552 };
0553
0554 template <std::size_t PADDING>
0555 struct LifecycleHandle {
0556 LifecycleCounters counters;
0557 Lifecycle<PADDING> inner;
0558
0559 LifecycleHandle() : counters{}, inner{&counters} {}
0560 };
0561
0562 #define checkCounters() \
0563 do { \
0564 BOOST_REQUIRE_EQUAL(l.counters.nCopy, counters.nCopy); \
0565 BOOST_REQUIRE_EQUAL(l.counters.nCopyConstruct, counters.nCopyConstruct); \
0566 BOOST_REQUIRE_EQUAL(l.counters.nMove, counters.nMove); \
0567 BOOST_REQUIRE_EQUAL(l.counters.nMoveConstruct, counters.nMoveConstruct); \
0568 BOOST_REQUIRE_EQUAL(l.counters.nDestroy, counters.nDestroy); \
0569 } while (0)
0570
0571 #define makeCounter(counter, n) \
0572 do { \
0573 counter += n; \
0574 checkCounters(); \
0575 } while (0)
0576
0577 #define incCopyConstruct(n) makeCounter(counters.nCopyConstruct, n)
0578 #define incCopy(n) makeCounter(counters.nCopy, n)
0579 #define incMoveConstruct(n) makeCounter(counters.nMoveConstruct, n)
0580 #define incMove(n) makeCounter(counters.nMove, n)
0581 #define incDestroy(n) makeCounter(counters.nDestroy, n)
0582
0583 BOOST_AUTO_TEST_CASE(LifeCycleSmall) {
0584 LifecycleCounters counters;
0585 LifecycleHandle<0> l;
0586
0587 checkCounters();
0588
0589 {
0590 const auto& o = l.inner;
0591 Any a{o};
0592 incCopyConstruct(1);
0593
0594 const auto& _a = a;
0595 {
0596 Any b{_a};
0597 incCopyConstruct(1);
0598 }
0599 incDestroy(1);
0600
0601 {
0602 Any b;
0603 b = _a;
0604 incCopyConstruct(1);
0605 b = _a;
0606 incCopy(1);
0607 }
0608 incDestroy(1);
0609
0610 {
0611 Any b{a};
0612 incCopyConstruct(1);
0613 b = a;
0614 incCopy(1);
0615 }
0616 incDestroy(1);
0617
0618 {
0619 auto _a2 = a;
0620 incCopyConstruct(1);
0621 Any b;
0622 b = std::move(_a2);
0623 incMoveConstruct(1);
0624 auto _a3 = a;
0625 incCopyConstruct(1);
0626 b = std::move(_a3);
0627 incMove(1);
0628 }
0629 incDestroy(3);
0630 }
0631 incDestroy(1);
0632
0633 checkCounters();
0634
0635 CHECK_ANY_ALLOCATIONS();
0636 }
0637
0638 BOOST_AUTO_TEST_CASE(LifeCycleHeap) {
0639 LifecycleCounters counters;
0640 LifecycleHandle<512> l;
0641
0642 checkCounters();
0643
0644 {
0645 const auto& o = l.inner;
0646 Any a{o};
0647 incCopyConstruct(1);
0648
0649 const auto& _a = a;
0650 {
0651 Any b{_a};
0652 incCopyConstruct(1);
0653 }
0654 incDestroy(1);
0655
0656 {
0657 Any b;
0658 b = _a;
0659 incCopyConstruct(1);
0660 b = _a;
0661 incCopy(1);
0662 }
0663 incDestroy(1);
0664
0665 {
0666 Any b{a};
0667 incCopyConstruct(1);
0668 b = a;
0669 incCopy(1);
0670 }
0671 incDestroy(1);
0672
0673 {
0674 Any _a2 = a;
0675 incCopyConstruct(1);
0676 Any b;
0677 b = std::move(_a2);
0678
0679
0680 Any _a3 = a;
0681 incCopyConstruct(1);
0682 b = std::move(_a3);
0683
0684 incDestroy(1);
0685 }
0686 incDestroy(1);
0687 }
0688 incDestroy(1);
0689
0690 checkCounters();
0691
0692 CHECK_ANY_ALLOCATIONS();
0693 }
0694
0695 BOOST_AUTO_TEST_CASE(AnyMoveOnlyMoveOnlyTypes) {
0696 using MoveOnlyAny = Acts::AnyMoveOnly;
0697
0698 using Ptr = std::unique_ptr<int>;
0699
0700
0701 {
0702 auto ptr = std::make_unique<int>(42);
0703 MoveOnlyAny a{std::move(ptr)};
0704 BOOST_CHECK(!!a);
0705 Ptr const* storedPtr = a.asPtr<Ptr>();
0706 BOOST_REQUIRE_NE(storedPtr, nullptr);
0707 BOOST_CHECK_NE(storedPtr->get(), nullptr);
0708 BOOST_CHECK_EQUAL(**storedPtr, 42);
0709 }
0710
0711
0712 {
0713 auto ptr = std::make_unique<int>(7);
0714 MoveOnlyAny a{std::move(ptr)};
0715 MoveOnlyAny b = std::move(a);
0716
0717 BOOST_CHECK(!!b);
0718 Ptr const* bPtr = b.asPtr<Ptr>();
0719 BOOST_REQUIRE_NE(bPtr, nullptr);
0720 int val = **bPtr;
0721 BOOST_CHECK_EQUAL(val, 7);
0722 }
0723
0724
0725 static_assert(!std::is_copy_constructible_v<MoveOnlyAny>);
0726 static_assert(!std::is_copy_assignable_v<MoveOnlyAny>);
0727 }
0728
0729 BOOST_AUTO_TEST_CASE(AnyTake) {
0730
0731 {
0732 Any a{42};
0733 BOOST_CHECK(!!a);
0734 int val = a.take<int>();
0735 BOOST_CHECK_EQUAL(val, 42);
0736 BOOST_CHECK(!a);
0737 }
0738
0739
0740 {
0741 auto ptr = std::make_unique<int>(99);
0742 Acts::AnyMoveOnly a{std::move(ptr)};
0743 BOOST_CHECK(!!a);
0744 auto taken = a.take<std::unique_ptr<int>>();
0745 BOOST_REQUIRE_NE(taken.get(), nullptr);
0746 BOOST_CHECK_EQUAL(*taken, 99);
0747 BOOST_CHECK(!a);
0748 }
0749
0750
0751 {
0752 Any a{42};
0753 BOOST_CHECK_THROW(a.take<float>(), std::bad_any_cast);
0754 BOOST_CHECK(!!a);
0755 BOOST_CHECK_EQUAL(a.as<int>(), 42);
0756 }
0757
0758
0759 {
0760 Any a;
0761 BOOST_CHECK_THROW(a.take<int>(), std::bad_any_cast);
0762 }
0763 }
0764
0765 struct ThrowOnDemand {
0766
0767 std::array<char, 64> blob{};
0768 explicit ThrowOnDemand(bool doThrow) {
0769 if (doThrow) {
0770 throw std::runtime_error{"boom"};
0771 }
0772 }
0773 };
0774
0775 BOOST_AUTO_TEST_CASE(AnyEmplaceThrowingConstructor) {
0776
0777
0778
0779
0780 BOOST_TEST_CONTEXT("local") {
0781 struct SmallThrow {
0782 int x{0};
0783 explicit SmallThrow(bool doThrow) {
0784 if (doThrow) {
0785 throw std::runtime_error{"boom"};
0786 }
0787 }
0788 };
0789 Any a{std::in_place_type<SmallThrow>, false};
0790 BOOST_CHECK(!!a);
0791 BOOST_CHECK_THROW(a.emplace<SmallThrow>(true), std::runtime_error);
0792 BOOST_CHECK(!a);
0793 }
0794 CHECK_ANY_ALLOCATIONS();
0795
0796 BOOST_TEST_CONTEXT("heap") {
0797 Any a{std::in_place_type<ThrowOnDemand>, false};
0798 BOOST_CHECK(!!a);
0799 BOOST_CHECK_THROW(a.emplace<ThrowOnDemand>(true), std::runtime_error);
0800 BOOST_CHECK(!a);
0801 }
0802 CHECK_ANY_ALLOCATIONS();
0803 }
0804
0805 BOOST_AUTO_TEST_CASE(AnySelfAssign) {
0806 using Large = std::array<unsigned long, 5>;
0807
0808 BOOST_TEST_CONTEXT("self copy-assign local") {
0809 Any a{7};
0810 Any& r = a;
0811 a = r;
0812 BOOST_CHECK_EQUAL(a.as<int>(), 7);
0813 }
0814 BOOST_TEST_CONTEXT("self copy-assign heap") {
0815 Large v{1, 2, 3, 4, 5};
0816 Any a{v};
0817 Any& r = a;
0818 a = r;
0819 BOOST_CHECK_EQUAL_COLLECTIONS(a.as<Large>().begin(), a.as<Large>().end(),
0820 v.begin(), v.end());
0821 }
0822 BOOST_TEST_CONTEXT("self move-assign local") {
0823 Any a{7};
0824 Any& r = a;
0825 a = std::move(r);
0826 BOOST_CHECK(!!a);
0827 BOOST_CHECK_EQUAL(a.as<int>(), 7);
0828 }
0829 BOOST_TEST_CONTEXT("self move-assign heap") {
0830 Large v{1, 2, 3, 4, 5};
0831 Any a{v};
0832 Any& r = a;
0833 a = std::move(r);
0834 BOOST_CHECK(!!a);
0835 BOOST_CHECK_EQUAL_COLLECTIONS(a.as<Large>().begin(), a.as<Large>().end(),
0836 v.begin(), v.end());
0837 }
0838 CHECK_ANY_ALLOCATIONS();
0839 }
0840
0841
0842
0843
0844
0845
0846
0847
0848 static_assert(!std::is_nothrow_copy_constructible_v<Any>);
0849 static_assert(!std::is_nothrow_copy_assignable_v<Any>);
0850 static_assert(std::is_nothrow_move_constructible_v<Any> ==
0851 detail::kAnyNoexcept);
0852 static_assert(std::is_nothrow_move_assignable_v<Any> == detail::kAnyNoexcept);
0853
0854 struct HeapCopyThrows {
0855
0856 std::array<int, 8> blob{};
0857 HeapCopyThrows() = default;
0858 HeapCopyThrows(const HeapCopyThrows& ) {
0859 throw std::runtime_error{"copy boom"};
0860 }
0861 HeapCopyThrows& operator=(const HeapCopyThrows&) = default;
0862 HeapCopyThrows(HeapCopyThrows&&) = default;
0863 HeapCopyThrows& operator=(HeapCopyThrows&&) = default;
0864 };
0865
0866 BOOST_AUTO_TEST_CASE(AnyCopyExceptionPropagates) {
0867 {
0868 Any a{std::in_place_type<HeapCopyThrows>};
0869
0870
0871 BOOST_CHECK_THROW(Any b{a}, std::runtime_error);
0872
0873
0874 Any c;
0875 BOOST_CHECK_THROW(c = a, std::runtime_error);
0876 BOOST_CHECK(!c);
0877 }
0878 CHECK_ANY_ALLOCATIONS();
0879 }
0880
0881
0882
0883
0884
0885 namespace {
0886
0887 struct AnyOfBase {
0888 virtual ~AnyOfBase() = default;
0889 virtual int value() const = 0;
0890 };
0891
0892
0893 struct DerivedSmall : public AnyOfBase {
0894 int v;
0895 explicit DerivedSmall(int x) : v{x} {}
0896 int value() const override { return v; }
0897 };
0898
0899
0900 struct DerivedLarge : public AnyOfBase {
0901 std::array<std::int64_t, 16> pad{};
0902 int v;
0903 explicit DerivedLarge(int x) : v{x} { pad.fill(x); }
0904 int value() const override { return v; }
0905 };
0906
0907
0908
0909 struct OtherBase {
0910 virtual ~OtherBase() = default;
0911 std::int64_t tag = 0;
0912 virtual std::int64_t getTag() const { return tag; }
0913 };
0914
0915 struct DerivedMI : public OtherBase, public AnyOfBase {
0916 int v;
0917 explicit DerivedMI(int x) : v{x} { tag = x; }
0918 int value() const override { return v; }
0919 };
0920
0921 struct Unrelated {
0922 int v;
0923 };
0924
0925 template <typename T>
0926 concept HasAsBase = requires(T t) { t.asBase(); };
0927
0928 }
0929
0930
0931 static_assert(!HasAsBase<Any>);
0932 static_assert(HasAsBase<AnyOf<AnyOfBase>>);
0933
0934
0935 static_assert(std::is_constructible_v<AnyOf<AnyOfBase>, DerivedSmall>);
0936 static_assert(std::is_constructible_v<AnyOf<AnyOfBase>, DerivedLarge>);
0937 static_assert(std::is_constructible_v<AnyOf<AnyOfBase>, DerivedMI>);
0938 static_assert(!std::is_constructible_v<AnyOf<AnyOfBase>, Unrelated>);
0939
0940 BOOST_AUTO_TEST_CASE(AnyOfBasicAccess) {
0941 {
0942 AnyOf<AnyOfBase> a{DerivedSmall{42}};
0943 BOOST_CHECK(static_cast<bool>(a));
0944
0945
0946 BOOST_REQUIRE(a.asBase() != nullptr);
0947 BOOST_CHECK_EQUAL(a.asBase()->value(), 42);
0948 BOOST_CHECK_EQUAL((*a).value(), 42);
0949 BOOST_CHECK_EQUAL(a->value(), 42);
0950
0951
0952 BOOST_REQUIRE(a.asPtr<DerivedSmall>() != nullptr);
0953 BOOST_CHECK_EQUAL(a.asPtr<DerivedSmall>()->v, 42);
0954 BOOST_CHECK(a.asPtr<DerivedLarge>() == nullptr);
0955 }
0956 CHECK_ANY_ALLOCATIONS();
0957 }
0958
0959 BOOST_AUTO_TEST_CASE(AnyOfConstAccess) {
0960 {
0961 const AnyOf<AnyOfBase> a{DerivedSmall{7}};
0962 const AnyOfBase* base = a.asBase();
0963 BOOST_REQUIRE(base != nullptr);
0964 BOOST_CHECK_EQUAL(base->value(), 7);
0965 BOOST_CHECK_EQUAL((*a).value(), 7);
0966 BOOST_CHECK_EQUAL(a->value(), 7);
0967 }
0968 CHECK_ANY_ALLOCATIONS();
0969 }
0970
0971 BOOST_AUTO_TEST_CASE(AnyOfEmpty) {
0972 {
0973 AnyOf<AnyOfBase> a;
0974 BOOST_CHECK(!a);
0975 BOOST_CHECK(a.asBase() == nullptr);
0976
0977 const AnyOf<AnyOfBase> b;
0978 BOOST_CHECK(b.asBase() == nullptr);
0979 }
0980 CHECK_ANY_ALLOCATIONS();
0981 }
0982
0983 BOOST_AUTO_TEST_CASE(AnyOfCopyMoveLocal) {
0984 {
0985 AnyOf<AnyOfBase> a{DerivedSmall{1}};
0986
0987
0988 AnyOf<AnyOfBase> b{a};
0989 BOOST_CHECK_EQUAL(a->value(), 1);
0990 BOOST_CHECK_EQUAL(b->value(), 1);
0991
0992 BOOST_CHECK(a.asBase() != b.asBase());
0993
0994
0995 AnyOf<AnyOfBase> c;
0996 c = a;
0997 BOOST_CHECK_EQUAL(c->value(), 1);
0998
0999
1000 AnyOf<AnyOfBase> d{std::move(b)};
1001 BOOST_CHECK_EQUAL(d->value(), 1);
1002
1003
1004 AnyOf<AnyOfBase> e;
1005 e = std::move(c);
1006 BOOST_CHECK_EQUAL(e->value(), 1);
1007 }
1008 CHECK_ANY_ALLOCATIONS();
1009 }
1010
1011 BOOST_AUTO_TEST_CASE(AnyOfCopyMoveHeap) {
1012 {
1013 AnyOf<AnyOfBase> a{DerivedLarge{5}};
1014 BOOST_CHECK_EQUAL(a->value(), 5);
1015
1016
1017 AnyOf<AnyOfBase> b{a};
1018 BOOST_CHECK_EQUAL(b->value(), 5);
1019 BOOST_CHECK(a.asBase() != b.asBase());
1020
1021
1022 AnyOf<AnyOfBase> c;
1023 c = a;
1024 BOOST_CHECK_EQUAL(c->value(), 5);
1025
1026
1027 AnyOf<AnyOfBase> d{std::move(b)};
1028 BOOST_CHECK_EQUAL(d->value(), 5);
1029 BOOST_CHECK(!b);
1030
1031
1032 AnyOf<AnyOfBase> e;
1033 e = std::move(c);
1034 BOOST_CHECK_EQUAL(e->value(), 5);
1035 BOOST_CHECK(!c);
1036 }
1037 CHECK_ANY_ALLOCATIONS();
1038 }
1039
1040 BOOST_AUTO_TEST_CASE(AnyOfMultipleInheritanceOffset) {
1041 {
1042 AnyOf<AnyOfBase> a{DerivedMI{99}};
1043
1044 DerivedMI* concrete = a.asPtr<DerivedMI>();
1045 BOOST_REQUIRE(concrete != nullptr);
1046
1047
1048
1049 BOOST_REQUIRE(static_cast<void*>(static_cast<AnyOfBase*>(concrete)) !=
1050 static_cast<void*>(concrete));
1051
1052
1053 BOOST_CHECK_EQUAL(a.asBase(), static_cast<AnyOfBase*>(concrete));
1054 BOOST_CHECK_EQUAL(a->value(), 99);
1055 }
1056 CHECK_ANY_ALLOCATIONS();
1057 }
1058
1059 BOOST_AUTO_TEST_SUITE_END()
1060
1061 }