From 9fd25e8b10b1291525a18c8b3e34256ca6151dd6 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 22 Mar 2025 11:50:31 +0000 Subject: Add ManyPtr which tracks specified subclasses This removes the need to repeated dynamic_cast the pointer. Provides interface which enforces the fastest option for the required types. --- application/main.cpp | 7 ++-- assetFactory/asset.h | 3 ++ assetFactory/assetFactory.cpp | 3 +- assetFactory/assetFactory.h | 2 +- game/network/rail.cpp | 2 +- gfx/gl/shadowMapper.cpp | 2 +- lib/manyPtr.h | 86 +++++++++++++++++++++++++++++++++++++++++++ lib/persistence.h | 3 +- test/test-assetFactory.cpp | 12 +++--- test/test-render.cpp | 13 ++++--- ui/gameMainWindow.cpp | 12 +++--- 11 files changed, 119 insertions(+), 26 deletions(-) create mode 100644 lib/manyPtr.h diff --git a/application/main.cpp b/application/main.cpp index 723f3d2..514fab6 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -86,7 +86,7 @@ public: } const std::shared_ptr train = world.create(l3, 800000); - auto b47 = std::dynamic_pointer_cast(assets.at("brush-47")); + auto b47 = assets.at("brush-47").dynamicCast(); for (int N = 0; N < 6; N++) { train->create(b47); } @@ -101,8 +101,9 @@ public: std::uniform_int_distribution treeVariantDistribution {1, 4}; for (auto x = 311000000; x < 311830000; x += 5000) { for (auto y = 491100000; y < 491130000; y += 5000) { - world.create(std::dynamic_pointer_cast(assets.at(std::format("Tree-{:#02}-{}", - treeDistribution(randomdev), treeVariantDistribution(randomdev)))), + world.create(assets.at(std::format("Tree-{:#02}-{}", treeDistribution(randomdev), + treeVariantDistribution(randomdev))) + .dynamicCast(), Location {terrain->positionAt({{x + positionOffsetDistribution(randomdev), y + positionOffsetDistribution(randomdev)}}), {0, rotationDistribution(randomdev), 0}}); diff --git a/assetFactory/asset.h b/assetFactory/asset.h index 5bdd2f2..b5de056 100644 --- a/assetFactory/asset.h +++ b/assetFactory/asset.h @@ -2,12 +2,15 @@ #include "factoryMesh.h" #include "persistence.h" +#include #include class TextureAtlas; +class Renderable; class Asset : public Persistence::Persistable, public StdTypeDefs { public: + using ManyPtr = ManySharedPtr; using TexturePtr = std::shared_ptr; std::string id; diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index 176e1f5..426fecc 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -5,6 +5,7 @@ #include "filesystem.h" #include "gfx/image.h" #include "gfx/models/texture.h" +#include "gfx/renderable.h" #include "object.h" #include "plane.h" #include "saxParse-persistence.h" @@ -146,7 +147,7 @@ bool AssetFactory::persist(Persistence::PersistenceStore & store) { using MapObjects = Persistence::MapByMember>; - using MapAssets = Persistence::MapByMember; + using MapAssets = Persistence::MapByMember; using MapTextureFragments = Persistence::MapByMember; using MapAssImp = Persistence::MapByMember, &AssImp::path>; return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects) diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h index 787f0a4..864e882 100644 --- a/assetFactory/assetFactory.h +++ b/assetFactory/assetFactory.h @@ -12,7 +12,7 @@ class Texture; class AssetFactory : public Persistence::Persistable { public: using Shapes = std::map>; - using Assets = std::map>; + using Assets = std::map>; using AssImps = std::map>; using TextureFragments = std::map>; using Colour = RGB; diff --git a/game/network/rail.cpp b/game/network/rail.cpp index 2a18b9a..5ce6036 100644 --- a/game/network/rail.cpp +++ b/game/network/rail.cpp @@ -182,7 +182,7 @@ RailLinks::render(const SceneShader & shader, const Frustum &) const const Surface * RailLinks::getBaseSurface() const { - return std::dynamic_pointer_cast(gameState->assets.at("terrain.surface.gravel")).get(); + return gameState->assets.at("terrain.surface.gravel").dynamicCast().get(); } RelativeDistance diff --git a/gfx/gl/shadowMapper.cpp b/gfx/gl/shadowMapper.cpp index 6525f76..3cb73f7 100644 --- a/gfx/gl/shadowMapper.cpp +++ b/gfx/gl/shadowMapper.cpp @@ -88,7 +88,7 @@ ShadowMapper::update(const SceneProvider & scene, const LightDirection & dir, co shadowStenciller.setLightDirection(dir); for (const auto & [id, asset] : gameState->assets) { - if (const auto r = std::dynamic_pointer_cast(asset)) { + if (const auto r = asset.getAs()) { r->updateStencil(shadowStenciller); } } diff --git a/lib/manyPtr.h b/lib/manyPtr.h new file mode 100644 index 0000000..9e08452 --- /dev/null +++ b/lib/manyPtr.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include + +template class ManyPtr : Primary { +public: + using element_type = typename Primary::element_type; + + template ManyPtr(Params &&... params) : Primary {std::forward(params)...} + { + updatePtrs(); + } + + using Primary::operator->; + using Primary::operator*; + using Primary::operator bool; + using Primary::get; + + template + void + reset(Params &&... params) + { + Primary::reset(std::forward(params)...); + updatePtrs(); + } + + template + [[nodiscard]] consteval static bool + couldBe() + { + return (std::is_convertible_v || ...); + } + + template + requires(couldBe()) + [[nodiscard]] auto + getAs() const + { + return std::get()>(others); + } + + template + requires(!couldBe() && requires { std::dynamic_pointer_cast(std::declval()); }) + [[nodiscard]] auto + dynamicCast() const + { + return std::dynamic_pointer_cast(*this); + } + + template + requires(!couldBe() && !requires { std::dynamic_pointer_cast(std::declval()); }) + [[nodiscard]] auto + dynamicCast() const + { + return dynamic_cast(get()); + } + +private: + using OtherPtrs = std::tuple; + + template + requires(couldBe()) + [[nodiscard]] consteval static bool + idx() + { + size_t typeIdx = 0; + return ((typeIdx++ && std::is_convertible_v) || ...); + } + + void + updatePtrs() + { + if (*this) { + others = {dynamic_cast(get())...}; + } + else { + others = {}; + } + } + + OtherPtrs others; +}; + +template using ManySharedPtr = ManyPtr, Others...>; +template using ManyUniquePtr = ManyPtr, Others...>; diff --git a/lib/persistence.h b/lib/persistence.h index e385b20..75bcea6 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -1,5 +1,6 @@ #pragma once +#include "manyPtr.h" #include #include #include @@ -200,7 +201,7 @@ namespace Persistence { } [[nodiscard]] virtual NameActionSelection setName(const std::string_view key, SelectionFactory &&) = 0; - virtual void selHandler() {}; + virtual void selHandler() { }; virtual void setType(const std::string_view, const Persistable *) = 0; SelectionPtr sel {}; diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 9bade82..03319da 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(surfaces, *boost::unit_test::timeout(5)) BOOST_CHECK_EQUAL(4, mf->assets.size()); auto gravelAsset = mf->assets.at("terrain.surface.gravel"); BOOST_REQUIRE(gravelAsset); - auto gravel = std::dynamic_pointer_cast(gravelAsset); + auto gravel = gravelAsset.dynamicCast(); BOOST_REQUIRE(gravel); BOOST_REQUIRE_EQUAL(gravel->name, "Gravel"); BOOST_REQUIRE_EQUAL(gravel->colorBias, RGB {.9F}); @@ -111,7 +111,7 @@ BOOST_AUTO_TEST_CASE(brush47xml, *boost::unit_test::timeout(5)) BOOST_CHECK_EQUAL(1, mf->assets.size()); auto brush47 = mf->assets.at("brush-47"); BOOST_REQUIRE(brush47); - auto brush47rvc = std::dynamic_pointer_cast(brush47); + auto brush47rvc = brush47.dynamicCast(); BOOST_REQUIRE(brush47rvc); BOOST_REQUIRE(brush47rvc->bodyMesh); BOOST_REQUIRE(brush47rvc->bogies.front()); @@ -130,7 +130,7 @@ BOOST_AUTO_TEST_CASE(foliage, *boost::unit_test::timeout(5)) gameState.assets = mf->assets; auto tree_01_1 = mf->assets.at("Tree-01-1"); BOOST_REQUIRE(tree_01_1); - auto tree_01_1_f = std::dynamic_pointer_cast(tree_01_1); + auto tree_01_1_f = tree_01_1.dynamicCast(); BOOST_REQUIRE(tree_01_1_f); auto plant1 = std::make_shared(tree_01_1_f, Location {{-2000, 2000, 0}, {0, 0, 0}}); @@ -151,9 +151,9 @@ BOOST_AUTO_TEST_CASE(lights, *boost::unit_test::timeout(5)) BOOST_REQUIRE(rlight); auto oldlamp = mf->assets.at("old-lamp"); BOOST_REQUIRE(oldlamp); - auto rlight_f = std::dynamic_pointer_cast(rlight); + auto rlight_f = rlight.dynamicCast(); BOOST_REQUIRE(rlight_f); - auto oldlamp_f = std::dynamic_pointer_cast(oldlamp); + auto oldlamp_f = oldlamp.dynamicCast(); BOOST_REQUIRE(oldlamp_f); auto light1 = std::make_shared(oldlamp_f, Location {{0, 0, 0}, {0, 0, 0}}); @@ -164,7 +164,7 @@ BOOST_AUTO_TEST_CASE(lights, *boost::unit_test::timeout(5)) objects.objects.push_back(oldlamp_f); // yes I'm hacking some floor to light up as though its a bush - auto floorf = std::dynamic_pointer_cast(mf->assets.at("floor")); + auto floorf = mf->assets.at("floor").dynamicCast(); auto floor = std::make_shared(floorf, Location {}); objects.objects.push_back(floorf); diff --git a/test/test-render.cpp b/test/test-render.cpp index 8390d25..a6e28bc 100644 --- a/test/test-render.cpp +++ b/test/test-render.cpp @@ -41,7 +41,7 @@ public: terrain->point(GeoData::VertexHandle {517}).z = 100'000; terrain->generateMeshes(); gameState->assets = AssetFactory::loadAll(RESDIR); - brush47rvc = std::dynamic_pointer_cast(gameState->assets.at("brush-47")); + brush47rvc = gameState->assets.at("brush-47").dynamicCast(); std::random_device randomdev {}; std::uniform_real_distribution rotationDistribution {0, two_pi}; std::uniform_int_distribution positionOffsetDistribution {-1500, +1500}; @@ -57,9 +57,10 @@ public: train2->bogies.back().setPosition(train2->bogies.back().position() + train2->location.position()); for (auto x = 40000; x < 100000; x += 5000) { for (auto y = 65000; y < 125000; y += 5000) { - gameState->world.create( - std::dynamic_pointer_cast(gameState->assets.at(std::format( - "Tree-{:#02}-{}", treeDistribution(randomdev), treeVariantDistribution(randomdev)))), + gameState->world.create(gameState->assets + .at(std::format("Tree-{:#02}-{}", treeDistribution(randomdev), + treeVariantDistribution(randomdev))) + .dynamicCast(), Location {{x + positionOffsetDistribution(randomdev), y + positionOffsetDistribution(randomdev), 1}, {0, rotationDistribution(randomdev), 0}}); @@ -76,7 +77,7 @@ public: water.render(shader, frustum); rail.render(shader, frustum); std::ranges::for_each(gameState->assets, [&shader, &frustum](const auto & asset) { - if (const auto renderable = std::dynamic_pointer_cast(asset.second)) { + if (const auto renderable = asset.second.template getAs()) { renderable->render(shader, frustum); } }); @@ -98,7 +99,7 @@ public: { terrain->shadows(shadowMapper, frustum); std::ranges::for_each(gameState->assets, [&shadowMapper, &frustum](const auto & asset) { - if (const auto renderable = std::dynamic_pointer_cast(asset.second)) { + if (const auto renderable = asset.second.template getAs()) { renderable->shadows(shadowMapper, frustum); } }); diff --git a/ui/gameMainWindow.cpp b/ui/gameMainWindow.cpp index d88bab5..b58f3dc 100644 --- a/ui/gameMainWindow.cpp +++ b/ui/gameMainWindow.cpp @@ -75,9 +75,9 @@ GameMainWindow::render() const void GameMainWindow::content(const SceneShader & shader, const Frustum & frustum) const { - for (const auto & [id, asset] : gameState->assets) { - if (const auto r = std::dynamic_pointer_cast(asset)) { - r->render(shader, frustum); + for (const auto & [assetId, asset] : gameState->assets) { + if (const auto renderable = asset.getAs()) { + renderable->render(shader, frustum); } } gameState->world.apply(&Renderable::render, shader, frustum); @@ -99,9 +99,9 @@ GameMainWindow::lights(const SceneShader & shader) const void GameMainWindow::shadows(const ShadowMapper & shadowMapper, const Frustum & frustum) const { - for (const auto & [id, asset] : gameState->assets) { - if (const auto r = std::dynamic_pointer_cast(asset)) { - r->shadows(shadowMapper, frustum); + for (const auto & [assetId, asset] : gameState->assets) { + if (const auto renderable = asset.getAs()) { + renderable->shadows(shadowMapper, frustum); } } gameState->world.apply(&Renderable::shadows, shadowMapper, frustum); -- cgit v1.2.3 From fcca8bc835db65ac170d1148d52a815df8838d53 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 22 Mar 2025 12:17:57 +0000 Subject: Invert how shared/unique is specified for Collection Template param is a pointer now, typedefs added for ease. --- game/gamestate.h | 2 +- game/network/network.h | 2 +- game/orders.h | 2 +- game/vehicles/train.h | 2 +- game/water.h | 2 +- lib/collection.h | 9 ++++++--- test/test-assetFactory.cpp | 2 +- test/test-collection.cpp | 6 +++--- ui/editNetwork.h | 2 +- ui/gameMainWindow.cpp | 2 +- ui/mainApplication.h | 2 +- ui/toolbar.h | 2 +- ui/windowContent.h | 2 +- 13 files changed, 20 insertions(+), 17 deletions(-) diff --git a/game/gamestate.h b/game/gamestate.h index 189417d..c5ad239 100644 --- a/game/gamestate.h +++ b/game/gamestate.h @@ -16,7 +16,7 @@ public: NO_MOVE(GameState); NO_COPY(GameState); - Collection world; + SharedCollection world; std::shared_ptr terrain; std::shared_ptr environment; AssetFactory::Assets assets; diff --git a/game/network/network.h b/game/network/network.h index 291c4ec..73c3788 100644 --- a/game/network/network.h +++ b/game/network/network.h @@ -77,7 +77,7 @@ class NetworkOf : public Network, public Renderable, public NetworkLinkHolder
  • links; + SharedCollection links; void joinLinks(const Link::Ptr &) const; protected: diff --git a/game/orders.h b/game/orders.h index ca5cfdb..840aa3c 100644 --- a/game/orders.h +++ b/game/orders.h @@ -5,7 +5,7 @@ class Objective; -class Orders : public Collection { +class Orders : public SharedCollection { public: [[nodiscard]] Objective * current() const; Objective * next(); diff --git a/game/vehicles/train.h b/game/vehicles/train.h index 4933347..88e30f9 100644 --- a/game/vehicles/train.h +++ b/game/vehicles/train.h @@ -15,7 +15,7 @@ class SceneShader; class ShadowMapper; template class Ray; -class Train : public Vehicle, public Collection, public Can, public Can { +class Train : public Vehicle, public UniqueCollection, public Can, public Can { public: explicit Train(const Link::CPtr & link, float linkDist = 0) : Vehicle {link, linkDist} { } diff --git a/game/water.h b/game/water.h index f9fe080..07d9ae1 100644 --- a/game/water.h +++ b/game/water.h @@ -29,6 +29,6 @@ private: void generateMeshes(); std::shared_ptr geoData; - Collection, false> meshes; + UniqueCollection> meshes; Texture::Ptr water; }; diff --git a/lib/collection.h b/lib/collection.h index 6802bcb..2deefb9 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -6,11 +6,11 @@ #include #include -template class Collection { +template class Collection { public: virtual ~Collection() = default; - using Ptr = std::conditional_t, std::unique_ptr>; + using Object = Ptr::element_type; using Objects = std::vector; Objects objects; @@ -19,7 +19,7 @@ public: create(Params &&... params) requires std::is_base_of_v { - if constexpr (shared) { + if constexpr (requires(Ptr ptr) { ptr = std::make_shared(std::forward(params)...); }) { auto obj = std::make_shared(std::forward(params)...); objects.emplace_back(obj); return obj; @@ -129,3 +129,6 @@ protected: }); } }; + +template using SharedCollection = Collection>; +template using UniqueCollection = Collection>; diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 03319da..72f13b3 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -71,7 +71,7 @@ public: sceneRenderer.render(*this); } - Collection objects; + SharedCollection objects; private: SceneRenderer sceneRenderer; diff --git a/test/test-collection.cpp b/test/test-collection.cpp index 00204fc..00298bb 100644 --- a/test/test-collection.cpp +++ b/test/test-collection.cpp @@ -34,10 +34,10 @@ public: } }; -using TestCollection = Collection; +using TestCollection = SharedCollection; -BOOST_TEST_DONT_PRINT_LOG_VALUE(Collection::Objects::const_iterator) -BOOST_TEST_DONT_PRINT_LOG_VALUE(Collection::Objects::const_reverse_iterator) +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::Objects::const_iterator) +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::Objects::const_reverse_iterator) BOOST_FIXTURE_TEST_SUITE(tc, TestCollection) diff --git a/ui/editNetwork.h b/ui/editNetwork.h index ae887bd..2fc102a 100644 --- a/ui/editNetwork.h +++ b/ui/editNetwork.h @@ -36,7 +36,7 @@ public: using Ptr = std::unique_ptr; protected: - Collection candidateLinks; + SharedCollection candidateLinks; }; private: diff --git a/ui/gameMainWindow.cpp b/ui/gameMainWindow.cpp index b58f3dc..07901b3 100644 --- a/ui/gameMainWindow.cpp +++ b/ui/gameMainWindow.cpp @@ -32,7 +32,7 @@ GameMainWindow::GameMainWindow(size_t w, size_t h) : WindowContent {w, h}, Scene { uiComponents.create(glm::vec2 {310'727'624, 494'018'810}); auto gms = uiComponents.create(&camera, ScreenAbsCoord {w, h}); - uiComponents.create(gms.get()); + uiComponents.create(gms); } GameMainWindow::~GameMainWindow() { } diff --git a/ui/mainApplication.h b/ui/mainApplication.h index a6cb126..1489587 100644 --- a/ui/mainApplication.h +++ b/ui/mainApplication.h @@ -6,7 +6,7 @@ class MainApplication : public ApplicationBase { public: - using Windows = Collection; + using Windows = SharedCollection; void mainLoop(); protected: diff --git a/ui/toolbar.h b/ui/toolbar.h index ea560f5..b0480e2 100644 --- a/ui/toolbar.h +++ b/ui/toolbar.h @@ -19,5 +19,5 @@ public: bool handleInput(const SDL_Event & e, const Position & parentPos) override; - Collection icons; + UniqueCollection icons; }; diff --git a/ui/windowContent.h b/ui/windowContent.h index 474445a..5437da6 100644 --- a/ui/windowContent.h +++ b/ui/windowContent.h @@ -21,6 +21,6 @@ public: virtual bool handleInput(const SDL_Event & e); protected: - ::Collection uiComponents; + UniqueCollection uiComponents; UIShader uiShader; }; -- cgit v1.2.3 From 163c8f75265054a168cc1e2750a0adb40fb08d83 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 22 Mar 2025 13:01:14 +0000 Subject: Make Collections::objects protected, extend interface Keeps all required features accessible, but through a controlled interface. --- application/main.cpp | 2 +- game/network/network.impl.h | 8 +++--- game/network/rail.cpp | 2 +- lib/collection.h | 44 +++++++++++++++++++++++++++-- test/test-assetFactory.cpp | 10 +++---- test/test-network.cpp | 68 ++++++++++++++++++++++----------------------- ui/builders/freeExtend.cpp | 4 +-- ui/builders/join.cpp | 2 +- ui/builders/straight.cpp | 2 +- 9 files changed, 90 insertions(+), 52 deletions(-) diff --git a/application/main.cpp b/application/main.cpp index 514fab6..1ca2192 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -113,7 +113,7 @@ public: mainLoop(); - world.objects.clear(); + world.removeAll(); return 0; } }; diff --git a/game/network/network.impl.h b/game/network/network.impl.h index 0a2f9ca..294f696 100644 --- a/game/network/network.impl.h +++ b/game/network/network.impl.h @@ -8,7 +8,7 @@ template void NetworkOf::joinLinks(const Link::Ptr & l) const { - for (const auto & ol : links.objects) { + for (const auto & ol : links) { Network::joinLinks(l, ol); } } @@ -18,11 +18,11 @@ Link::Ptr NetworkOf::intersectRayLinks(const Ray & ray) const { // Click link - if (const auto link = std::find_if(links.objects.begin(), links.objects.end(), + if (const auto link = std::find_if(links.begin(), links.end(), [&ray](const std::shared_ptr & link) { return link->intersectRay(ray); }); - link != links.objects.end()) { + link != links.end()) { return *link; } return {}; @@ -32,7 +32,7 @@ template float NetworkOf::findNodeDirection(Node::AnyCPtr n) const { - for (const auto & l : links.objects) { + for (const auto & l : links) { for (const auto & e : l->ends) { // cppcheck-suppress useStlAlgorithm if (e.node.get() == n.get()) { diff --git a/game/network/rail.cpp b/game/network/rail.cpp index 5ce6036..dfe1dca 100644 --- a/game/network/rail.cpp +++ b/game/network/rail.cpp @@ -168,7 +168,7 @@ namespace { void RailLinks::render(const SceneShader & shader, const Frustum &) const { - if (!links.objects.empty()) { + if (!links.empty()) { texture->bind(); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-1, 0); diff --git a/lib/collection.h b/lib/collection.h index 2deefb9..c843bad 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -12,7 +12,19 @@ public: using Object = Ptr::element_type; using Objects = std::vector; - Objects objects; + + Collection & + operator=(Objects && other) + { + objects = std::move(other); + return *this; + } + + const Ptr & + operator[](size_t idx) const + { + return objects[idx]; + } template auto @@ -91,19 +103,45 @@ public: }); } - auto + [[nodiscard]] auto + begin() const + { + return objects.begin(); + } + + [[nodiscard]] auto end() const { return objects.end(); } - auto + [[nodiscard]] auto + rbegin() const + { + return objects.rbegin(); + } + + [[nodiscard]] auto rend() const { return objects.rend(); } + [[nodiscard]] bool + empty() const + { + return objects.empty(); + } + + auto + emplace(Ptr && ptr) + { + return objects.emplace_back(std::move(ptr)); + } + protected: + Objects objects; + template auto apply_internal(const auto begin, const auto end, const auto & m, Params &&... params) const diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 72f13b3..68ce6a6 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -118,7 +118,7 @@ BOOST_AUTO_TEST_CASE(brush47xml, *boost::unit_test::timeout(5)) BOOST_REQUIRE(brush47rvc->bogies.back()); auto railVehicle = std::make_shared(brush47rvc); - objects.objects.push_back(brush47rvc); + objects.emplace(brush47rvc); render(10000); } @@ -137,7 +137,7 @@ BOOST_AUTO_TEST_CASE(foliage, *boost::unit_test::timeout(5)) auto plant2 = std::make_shared(tree_01_1_f, Location {{3000, -4000, 0}, {0, 1, 0}}); auto plant3 = std::make_shared(tree_01_1_f, Location {{-2000, -4000, 0}, {0, 2, 0}}); auto plant4 = std::make_shared(tree_01_1_f, Location {{3000, 2000, 0}, {0, 3, 0}}); - objects.objects.push_back(tree_01_1_f); + objects.emplace(tree_01_1_f); render(6000); } @@ -160,13 +160,13 @@ BOOST_AUTO_TEST_CASE(lights, *boost::unit_test::timeout(5)) auto light2 = std::make_shared(rlight_f, Location {{-4000, 0, 0}, {0, 2, 0}}); auto light3 = std::make_shared(rlight_f, Location {{-4000, -4000, 0}, {0, 1, 0}}); auto light4 = std::make_shared(oldlamp_f, Location {{3000, 4600, 0}, {0, 2, 0}}); - objects.objects.push_back(rlight_f); - objects.objects.push_back(oldlamp_f); + objects.emplace(rlight_f); + objects.emplace(oldlamp_f); // yes I'm hacking some floor to light up as though its a bush auto floorf = mf->assets.at("floor").dynamicCast(); auto floor = std::make_shared(floorf, Location {}); - objects.objects.push_back(floorf); + objects.emplace(floorf); render(6000); } diff --git a/test/test-network.cpp b/test/test-network.cpp index 51fea8b..19a740e 100644 --- a/test/test-network.cpp +++ b/test/test-network.cpp @@ -139,83 +139,83 @@ BOOST_DATA_TEST_CASE(newNodeAt_new, INVALID_NODES, p) BOOST_AUTO_TEST_CASE(network_joins) { // Ends - BOOST_CHECK(links.objects[2]->ends[1].nexts.empty()); + BOOST_CHECK(links[2]->ends[1].nexts.empty()); // Join 0 <-> 1 - BOOST_REQUIRE_EQUAL(links.objects[0]->ends[1].nexts.size(), 2); - BOOST_CHECK_EQUAL(links.objects[0]->ends[1].nexts[0].first.lock().get(), links.objects[1].get()); - BOOST_CHECK_EQUAL(links.objects[0]->ends[1].nexts[0].second, 0); - BOOST_CHECK_EQUAL(links.objects[0]->ends[1].nexts[1].first.lock().get(), links.objects[5].get()); - BOOST_CHECK_EQUAL(links.objects[0]->ends[1].nexts[1].second, 0); - BOOST_REQUIRE_EQUAL(links.objects[1]->ends[0].nexts.size(), 2); - BOOST_CHECK_EQUAL(links.objects[1]->ends[0].nexts[0].first.lock().get(), links.objects[0].get()); - BOOST_CHECK_EQUAL(links.objects[1]->ends[0].nexts[0].second, 1); - BOOST_CHECK_EQUAL(links.objects[1]->ends[0].nexts[1].first.lock().get(), links.objects[5].get()); - BOOST_CHECK_EQUAL(links.objects[1]->ends[0].nexts[1].second, 0); + BOOST_REQUIRE_EQUAL(links[0]->ends[1].nexts.size(), 2); + BOOST_CHECK_EQUAL(links[0]->ends[1].nexts[0].first.lock().get(), links[1].get()); + BOOST_CHECK_EQUAL(links[0]->ends[1].nexts[0].second, 0); + BOOST_CHECK_EQUAL(links[0]->ends[1].nexts[1].first.lock().get(), links[5].get()); + BOOST_CHECK_EQUAL(links[0]->ends[1].nexts[1].second, 0); + BOOST_REQUIRE_EQUAL(links[1]->ends[0].nexts.size(), 2); + BOOST_CHECK_EQUAL(links[1]->ends[0].nexts[0].first.lock().get(), links[0].get()); + BOOST_CHECK_EQUAL(links[1]->ends[0].nexts[0].second, 1); + BOOST_CHECK_EQUAL(links[1]->ends[0].nexts[1].first.lock().get(), links[5].get()); + BOOST_CHECK_EQUAL(links[1]->ends[0].nexts[1].second, 0); // Join 1 <-> 2 - BOOST_REQUIRE_EQUAL(links.objects[1]->ends[1].nexts.size(), 2); - BOOST_CHECK_EQUAL(links.objects[1]->ends[1].nexts[0].first.lock().get(), links.objects[2].get()); - BOOST_CHECK_EQUAL(links.objects[1]->ends[1].nexts[0].second, 0); - BOOST_REQUIRE_EQUAL(links.objects[2]->ends[0].nexts.size(), 2); - BOOST_CHECK_EQUAL(links.objects[2]->ends[0].nexts[0].first.lock().get(), links.objects[1].get()); - BOOST_CHECK_EQUAL(links.objects[2]->ends[0].nexts[0].second, 1); + BOOST_REQUIRE_EQUAL(links[1]->ends[1].nexts.size(), 2); + BOOST_CHECK_EQUAL(links[1]->ends[1].nexts[0].first.lock().get(), links[2].get()); + BOOST_CHECK_EQUAL(links[1]->ends[1].nexts[0].second, 0); + BOOST_REQUIRE_EQUAL(links[2]->ends[0].nexts.size(), 2); + BOOST_CHECK_EQUAL(links[2]->ends[0].nexts[0].first.lock().get(), links[1].get()); + BOOST_CHECK_EQUAL(links[2]->ends[0].nexts[0].second, 1); } BOOST_DATA_TEST_CASE(routeTo_nodeNotInNetwork, INVALID_NODES, dest) { - const auto & start = links.objects.front()->ends[1]; + const auto & start = links[0]->ends[1]; BOOST_CHECK_THROW((void)routeFromTo(start, dest), std::out_of_range); } BOOST_AUTO_TEST_CASE(routeTo_noSteps) { - const auto & start = links.objects.front()->ends[1]; + const auto & start = links[0]->ends[1]; auto r = this->routeFromTo(start, p100); BOOST_CHECK(r.empty()); } BOOST_AUTO_TEST_CASE(routeTo_upStream_to2) { - const auto & start = links.objects.front()->ends[1]; + const auto & start = links[0]->ends[1]; auto r = this->routeFromTo(start, p200); BOOST_REQUIRE_EQUAL(r.size(), 1); - BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[1].get()); + BOOST_CHECK_EQUAL(r[0].first.lock().get(), links[1].get()); } BOOST_AUTO_TEST_CASE(routeTo_upStream_to3) { - const auto & start = links.objects[0]->ends[1]; + const auto & start = links[0]->ends[1]; auto r = this->routeFromTo(start, p300); BOOST_REQUIRE_EQUAL(r.size(), 2); - BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[1].get()); - BOOST_CHECK_EQUAL(r[1].first.lock().get(), links.objects[2].get()); + BOOST_CHECK_EQUAL(r[0].first.lock().get(), links[1].get()); + BOOST_CHECK_EQUAL(r[1].first.lock().get(), links[2].get()); } BOOST_AUTO_TEST_CASE(routeTo_downStream_to0) { - const auto & start = links.objects[2]->ends[0]; + const auto & start = links[2]->ends[0]; auto r = this->routeFromTo(start, p000); BOOST_REQUIRE_EQUAL(r.size(), 2); - BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[1].get()); - BOOST_CHECK_EQUAL(r[1].first.lock().get(), links.objects[0].get()); + BOOST_CHECK_EQUAL(r[0].first.lock().get(), links[1].get()); + BOOST_CHECK_EQUAL(r[1].first.lock().get(), links[0].get()); } BOOST_AUTO_TEST_CASE(routeTo_upStream_3to300) { - const auto & start = links.objects[3]->ends[1]; + const auto & start = links[3]->ends[1]; auto r = this->routeFromTo(start, p300); BOOST_REQUIRE_EQUAL(r.size(), 2); - BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[4].get()); - BOOST_CHECK_EQUAL(r[1].first.lock().get(), links.objects[2].get()); + BOOST_CHECK_EQUAL(r[0].first.lock().get(), links[4].get()); + BOOST_CHECK_EQUAL(r[1].first.lock().get(), links[2].get()); } BOOST_AUTO_TEST_CASE(routeTo_downStream_3to300) { - const auto & start = links.objects[3]->ends[0]; + const auto & start = links[3]->ends[0]; auto r = this->routeFromTo(start, p300); BOOST_REQUIRE_EQUAL(r.size(), 3); - BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[0].get()); - BOOST_CHECK_EQUAL(r[1].first.lock().get(), links.objects[1].get()); - BOOST_CHECK_EQUAL(r[2].first.lock().get(), links.objects[2].get()); + BOOST_CHECK_EQUAL(r[0].first.lock().get(), links[0].get()); + BOOST_CHECK_EQUAL(r[1].first.lock().get(), links[1].get()); + BOOST_CHECK_EQUAL(r[2].first.lock().get(), links[2].get()); } BOOST_AUTO_TEST_SUITE_END() diff --git a/ui/builders/freeExtend.cpp b/ui/builders/freeExtend.cpp index ab5a998..09e1c75 100644 --- a/ui/builders/freeExtend.cpp +++ b/ui/builders/freeExtend.cpp @@ -16,10 +16,10 @@ BuilderFreeExtend::move( { if (p1) { if (const auto p = network->intersectRayNodes(ray)) { - candidateLinks.objects = network->candidateJoins(*p1, p->pos); + candidateLinks = network->candidateJoins(*p1, p->pos); } else if (const auto p = geoData->intersectRay(ray)) { - candidateLinks.objects = network->candidateExtend(*p1, p->first); + candidateLinks = network->candidateExtend(*p1, p->first); } else { candidateLinks.removeAll(); diff --git a/ui/builders/join.cpp b/ui/builders/join.cpp index 6941e23..161b081 100644 --- a/ui/builders/join.cpp +++ b/ui/builders/join.cpp @@ -15,7 +15,7 @@ BuilderJoin::move(Network * network, const GeoData *, const SDL_MouseMotionEvent { if (p1) { if (const auto p = network->intersectRayNodes(ray)) { - candidateLinks.objects = network->candidateJoins(p1->pos, p->pos); + candidateLinks = network->candidateJoins(p1->pos, p->pos); } else { candidateLinks.removeAll(); diff --git a/ui/builders/straight.cpp b/ui/builders/straight.cpp index 338aa8a..6815689 100644 --- a/ui/builders/straight.cpp +++ b/ui/builders/straight.cpp @@ -17,7 +17,7 @@ BuilderStraight::move( { if (p1) { if (const auto p = geoData->intersectRay(ray)) { - candidateLinks.objects = network->candidateStraight(*p1, p->first); + candidateLinks = network->candidateStraight(*p1, p->first); } else { candidateLinks.removeAll(); -- cgit v1.2.3 From a53a6672b8fc89834b1ec3aa7afae150a617473f Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 23 Mar 2025 13:00:03 +0000 Subject: Populate typed collection of pointers create and emplace only, so far --- lib/collection.h | 79 +++++++++++++++++++++++++++++++++++++++++++++--- test/test-collection.cpp | 16 +++++++++- 2 files changed, 89 insertions(+), 6 deletions(-) diff --git a/lib/collection.h b/lib/collection.h index c843bad..e68a8db 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -6,12 +6,13 @@ #include #include -template class Collection { +template class Collection { public: virtual ~Collection() = default; using Object = Ptr::element_type; using Objects = std::vector; + template using OtherObjects = std::vector; Collection & operator=(Objects && other) @@ -34,10 +35,14 @@ public: if constexpr (requires(Ptr ptr) { ptr = std::make_shared(std::forward(params)...); }) { auto obj = std::make_shared(std::forward(params)...); objects.emplace_back(obj); + addOthersType(obj.get()); return obj; } else { - return static_cast(objects.emplace_back(std::make_unique(std::forward(params)...)).get()); + auto obj = static_cast( + objects.emplace_back(std::make_unique(std::forward(params)...)).get()); + addOthersType(obj); + return obj; } } @@ -136,11 +141,75 @@ public: auto emplace(Ptr && ptr) { - return objects.emplace_back(std::move(ptr)); + auto object = objects.emplace_back(std::move(ptr)); + addOthersPtr(object.get()); + return object; } protected: Objects objects; + std::tuple...> otherObjects; + + template + void + addOthersType(T * obj) + { + applyToOthersType( + [](auto & others, auto ptr) { + others.emplace_back(ptr); + }, + obj); + } + + void + addOthersPtr(Object * obj) + { + applyToOthersPtr( + [](auto & others, auto ptr) { + others.emplace_back(ptr); + }, + obj); + } + + template + requires(sizeof...(Others) == 0) + void + applyToOthersType(const auto &, T *) + { + } + + void + applyToOthersPtr(const auto &, Object *) + requires(sizeof...(Others) == 0) + { + } + + template + requires(sizeof...(Others) > 0) + void + applyToOthersType(const auto & func, T * obj) + { + ( + [&]() { + if constexpr (std::is_convertible_v) { + std::invoke(func, std::get>(otherObjects), obj); + } + }(), + ...); + } + + void + applyToOthersPtr(const auto & func, Object * obj) + requires(sizeof...(Others) > 0) + { + ( + [&]() { + if (auto ptr = dynamic_cast(obj)) { + std::invoke(func, std::get>(otherObjects), ptr); + } + }(), + ...); + } template auto @@ -168,5 +237,5 @@ protected: } }; -template using SharedCollection = Collection>; -template using UniqueCollection = Collection>; +template using SharedCollection = Collection, Others...>; +template using UniqueCollection = Collection, Others...>; diff --git a/test/test-collection.cpp b/test/test-collection.cpp index 00298bb..5c67a8c 100644 --- a/test/test-collection.cpp +++ b/test/test-collection.cpp @@ -34,7 +34,7 @@ public: } }; -using TestCollection = SharedCollection; +using TestCollection = SharedCollection; BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::Objects::const_iterator) BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::Objects::const_reverse_iterator) @@ -51,12 +51,24 @@ BOOST_AUTO_TEST_CASE(empty) BOOST_AUTO_TEST_CASE(a_base) { auto b = create(); + BOOST_CHECK_EQUAL(objects.size(), 1); + BOOST_CHECK(std::get>(otherObjects).empty()); BOOST_REQUIRE(apply(&Base::add)); BOOST_CHECK_EQUAL(b->total, 1); const auto i = applyOne(&Base::add); BOOST_CHECK_EQUAL(i, end()); } +BOOST_AUTO_TEST_CASE(emplace_others) +{ + emplace(std::make_shared()); + BOOST_CHECK_EQUAL(objects.size(), 1); + BOOST_CHECK(std::get>(otherObjects).empty()); + emplace(std::make_shared()); + BOOST_CHECK_EQUAL(objects.size(), 2); + BOOST_CHECK_EQUAL(std::get>(otherObjects).size(), 1); +} + BOOST_AUTO_TEST_CASE(a_rbase) { auto b = create(); @@ -69,6 +81,8 @@ BOOST_AUTO_TEST_CASE(a_rbase) BOOST_AUTO_TEST_CASE(a_sub) { auto s = create(); + BOOST_CHECK_EQUAL(objects.size(), 1); + BOOST_CHECK_EQUAL(std::get>(otherObjects).size(), 1); BOOST_REQUIRE(apply(&Base::add)); BOOST_CHECK_EQUAL(s->total, 2); const auto i = applyOne(&Base::add); -- cgit v1.2.3 From 422b466caa4bcd4b30f08d9a24535dad8ed20f0d Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 23 Mar 2025 13:56:24 +0000 Subject: Other objects support in operator= --- lib/collection.h | 8 ++++-- test/test-collection.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/lib/collection.h b/lib/collection.h index e68a8db..329b681 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -18,6 +18,10 @@ public: operator=(Objects && other) { objects = std::move(other); + ((std::get>(otherObjects).clear()), ...); + for (const auto & other : objects) { + addOthersPtr(other.get()); + } return *this; } @@ -138,10 +142,10 @@ public: return objects.empty(); } - auto + decltype(auto) emplace(Ptr && ptr) { - auto object = objects.emplace_back(std::move(ptr)); + const auto & object = objects.emplace_back(std::move(ptr)); addOthersPtr(object.get()); return object; } diff --git a/test/test-collection.cpp b/test/test-collection.cpp index 5c67a8c..0cd08d5 100644 --- a/test/test-collection.cpp +++ b/test/test-collection.cpp @@ -43,6 +43,7 @@ BOOST_FIXTURE_TEST_SUITE(tc, TestCollection) BOOST_AUTO_TEST_CASE(empty) { + BOOST_CHECK(TestCollection::empty()); BOOST_REQUIRE(!apply(&Base::add)); const auto i = applyOne(&Base::add); BOOST_CHECK_EQUAL(i, end()); @@ -90,6 +91,70 @@ BOOST_AUTO_TEST_CASE(a_sub) BOOST_CHECK_EQUAL(*i, s); } +BOOST_AUTO_TEST_CASE(begin_end) +{ + BOOST_CHECK_EQUAL(0, std::distance(begin(), end())); + create(); + create(); + BOOST_CHECK_EQUAL(2, std::distance(begin(), end())); +} + +BOOST_AUTO_TEST_CASE(rbegin_rend) +{ + BOOST_CHECK_EQUAL(0, std::distance(rbegin(), rend())); + create(); + create(); + BOOST_CHECK_EQUAL(2, std::distance(rbegin(), rend())); +} + +BOOST_AUTO_TEST_SUITE_END() + +using TestUniqueCollection = UniqueCollection; +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestUniqueCollection::Objects::const_iterator) +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestUniqueCollection::Objects::const_reverse_iterator) + +BOOST_FIXTURE_TEST_SUITE(utc, TestUniqueCollection) + +BOOST_AUTO_TEST_CASE(unique_create) +{ + create(); + BOOST_CHECK_EQUAL(objects.size(), 1); + BOOST_CHECK(std::get>(otherObjects).empty()); + create(); + BOOST_CHECK_EQUAL(objects.size(), 2); + BOOST_CHECK_EQUAL(std::get>(otherObjects).size(), 1); +} + +BOOST_AUTO_TEST_CASE(move_assign) +{ + create(); + create(); + + TestUniqueCollection::Objects other; + TestUniqueCollection::operator=(std::move(other)); + BOOST_CHECK(objects.empty()); + BOOST_CHECK(std::get>(otherObjects).empty()); + + other.push_back(std::make_unique()); + other.push_back(std::make_unique()); + TestUniqueCollection::operator=(std::move(other)); + BOOST_CHECK_EQUAL(objects.size(), 2); + BOOST_CHECK_EQUAL(std::get>(otherObjects).size(), 1); + BOOST_CHECK(other.empty()); +} + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_FIXTURE_TEST_SUITE(btc, UniqueCollection) + +BOOST_AUTO_TEST_CASE(no_others) +{ + create(); + create(); + emplace(std::make_unique()); + emplace(std::make_unique()); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_CASE(wrapped_ptr_file_cons) -- cgit v1.2.3 From 035299f23a9207bb521b19e2f77154c276cf3033 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 23 Mar 2025 14:21:12 +0000 Subject: Other objects support in removeAll/clear removeAll requires a type that is one of Others, clear clears everything regardless of type. --- application/main.cpp | 4 ++-- lib/collection.h | 11 ++++++++++- test/test-collection.cpp | 24 ++++++++++++++++++++++++ ui/builders/freeExtend.cpp | 4 ++-- ui/builders/join.cpp | 4 ++-- ui/builders/straight.cpp | 6 +++--- 6 files changed, 43 insertions(+), 10 deletions(-) diff --git a/application/main.cpp b/application/main.cpp index 1ca2192..9120376 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -90,7 +90,7 @@ public: for (int N = 0; N < 6; N++) { train->create(b47); } - train->orders.removeAll(); + train->orders.clear(); train->orders.create(&train->orders); train->currentActivity = train->orders.current()->createActivity(); @@ -113,7 +113,7 @@ public: mainLoop(); - world.removeAll(); + world.clear(); return 0; } }; diff --git a/lib/collection.h b/lib/collection.h index 329b681..98f043b 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -103,15 +103,24 @@ public: return applyOne_internal(objects.rbegin(), objects.rend(), m, std::forward(params)...); } - template + template + requires(std::is_convertible_v || ...) auto removeAll() { + std::get>(otherObjects).clear(); return std::erase_if(objects, [](auto && op) { return dynamic_cast(op.get()); }); } + void + clear() + { + ((std::get>(otherObjects).clear()), ...); + objects.clear(); + } + [[nodiscard]] auto begin() const { diff --git a/test/test-collection.cpp b/test/test-collection.cpp index 0cd08d5..13df95c 100644 --- a/test/test-collection.cpp +++ b/test/test-collection.cpp @@ -143,6 +143,30 @@ BOOST_AUTO_TEST_CASE(move_assign) BOOST_CHECK(other.empty()); } +BOOST_AUTO_TEST_CASE(clearAll) +{ + create(); + create(); + emplace(std::make_unique()); + emplace(std::make_unique()); + + clear(); + BOOST_CHECK(objects.empty()); + BOOST_CHECK(std::get>(otherObjects).empty()); +} + +BOOST_AUTO_TEST_CASE(removeAllOfSub) +{ + create(); + create(); + emplace(std::make_unique()); + emplace(std::make_unique()); + + removeAll(); + BOOST_CHECK_EQUAL(objects.size(), 2); + BOOST_CHECK(std::get>(otherObjects).empty()); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_FIXTURE_TEST_SUITE(btc, UniqueCollection) diff --git a/ui/builders/freeExtend.cpp b/ui/builders/freeExtend.cpp index 09e1c75..aff7cd7 100644 --- a/ui/builders/freeExtend.cpp +++ b/ui/builders/freeExtend.cpp @@ -22,11 +22,11 @@ BuilderFreeExtend::move( candidateLinks = network->candidateExtend(*p1, p->first); } else { - candidateLinks.removeAll(); + candidateLinks.clear(); } } else { - candidateLinks.removeAll(); + candidateLinks.clear(); } } diff --git a/ui/builders/join.cpp b/ui/builders/join.cpp index 161b081..f6cbce5 100644 --- a/ui/builders/join.cpp +++ b/ui/builders/join.cpp @@ -18,7 +18,7 @@ BuilderJoin::move(Network * network, const GeoData *, const SDL_MouseMotionEvent candidateLinks = network->candidateJoins(p1->pos, p->pos); } else { - candidateLinks.removeAll(); + candidateLinks.clear(); } } } @@ -33,7 +33,7 @@ BuilderJoin::click( if (p1) { create(network, geoData, p1, p); p1.reset(); - candidateLinks.removeAll(); + candidateLinks.clear(); } else { p1 = p; diff --git a/ui/builders/straight.cpp b/ui/builders/straight.cpp index 6815689..e7d83b5 100644 --- a/ui/builders/straight.cpp +++ b/ui/builders/straight.cpp @@ -20,7 +20,7 @@ BuilderStraight::move( candidateLinks = network->candidateStraight(*p1, p->first); } else { - candidateLinks.removeAll(); + candidateLinks.clear(); } } } @@ -34,7 +34,7 @@ BuilderStraight::click( if (const auto p = geoData->intersectRay(ray)) { if (p1) { create(network, geoData, *p1, p->first); - candidateLinks.removeAll(); + candidateLinks.clear(); p1.reset(); } else { @@ -44,7 +44,7 @@ BuilderStraight::click( return; case SDL_BUTTON_MIDDLE: p1.reset(); - candidateLinks.removeAll(); + candidateLinks.clear(); return; } } -- cgit v1.2.3 From 5f12ed9f259a825efc7c2354230932712273fab6 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 23 Mar 2025 15:25:03 +0000 Subject: Use otherObjects where possible for find --- lib/collection.h | 32 ++++++++++++++++++++++++++------ test/test-collection.cpp | 29 +++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/lib/collection.h b/lib/collection.h index 98f043b..6ee6c82 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -54,12 +54,19 @@ public: T * find() { - if (auto i = std::find_if(objects.begin(), objects.end(), - [](auto && o) { - return (dynamic_cast(o.get())); - }); - i != objects.end()) { - return static_cast(i->get()); + const auto & srcObjects = containerFor(); + if constexpr (std::is_convertible_v::value_type, T *>) { + if (srcObjects.empty()) { + return nullptr; + } + return srcObjects.front(); + } + else if (auto i = std::find_if(srcObjects.begin(), srcObjects.end(), + [](auto && o) { + return dynamic_cast(std::to_address(o)) != nullptr; + }); + i != srcObjects.end()) { + return static_cast(std::to_address(*i)); } return nullptr; } @@ -224,6 +231,19 @@ protected: ...); } + template + [[nodiscard]] + const auto & + containerFor() const + { + if constexpr ((std::is_convertible_v || ...)) { + return std::get>(otherObjects); + } + else { + return objects; + } + } + template auto apply_internal(const auto begin, const auto end, const auto & m, Params &&... params) const diff --git a/test/test-collection.cpp b/test/test-collection.cpp index 13df95c..7fadcf9 100644 --- a/test/test-collection.cpp +++ b/test/test-collection.cpp @@ -47,6 +47,8 @@ BOOST_AUTO_TEST_CASE(empty) BOOST_REQUIRE(!apply(&Base::add)); const auto i = applyOne(&Base::add); BOOST_CHECK_EQUAL(i, end()); + BOOST_CHECK(!find()); + BOOST_CHECK(!find()); } BOOST_AUTO_TEST_CASE(a_base) @@ -58,16 +60,20 @@ BOOST_AUTO_TEST_CASE(a_base) BOOST_CHECK_EQUAL(b->total, 1); const auto i = applyOne(&Base::add); BOOST_CHECK_EQUAL(i, end()); + BOOST_CHECK_EQUAL(b.get(), find()); + BOOST_CHECK(!find()); } BOOST_AUTO_TEST_CASE(emplace_others) { - emplace(std::make_shared()); + auto b = emplace(std::make_shared()); BOOST_CHECK_EQUAL(objects.size(), 1); BOOST_CHECK(std::get>(otherObjects).empty()); - emplace(std::make_shared()); + auto s = emplace(std::make_shared()); BOOST_CHECK_EQUAL(objects.size(), 2); BOOST_CHECK_EQUAL(std::get>(otherObjects).size(), 1); + BOOST_CHECK_EQUAL(b.get(), find()); + BOOST_CHECK_EQUAL(s.get(), find()); } BOOST_AUTO_TEST_CASE(a_rbase) @@ -107,6 +113,25 @@ BOOST_AUTO_TEST_CASE(rbegin_rend) BOOST_CHECK_EQUAL(2, std::distance(rbegin(), rend())); } +BOOST_AUTO_TEST_CASE(createCreate) +{ + auto b = findOrCreate(); + BOOST_CHECK(b); + auto b2 = findOrCreate(); + BOOST_CHECK_EQUAL(b, b2); + auto s = findOrCreate(); + BOOST_CHECK_NE(s, b); + auto s2 = findOrCreate(); + BOOST_CHECK_EQUAL(s, s2); +} + +BOOST_AUTO_TEST_CASE(createCreateSub) +{ + auto s = findOrCreate(); + auto b = findOrCreate(); + BOOST_CHECK_EQUAL(s, b); +} + BOOST_AUTO_TEST_SUITE_END() using TestUniqueCollection = UniqueCollection; -- cgit v1.2.3 From 8734626b2c6fc36acc59e0d7bf7e51d4cff719de Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 23 Mar 2025 18:09:16 +0000 Subject: Use typed collections for apply/applyOne --- lib/collection.h | 53 ++++++++++++++++++++++++++++++++---------------- test/test-collection.cpp | 28 +++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/lib/collection.h b/lib/collection.h index 6ee6c82..d32cff1 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -86,28 +86,32 @@ public: auto apply(const auto & m, Params &&... params) const { - return apply_internal(objects.begin(), objects.end(), m, std::forward(params)...); + const auto & srcObjects = containerFor(); + return apply_internal(srcObjects.begin(), srcObjects.end(), m, std::forward(params)...); } template auto rapply(const auto & m, Params &&... params) const { - return apply_internal(objects.rbegin(), objects.rend(), m, std::forward(params)...); + const auto & srcObjects = containerFor(); + return apply_internal(srcObjects.rbegin(), srcObjects.rend(), m, std::forward(params)...); } template auto applyOne(const auto & m, Params &&... params) const { - return applyOne_internal(objects.begin(), objects.end(), m, std::forward(params)...); + const auto & srcObjects = containerFor(); + return applyOne_internal(srcObjects.begin(), srcObjects.end(), m, std::forward(params)...); } template auto rapplyOne(const auto & m, Params &&... params) const { - return applyOne_internal(objects.rbegin(), objects.rend(), m, std::forward(params)...); + const auto & srcObjects = containerFor(); + return applyOne_internal(srcObjects.rbegin(), srcObjects.rend(), m, std::forward(params)...); } template @@ -248,25 +252,40 @@ protected: auto apply_internal(const auto begin, const auto end, const auto & m, Params &&... params) const { - return std::count_if(begin, end, [&m, ¶ms...](auto && op) { - if (auto o = dynamic_cast(op.get())) { - std::invoke(m, o, std::forward(params)...); - return true; - } - return false; - }); + if constexpr (std::is_convertible_v) { + std::for_each(begin, end, [&m, ¶ms...](auto && op) { + std::invoke(m, op, std::forward(params)...); + }); + return std::distance(begin, end); + } + else { + return std::count_if(begin, end, [&m, ¶ms...](auto && op) { + if (auto o = dynamic_cast(op.get())) { + std::invoke(m, o, std::forward(params)...); + return true; + } + return false; + }); + } } template auto applyOne_internal(const auto begin, const auto end, const auto & m, Params &&... params) const { - return std::find_if(begin, end, [&m, ¶ms...](auto && op) { - if (auto o = dynamic_cast(op.get())) { - return std::invoke(m, o, std::forward(params)...); - } - return false; - }); + if constexpr (std::is_convertible_v) { + return std::find_if(begin, end, [&m, ¶ms...](auto && op) { + return std::invoke(m, op, std::forward(params)...); + }); + } + else { + return std::find_if(begin, end, [&m, ¶ms...](auto && op) { + if (auto o = dynamic_cast(op.get())) { + return std::invoke(m, o, std::forward(params)...); + } + return false; + }); + } } }; diff --git a/test/test-collection.cpp b/test/test-collection.cpp index 7fadcf9..5aae9f0 100644 --- a/test/test-collection.cpp +++ b/test/test-collection.cpp @@ -21,6 +21,12 @@ public: return false; } + [[nodiscard]] virtual bool + yes() const + { + return true; + } + unsigned int total {0}; }; @@ -137,6 +143,8 @@ BOOST_AUTO_TEST_SUITE_END() using TestUniqueCollection = UniqueCollection; BOOST_TEST_DONT_PRINT_LOG_VALUE(TestUniqueCollection::Objects::const_iterator) BOOST_TEST_DONT_PRINT_LOG_VALUE(TestUniqueCollection::Objects::const_reverse_iterator) +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestUniqueCollection::Objects::iterator) +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestUniqueCollection::Objects::reverse_iterator) BOOST_FIXTURE_TEST_SUITE(utc, TestUniqueCollection) @@ -204,6 +212,26 @@ BOOST_AUTO_TEST_CASE(no_others) emplace(std::make_unique()); } +BOOST_AUTO_TEST_CASE(applyAll) +{ + create(); + BOOST_CHECK_EQUAL(0, apply(&Base::add)); + BOOST_CHECK_EQUAL(1, apply(&Base::add)); + create(); + BOOST_CHECK_EQUAL(1, apply(&Base::add)); + BOOST_CHECK_EQUAL(2, apply(&Base::add)); +} + +BOOST_AUTO_TEST_CASE(applyOneType) +{ + create(); + BOOST_CHECK_EQUAL(objects.end(), applyOne(&Base::yes)); + BOOST_CHECK_EQUAL(objects.begin(), applyOne(&Base::yes)); + create(); + BOOST_CHECK_EQUAL(objects.begin() + 1, applyOne(&Base::yes)); + BOOST_CHECK_EQUAL(objects.begin(), applyOne(&Base::yes)); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_CASE(wrapped_ptr_file_cons) -- cgit v1.2.3 From 7b44e4f20b91a64b778bcc8b2f00602d4ba13bd2 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 23 Mar 2025 18:46:10 +0000 Subject: Support using typed collections for subclass filtering --- lib/collection.h | 17 +++++++++++++---- test/test-collection.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/lib/collection.h b/lib/collection.h index d32cff1..b786e2f 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -235,13 +235,22 @@ protected: ...); } + template + requires((std::is_convertible_v || ...)) + [[nodiscard]] consteval static bool + idx() + { + size_t typeIdx = 0; + return ((typeIdx++ && std::is_convertible_v) || ...); + } + template [[nodiscard]] - const auto & + constexpr const auto & containerFor() const { if constexpr ((std::is_convertible_v || ...)) { - return std::get>(otherObjects); + return std::get()>(otherObjects); } else { return objects; @@ -260,7 +269,7 @@ protected: } else { return std::count_if(begin, end, [&m, ¶ms...](auto && op) { - if (auto o = dynamic_cast(op.get())) { + if (auto o = dynamic_cast(std::to_address(op))) { std::invoke(m, o, std::forward(params)...); return true; } @@ -280,7 +289,7 @@ protected: } else { return std::find_if(begin, end, [&m, ¶ms...](auto && op) { - if (auto o = dynamic_cast(op.get())) { + if (auto o = dynamic_cast(std::to_address(op))) { return std::invoke(m, o, std::forward(params)...); } return false; diff --git a/test/test-collection.cpp b/test/test-collection.cpp index 5aae9f0..a399845 100644 --- a/test/test-collection.cpp +++ b/test/test-collection.cpp @@ -40,10 +40,15 @@ public: } }; +class Sub1 : public Sub { }; + using TestCollection = SharedCollection; +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::Objects::iterator) BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::Objects::const_iterator) BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::Objects::const_reverse_iterator) +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::OtherObjects::iterator) +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::OtherObjects::const_iterator) BOOST_FIXTURE_TEST_SUITE(tc, TestCollection) @@ -55,6 +60,7 @@ BOOST_AUTO_TEST_CASE(empty) BOOST_CHECK_EQUAL(i, end()); BOOST_CHECK(!find()); BOOST_CHECK(!find()); + BOOST_CHECK(!find()); } BOOST_AUTO_TEST_CASE(a_base) @@ -68,6 +74,7 @@ BOOST_AUTO_TEST_CASE(a_base) BOOST_CHECK_EQUAL(i, end()); BOOST_CHECK_EQUAL(b.get(), find()); BOOST_CHECK(!find()); + BOOST_CHECK(!find()); } BOOST_AUTO_TEST_CASE(emplace_others) @@ -80,6 +87,7 @@ BOOST_AUTO_TEST_CASE(emplace_others) BOOST_CHECK_EQUAL(std::get>(otherObjects).size(), 1); BOOST_CHECK_EQUAL(b.get(), find()); BOOST_CHECK_EQUAL(s.get(), find()); + BOOST_CHECK(!find()); } BOOST_AUTO_TEST_CASE(a_rbase) @@ -103,6 +111,38 @@ BOOST_AUTO_TEST_CASE(a_sub) BOOST_CHECK_EQUAL(*i, s); } +BOOST_AUTO_TEST_CASE(filter) +{ + create(); + BOOST_CHECK_EQUAL(1, apply(&Base::yes)); + BOOST_CHECK_EQUAL(0, apply(&Base::yes)); + BOOST_CHECK_EQUAL(0, apply(&Base::yes)); + BOOST_CHECK_EQUAL(objects.begin(), applyOne(&Base::yes)); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).end(), applyOne(&Base::yes)); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).end(), applyOne(&Base::yes)); + create(); + BOOST_CHECK_EQUAL(2, apply(&Base::yes)); + BOOST_CHECK_EQUAL(1, apply(&Base::yes)); + BOOST_CHECK_EQUAL(0, apply(&Base::yes)); + BOOST_CHECK_EQUAL(objects.begin(), applyOne(&Base::yes)); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).begin(), applyOne(&Base::yes)); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).end(), applyOne(&Base::yes)); + create(); + BOOST_CHECK_EQUAL(3, apply(&Base::yes)); + BOOST_CHECK_EQUAL(2, apply(&Base::yes)); + BOOST_CHECK_EQUAL(1, apply(&Base::yes)); + BOOST_CHECK_EQUAL(objects.begin(), applyOne(&Base::yes)); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).begin(), applyOne(&Base::yes)); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).begin() + 1, applyOne(&Base::yes)); + + BOOST_CHECK_EQUAL(std::get()>(otherObjects).size(), 2); + BOOST_CHECK_EQUAL(std::get()>(otherObjects).size(), 2); + + BOOST_CHECK_EQUAL(&objects, &containerFor()); + BOOST_CHECK_EQUAL(&std::get<0>(otherObjects), &containerFor()); + BOOST_CHECK_EQUAL(&std::get<0>(otherObjects), &containerFor()); +} + BOOST_AUTO_TEST_CASE(begin_end) { BOOST_CHECK_EQUAL(0, std::distance(begin(), end())); @@ -230,6 +270,8 @@ BOOST_AUTO_TEST_CASE(applyOneType) create(); BOOST_CHECK_EQUAL(objects.begin() + 1, applyOne(&Base::yes)); BOOST_CHECK_EQUAL(objects.begin(), applyOne(&Base::yes)); + create(); + BOOST_CHECK_EQUAL(objects.begin() + 2, applyOne(&Base::yes)); } BOOST_AUTO_TEST_SUITE_END() -- cgit v1.2.3 From 5edabac3544f2d270044a6bfe4f0e9709b3799a4 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 23 Mar 2025 21:01:09 +0000 Subject: Add Renderable typed collection to GameState worldObjects --- game/gamestate.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/game/gamestate.h b/game/gamestate.h index c5ad239..85cb5db 100644 --- a/game/gamestate.h +++ b/game/gamestate.h @@ -8,6 +8,7 @@ class WorldObject; class Terrain; class Environment; +class Renderable; class GameState { public: @@ -16,7 +17,7 @@ public: NO_MOVE(GameState); NO_COPY(GameState); - SharedCollection world; + SharedCollection world; std::shared_ptr terrain; std::shared_ptr environment; AssetFactory::Assets assets; -- cgit v1.2.3 From ae99a2124da32e4d2474e6dc6cf54322b688b743 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 24 Mar 2025 18:10:30 +0000 Subject: Use is_base_of_v instead of is_convertible_v to choose OtherObjects --- lib/collection.h | 6 +++--- test/test-collection.cpp | 4 ++++ ui/gameMainWindow.cpp | 6 +++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/collection.h b/lib/collection.h index b786e2f..8e9cf1a 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -236,12 +236,12 @@ protected: } template - requires((std::is_convertible_v || ...)) + requires((std::is_base_of_v || ...)) [[nodiscard]] consteval static bool idx() { size_t typeIdx = 0; - return ((typeIdx++ && std::is_convertible_v) || ...); + return ((typeIdx++ && std::is_base_of_v) || ...); } template @@ -249,7 +249,7 @@ protected: constexpr const auto & containerFor() const { - if constexpr ((std::is_convertible_v || ...)) { + if constexpr ((std::is_base_of_v || ...)) { return std::get()>(otherObjects); } else { diff --git a/test/test-collection.cpp b/test/test-collection.cpp index a399845..90a3bd1 100644 --- a/test/test-collection.cpp +++ b/test/test-collection.cpp @@ -113,6 +113,10 @@ BOOST_AUTO_TEST_CASE(a_sub) BOOST_AUTO_TEST_CASE(filter) { + static_assert(TestCollection::idx() == 0); + static_assert(TestCollection::idx() == 0); + static_assert(TestCollection::idx() == 0); + static_assert(TestCollection::idx() == 0); create(); BOOST_CHECK_EQUAL(1, apply(&Base::yes)); BOOST_CHECK_EQUAL(0, apply(&Base::yes)); diff --git a/ui/gameMainWindow.cpp b/ui/gameMainWindow.cpp index 07901b3..f8c568b 100644 --- a/ui/gameMainWindow.cpp +++ b/ui/gameMainWindow.cpp @@ -80,7 +80,7 @@ GameMainWindow::content(const SceneShader & shader, const Frustum & frustum) con renderable->render(shader, frustum); } } - gameState->world.apply(&Renderable::render, shader, frustum); + gameState->world.apply(&Renderable::render, shader, frustum); uiComponents.apply(&WorldOverlay::render, shader, frustum); } @@ -93,7 +93,7 @@ GameMainWindow::environment(const SceneShader &, const SceneRenderer & r) const void GameMainWindow::lights(const SceneShader & shader) const { - gameState->world.apply(&Renderable::lights, shader); + gameState->world.apply(&Renderable::lights, shader); } void @@ -104,5 +104,5 @@ GameMainWindow::shadows(const ShadowMapper & shadowMapper, const Frustum & frust renderable->shadows(shadowMapper, frustum); } } - gameState->world.apply(&Renderable::shadows, shadowMapper, frustum); + gameState->world.apply(&Renderable::shadows, shadowMapper, frustum); } -- cgit v1.2.3 From 00b932d71984991c6d79875478fc25af172b7bc1 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 25 Mar 2025 02:59:04 +0000 Subject: Fix lookup idx for otherObjects suitable for T --- lib/collection.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/collection.h b/lib/collection.h index 8e9cf1a..b7d7d81 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -237,11 +237,12 @@ protected: template requires((std::is_base_of_v || ...)) - [[nodiscard]] consteval static bool + [[nodiscard]] consteval static size_t idx() { size_t typeIdx = 0; - return ((typeIdx++ && std::is_base_of_v) || ...); + auto found = ((++typeIdx && std::is_base_of_v) || ...); + return typeIdx - found; } template -- cgit v1.2.3 From d122a22ef123e8d0e5ca05ff760645da71b248b0 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 27 Mar 2025 01:02:23 +0000 Subject: applyToOthersType allows passing any params in, not just a T --- lib/collection.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/collection.h b/lib/collection.h index b7d7d81..853b982 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -195,10 +195,10 @@ protected: obj); } - template + template requires(sizeof...(Others) == 0) void - applyToOthersType(const auto &, T *) + applyToOthersType(const auto &, Params...) { } @@ -208,15 +208,16 @@ protected: { } - template + template requires(sizeof...(Others) > 0) void - applyToOthersType(const auto & func, T * obj) + applyToOthersType(const auto & func, Params &&... params) { ( [&]() { if constexpr (std::is_convertible_v) { - std::invoke(func, std::get>(otherObjects), obj); + std::invoke( + func, std::get>(otherObjects), std::forward(params)...); } }(), ...); -- cgit v1.2.3 From ad5108bd5d8c91bc8d3cab0ab6240c4c6dc71ebc Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 27 Mar 2025 23:39:11 +0000 Subject: Fix up removeAll and test with more complex hierarchy --- lib/collection.h | 20 +++++++++--- test/test-collection.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 6 deletions(-) diff --git a/lib/collection.h b/lib/collection.h index 853b982..5f39e71 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -115,14 +115,24 @@ public: } template - requires(std::is_convertible_v || ...) + requires std::is_base_of_v auto removeAll() { - std::get>(otherObjects).clear(); - return std::erase_if(objects, [](auto && op) { - return dynamic_cast(op.get()); - }); + auto removeAllFrom = [](auto & container) { + if constexpr (std::is_base_of_v) { + const auto size = container.size(); + container.clear(); + return size; + } + else { + return std::erase_if(container, [](auto && objPtr) -> bool { + return dynamic_cast(std::to_address(objPtr)); + }); + } + }; + (removeAllFrom(std::get>(otherObjects)), ...); + return removeAllFrom(objects); } void diff --git a/test/test-collection.cpp b/test/test-collection.cpp index 90a3bd1..b0f2c43 100644 --- a/test/test-collection.cpp +++ b/test/test-collection.cpp @@ -42,6 +42,15 @@ public: class Sub1 : public Sub { }; +class Sub2 : public Sub { }; + +class Base2 { +public: + virtual ~Base2() = default; +}; + +class Multi : public Sub1, public Base2 { }; + using TestCollection = SharedCollection; BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::Objects::iterator) @@ -238,8 +247,9 @@ BOOST_AUTO_TEST_CASE(removeAllOfSub) create(); emplace(std::make_unique()); emplace(std::make_unique()); + emplace(std::make_unique()); - removeAll(); + BOOST_CHECK_EQUAL(removeAll(), 3); BOOST_CHECK_EQUAL(objects.size(), 2); BOOST_CHECK(std::get>(otherObjects).empty()); } @@ -280,6 +290,75 @@ BOOST_AUTO_TEST_CASE(applyOneType) BOOST_AUTO_TEST_SUITE_END() +using MultiCollection = Collection, Multi, Sub, Base2>; + +BOOST_FIXTURE_TEST_SUITE(multi, MultiCollection) + +BOOST_AUTO_TEST_CASE(addMulti) +{ + static_assert(MultiCollection::idx() == 0); + static_assert(MultiCollection::idx() == 1); + static_assert(MultiCollection::idx() == 2); + create(); + BOOST_CHECK_EQUAL(objects.size(), 1); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); + create(); + BOOST_CHECK_EQUAL(objects.size(), 2); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 1); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); + create(); + BOOST_CHECK_EQUAL(objects.size(), 3); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 2); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); + create(); + BOOST_CHECK_EQUAL(objects.size(), 4); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 3); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); + create(); + BOOST_CHECK_EQUAL(objects.size(), 5); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 1); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 4); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 1); +} + +BOOST_AUTO_TEST_CASE(removeMulti) +{ + create(); + create(); + create(); + create(); + create(); + BOOST_CHECK_EQUAL(objects.size(), 5); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 1); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 4); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 1); + + BOOST_CHECK_EQUAL(removeAll(), 1); + BOOST_CHECK_EQUAL(objects.size(), 4); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 3); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); + + BOOST_CHECK_EQUAL(removeAll(), 3); + BOOST_CHECK_EQUAL(objects.size(), 1); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); + + BOOST_CHECK_EQUAL(removeAll(), 1); + BOOST_CHECK_EQUAL(objects.size(), 0); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); +} + +BOOST_AUTO_TEST_SUITE_END() + BOOST_AUTO_TEST_CASE(wrapped_ptr_file_cons) { using FilePtr = wrapped_ptr; -- cgit v1.2.3 From b710db753c92522f9acfbef4a02b440e4178b067 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 28 Mar 2025 01:18:50 +0000 Subject: Add standard special members --- lib/collection.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/collection.h b/lib/collection.h index 5f39e71..91a36c2 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -3,13 +3,17 @@ #include #include #include +#include #include #include template class Collection { public: + Collection() = default; virtual ~Collection() = default; + DEFAULT_MOVE_NO_COPY(Collection); + using Object = Ptr::element_type; using Objects = std::vector; template using OtherObjects = std::vector; -- cgit v1.2.3 From 014373a9ef99e4c9cc1c15f52e0946ea413c868d Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 28 Mar 2025 01:20:16 +0000 Subject: Expose size of each container by type --- lib/collection.h | 8 ++++++++ test/test-collection.cpp | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/collection.h b/lib/collection.h index 91a36c2..22dcc52 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -35,6 +35,14 @@ public: return objects[idx]; } + template + requires(std::is_same_v || (std::is_base_of_v || ...)) + [[nodiscard]] auto + size() const noexcept + { + return containerFor().size(); + } + template auto create(Params &&... params) diff --git a/test/test-collection.cpp b/test/test-collection.cpp index b0f2c43..620d3ab 100644 --- a/test/test-collection.cpp +++ b/test/test-collection.cpp @@ -320,10 +320,10 @@ BOOST_AUTO_TEST_CASE(addMulti) BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 3); BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); create(); - BOOST_CHECK_EQUAL(objects.size(), 5); - BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 1); - BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 4); - BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 1); + BOOST_CHECK_EQUAL(size(), 5); + BOOST_CHECK_EQUAL(size(), 1); + BOOST_CHECK_EQUAL(size(), 4); + BOOST_CHECK_EQUAL(size(), 1); } BOOST_AUTO_TEST_CASE(removeMulti) -- cgit v1.2.3 From a8b2b32342738aaba6dfc662df232a67729a6bdb Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 28 Mar 2025 03:00:55 +0000 Subject: Fix check for bulk removal --- lib/collection.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/collection.h b/lib/collection.h index 22dcc52..1c77e1c 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -132,7 +132,7 @@ public: removeAll() { auto removeAllFrom = [](auto & container) { - if constexpr (std::is_base_of_v) { + if constexpr (std::is_base_of_v>) { const auto size = container.size(); container.clear(); return size; -- cgit v1.2.3