From 073001a3ec017155b90fc56fa46cd4f617512750 Mon Sep 17 00:00:00 2001 From: Antoine Lambert Date: Tue, 17 Dec 2024 19:58:16 +0100 Subject: [PATCH] talipot-core/GraphImpl: Simplify node/edge iterators implementation Move iterators implementation from GraphStorage to GraphImpl and use iterator helpers to reimplement them. --- .../include/talipot/GraphStorage.h | 48 +----- library/talipot-core/src/GraphImpl.cpp | 27 ++- library/talipot-core/src/GraphStorage.cpp | 155 +----------------- library/talipot-core/src/Observable.cpp | 8 +- 4 files changed, 25 insertions(+), 213 deletions(-) diff --git a/library/talipot-core/include/talipot/GraphStorage.h b/library/talipot-core/include/talipot/GraphStorage.h index 5a9eaecc60..1efaec27be 100644 --- a/library/talipot-core/include/talipot/GraphStorage.h +++ b/library/talipot-core/include/talipot/GraphStorage.h @@ -1,6 +1,6 @@ /** * - * Copyright (C) 2019-2021 The Talipot developers + * Copyright (C) 2019-2024 The Talipot developers * * Talipot is a fork of Tulip, created by David Auber * and the Tulip development Team from LaBRI, University of Bordeaux @@ -124,33 +124,7 @@ class GraphStorage { * @brief restore a state of the ids management */ void restoreIdsMemento(const GraphStorageIdsMemento *); - //======================================================= - /** - * @brief Return a Talipot iterator on edges of the graph - * @warning: The returned iterator should be deleted by the caller to prevent memory leaks - */ - Iterator *getEdges() const { - return edgeIds.getElts(); - } - //======================================================= - /** - * @brief Return a Talipot iterator on adjacent edges of the node n - * @warning: be careful that loops appear twice - * @warning: The returned iterator should be deleted by the caller to prevent memory leaks - */ - Iterator *getInOutEdges(const node n) const; - //======================================================= - /** - * @brief Return a Talipot iterator on out edges of the node n - * @warning: The returned iterator must be deleted by the caller to prevent memory leaks - */ - Iterator *getOutEdges(const node n) const; - //======================================================= - /** - * @brief Return a Talipot iterator on in edges of the node n - * @warning: The returned iterator must be deleted by the caller to prevent memory leaks - */ - Iterator *getInEdges(const node n) const; + //======================================================= /** * @brief Return if edges exist between two nodes @@ -165,24 +139,6 @@ class GraphStorage { std::vector getEdges(const node src, const node tgt, bool directed, const Graph *sg = nullptr) const; - //======================================================= - /** - * @brief Return a Talipot iterator on adjacent nodes of the node n - * @warning: The returned iterator must be deleted by the caller to prevent memory leaks - */ - Iterator *getInOutNodes(const node n) const; - //======================================================= - /** - * @brief Return a Talipot iterator on in nodes of the node n - * @warning: The returned iterator must be deleted by the caller to prevent memory leaks - */ - Iterator *getInNodes(const node n) const; - //======================================================= - /** - * @brief Return a Talipot iterator on out nodes of the node n - * @warning: The returned iterator must be deleted by the caller to prevent memory leaks - */ - Iterator *getOutNodes(const node n) const; //======================================================= /** * @brief Return the degree of a node diff --git a/library/talipot-core/src/GraphImpl.cpp b/library/talipot-core/src/GraphImpl.cpp index fd7f7be4b0..7ef56201ba 100644 --- a/library/talipot-core/src/GraphImpl.cpp +++ b/library/talipot-core/src/GraphImpl.cpp @@ -1,6 +1,6 @@ /** * - * Copyright (C) 2019-2023 The Talipot developers + * Copyright (C) 2019-2024 The Talipot developers * * Talipot is a fork of Tulip, created by David Auber * and the Tulip development Team from LaBRI, University of Bordeaux @@ -17,6 +17,7 @@ #include #include #include +#include using namespace std; using namespace tlp; @@ -232,35 +233,43 @@ void GraphImpl::delEdge(const edge e, bool) { } //---------------------------------------------------------------- Iterator *GraphImpl::getNodes() const { - return storage.getNodes(); + return stlIterator(storage.nodes()); } //---------------------------------------------------------------- Iterator *GraphImpl::getInNodes(const node n) const { - return storage.getInNodes(n); + return uniqueIterator(conversionIterator( + filterIterator(storage.incidence(n), [this, n](const edge e) { return target(e) == n; }), + [this](const edge e) { return source(e); })); } //---------------------------------------------------------------- Iterator *GraphImpl::getOutNodes(const node n) const { - return storage.getOutNodes(n); + return uniqueIterator(conversionIterator( + filterIterator(storage.incidence(n), [this, n](const edge e) { return source(e) == n; }), + [this](const edge e) { return target(e); })); } //---------------------------------------------------------------- Iterator *GraphImpl::getInOutNodes(const node n) const { - return storage.getInOutNodes(n); + return conversionIterator(storage.incidence(n), [this, n](const edge e) { + return source(e) == n ? target(e) : source(e); + }); } //---------------------------------------------------------------- Iterator *GraphImpl::getEdges() const { - return storage.getEdges(); + return stlIterator(storage.edges()); } //---------------------------------------------------------------- Iterator *GraphImpl::getInEdges(const node n) const { - return storage.getInEdges(n); + return uniqueIterator( + filterIterator(storage.incidence(n), [this, n](const edge e) { return target(e) == n; })); } //---------------------------------------------------------------- Iterator *GraphImpl::getOutEdges(const node n) const { - return storage.getOutEdges(n); + return uniqueIterator( + filterIterator(storage.incidence(n), [this, n](const edge e) { return source(e) == n; })); } //---------------------------------------------------------------- Iterator *GraphImpl::getInOutEdges(const node n) const { - return storage.getInOutEdges(n); + return stlIterator(storage.incidence(n)); } //---------------------------------------------------------------- std::vector GraphImpl::getEdges(const node src, const node tgt, bool directed) const { diff --git a/library/talipot-core/src/GraphStorage.cpp b/library/talipot-core/src/GraphStorage.cpp index 4b78520c82..41bd80703a 100644 --- a/library/talipot-core/src/GraphStorage.cpp +++ b/library/talipot-core/src/GraphStorage.cpp @@ -1,6 +1,6 @@ /** * - * Copyright (C) 2019-2023 The Talipot developers + * Copyright (C) 2019-2024 The Talipot developers * * Talipot is a fork of Tulip, created by David Auber * and the Tulip development Team from LaBRI, University of Bordeaux @@ -63,139 +63,6 @@ void GraphStorage::restoreIdsMemento(const GraphStorageIdsMemento *memento) { edgeIds = memento->edgeIds; } //======================================================= -// specific iterator classes used to implement -// the get*Nodes & get*Edges methods - -// define some values for further template specializations -// IO_IN => in nodes/edges -// IO_OUT => out nodes/edges -// IO_INOUT => inout nodes/edges -// IO_ prefix is needed on windows platform -// to avoid compilation failure -enum IO_TYPE { IO_IN = 0, IO_OUT = 1, IO_INOUT = 2 }; - -// define a template class to iterate on in or out edges of a given node -template -struct IOEdgeContainerIterator : public Iterator, - public MemoryPool> { - node n; - edge curEdge; - std::set loops; - const std::vector> &edges; - std::vector::iterator it, itEnd; - - void prepareNext() { - for (; it != itEnd; ++it) { - curEdge = *it; - const auto &[curEdgeSrc, curEdgeTgt] = edges[curEdge.id]; - // note that io_type value may only be IO_IN which is null - // or IO_OUT which is define to 1 - node curNode; - - if constexpr (io_type != IO_IN) { - curNode = curEdgeSrc; - } else { - curNode = curEdgeTgt; - } - - if (curNode != n) { - continue; - } - - if constexpr (io_type != IO_IN) { - curNode = curEdgeTgt; - } else { - curNode = curEdgeSrc; - } - - if (curNode == n) { - if (!loops.contains(curEdge)) { - loops.insert(curEdge); - ++it; - return; - } - } else { - ++it; - return; - } - } - - // set curEdge as invalid - curEdge = edge(); - } - - IOEdgeContainerIterator(node n, std::vector &v, - const std::vector> &edges) - : n(n), edges(edges), it(v.begin()), itEnd(v.end()) { - prepareNext(); - } - - ~IOEdgeContainerIterator() override = default; - - edge next() override { - // check hasNext() - assert(curEdge.isValid()); - // we are already pointing to the next - edge tmp = curEdge; - // anticipating the next iteration - prepareNext(); - return tmp; - } - - bool hasNext() override { - return (curEdge.isValid()); - } -}; - -// define a class to iterate on in or out nodes of a given node -template -struct IONodesIterator : public Iterator, public MemoryPool> { - node n; - const std::vector> &edges; - Iterator *it; - - IONodesIterator(node n, std::vector &nEdges, - const std::vector> &edges) - : n(n), edges(edges) { - if constexpr (io_type == IO_INOUT) { - it = stlIterator(nEdges); - } else { - it = new IOEdgeContainerIterator(n, nEdges, edges); - } - } - - ~IONodesIterator() override { - delete it; - } - - bool hasNext() override { - return (it->hasNext()); - } - - node next() override { - // check hasNext() - assert(it->hasNext()); - const auto &[src, tgt] = edges[it->next()]; - - // out nodes - if constexpr (io_type == IO_OUT) { - return tgt; - } - // in nodes - else if constexpr (io_type == IO_IN) { - return src; - } - // inout nodes - else { - return (src == n) ? tgt : src; - } - } -}; -//======================================================= -Iterator *GraphStorage::getInOutEdges(const node n) const { - return stlIterator(nodeData[n.id].edges); -} -//======================================================= std::vector GraphStorage::getEdges(const node src, const node tgt, bool directed, const Graph *sg) const { @@ -217,26 +84,6 @@ std::vector GraphStorage::getEdges(const node src, const node tgt, bool di return edges; } //======================================================= -Iterator *GraphStorage::getOutEdges(const node n) const { - return new IOEdgeContainerIterator(n, nodeData[n.id].edges, edgeEnds); -} -//======================================================= -Iterator *GraphStorage::getInEdges(const node n) const { - return new IOEdgeContainerIterator(n, nodeData[n.id].edges, edgeEnds); -} -//======================================================= -Iterator *GraphStorage::getInOutNodes(const node n) const { - return new IONodesIterator(n, nodeData[n.id].edges, edgeEnds); -} -//======================================================= -Iterator *GraphStorage::getInNodes(const node n) const { - return new IONodesIterator(n, nodeData[n.id].edges, edgeEnds); -} -//======================================================= -Iterator *GraphStorage::getOutNodes(const node n) const { - return new IONodesIterator(n, nodeData[n.id].edges, edgeEnds); -} -//======================================================= /** * @brief Reconnect the edge e to have the new given ends */ diff --git a/library/talipot-core/src/Observable.cpp b/library/talipot-core/src/Observable.cpp index 00554277be..da77949490 100644 --- a/library/talipot-core/src/Observable.cpp +++ b/library/talipot-core/src/Observable.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include using namespace std; @@ -35,7 +35,7 @@ TLP_DEFINE_GLOBAL_LOCK(ObservableGraphUpdate); //================================= static bool oGraphDestroyed = false; -class ObservationGraph : public GraphStorage { +class ObservationGraph : public GraphImpl { public: // a pointer to the object represented by a node tlp::NodeVectorProperty pointer; @@ -314,7 +314,7 @@ void Observable::addOnlooker(const Observable &obs, OBSERVABLEEDGETYPE type) con edge link; if (isBound() && obs.isBound()) { - auto edges = observationGraph.getEdges(obs._n, _n, false); + auto edges = observationGraph.getEdges(obs._n, _n, false, nullptr); if (!edges.empty()) { link = edges[0]; } @@ -531,7 +531,7 @@ void Observable::removeOnlooker(const Observable &obs, OBSERVABLEEDGETYPE type) throw ObservableException("removeOnlooker called on a deleted Observable"); } - auto edges = observationGraph.getEdges(obs._n, _n, false); + auto edges = observationGraph.getEdges(obs._n, _n, false, nullptr); edge link; if (!edges.empty()) { link = edges[0];