diff --git a/alib2data/src/graph/GraphToStringComposer.cpp b/alib2data/src/graph/GraphToStringComposer.cpp index f08d93d8dbcc5426f963c90c838a543dbea51490..b48f81a48a52b277c1ac6dda4b13c34f371e0728 100644 --- a/alib2data/src/graph/GraphToStringComposer.cpp +++ b/alib2data/src/graph/GraphToStringComposer.cpp @@ -51,29 +51,23 @@ void GraphToStringComposer::Visit(void *data, const UndirectedGraph &graph) cons composeEdgeValues(out, graph); } -void GraphToStringComposer::Visit(void *data, const Node &node) const +void GraphToStringComposer::composeNode(std::ostream& out, const Node &node) const { - std::ostream &out = *static_cast<std::ostream*>(data); - alib::stringApi<label::Label>::compose(out, node.getName()); } -void GraphToStringComposer::Visit(void *data, const DirectedEdge &edge) const +void GraphToStringComposer::composeEdge(std::ostream& out, const DirectedEdge &edge) const { - std::ostream &out = *static_cast<std::ostream*>(data); - - edge.getFromNode().Accept(static_cast<void*>(&out), *this); + composeNode(out, edge.getFromNode()); alib::stringApi<label::Label>::compose(out, edge.getName()); - edge.getToNode().Accept(static_cast<void*>(&out), *this); + composeNode(out, edge.getToNode()); } -void GraphToStringComposer::Visit(void *data, const UndirectedEdge &edge) const +void GraphToStringComposer::composeEdge(std::ostream& out, const UndirectedEdge &edge) const { - std::ostream &out = *static_cast<std::ostream*>(data); - - edge.getFirstNode().Accept(static_cast<void*>(&out), *this); + composeNode(out, edge.getFirstNode()); alib::stringApi<label::Label>::compose(out, edge.getName()); - edge.getSecondNode().Accept(static_cast<void*>(&out), *this); + composeNode(out, edge.getSecondNode()); } void GraphToStringComposer::composeRepresentation(std::ostream &out, REPRESENTATION representation) const @@ -91,7 +85,7 @@ void GraphToStringComposer::composeNodes(std::ostream &out, const std::set<Node> } else { out << ","; } - node.Accept(static_cast<void*>(&out), *this); + composeNode(out, node); } } @@ -105,7 +99,7 @@ void GraphToStringComposer::composeDirectedEdges(std::ostream &out, const std::s } else { out << ","; } - edge.Accept(static_cast<void*>(&out), *this); + composeEdge(out, edge); } } @@ -119,7 +113,7 @@ void GraphToStringComposer::composeUndirectedEdges(std::ostream &out, const std: } else { out << ","; } - edge.Accept(static_cast<void*>(&out), *this); + composeEdge(out, edge); } } @@ -134,7 +128,7 @@ void GraphToStringComposer::composeNodeValues(std::ostream &out, const T &graph) } else { out << ","; } - i.first.Accept(static_cast<void*>(&out), *this); + composeNode(out, i.first); out << "=" << i.second; } } @@ -150,7 +144,7 @@ void GraphToStringComposer::composeEdgeValues(std::ostream &out, const T &graph) } else { out << ","; } - i.first.Accept(static_cast<void*>(&out), *this); + composeEdge(out, i.first); out << "=" << i.second; } } diff --git a/alib2data/src/graph/GraphToStringComposer.h b/alib2data/src/graph/GraphToStringComposer.h index 0a65d115f93e9d7aeb9db7f673df353a58b50f13..67a8056b1754487fc522098197524f937831f9d8 100644 --- a/alib2data/src/graph/GraphToStringComposer.h +++ b/alib2data/src/graph/GraphToStringComposer.h @@ -12,11 +12,13 @@ #include "Graph.h" #include "GraphRepresentation.h" -#include "common/GraphElement.h" +#include "common/Node.h" +#include "directed/DirectedEdge.h" +#include "undirected/UndirectedEdge.h" namespace graph { -class GraphToStringComposer : public VisitableGraphBase::const_visitor_type, GraphElement::const_visitor_type +class GraphToStringComposer : public VisitableGraphBase::const_visitor_type { public: GraphToStringComposer() {} @@ -27,9 +29,9 @@ private: void Visit(void *data, const DirectedGraph &graph) const; void Visit(void *data, const UndirectedGraph &graph) const; - void Visit(void *data, const Node &node) const; - void Visit(void *data, const DirectedEdge &edge) const; - void Visit(void *data, const UndirectedEdge &edge) const; + void composeNode(std::ostream& out, const Node &node) const; + void composeEdge(std::ostream& out, const DirectedEdge &edge) const; + void composeEdge(std::ostream& out, const UndirectedEdge &edge) const; void composeRepresentation(std::ostream &out, REPRESENTATION representation) const; void composeNodes(std::ostream &out, const std::set<Node> &nodes) const; diff --git a/alib2data/src/graph/GraphToXMLComposer.cpp b/alib2data/src/graph/GraphToXMLComposer.cpp index 3dc0210634ec5732e114524b1b93d2750e40d257..6cc9087c409fc2b679a96a65b05cc9d565f440e0 100644 --- a/alib2data/src/graph/GraphToXMLComposer.cpp +++ b/alib2data/src/graph/GraphToXMLComposer.cpp @@ -14,48 +14,42 @@ namespace graph { -void GraphToXMLComposer::Visit(void *data, const Node &node) const +void GraphToXMLComposer::composeNode(std::deque<sax::Token>& out, const Node &node) const { - std::deque<sax::Token> &out = *static_cast<std::deque<sax::Token>*>(data); - out.emplace_back("node", sax::Token::TokenType::START_ELEMENT); alib::xmlApi<label::Label>::compose(out, node.getName()); out.emplace_back("node", sax::Token::TokenType::END_ELEMENT); } -void GraphToXMLComposer::Visit(void *data, const DirectedEdge &edge) const +void GraphToXMLComposer::composeEdge(std::deque<sax::Token>& out, const DirectedEdge &edge) const { - std::deque<sax::Token> &out = *static_cast<std::deque<sax::Token>*>(data); - out.emplace_back("edge", sax::Token::TokenType::START_ELEMENT); out.emplace_back("from", sax::Token::TokenType::START_ELEMENT); - edge.getFromNode().Accept(static_cast<void*>(&out), *this); + composeNode(out, edge.getFromNode()); out.emplace_back("from", sax::Token::TokenType::END_ELEMENT); alib::xmlApi<label::Label>::compose(out, edge.getName()); out.emplace_back("to", sax::Token::TokenType::START_ELEMENT); - edge.getToNode().Accept(static_cast<void*>(&out), *this); + composeNode(out, edge.getToNode()); out.emplace_back("to", sax::Token::TokenType::END_ELEMENT); out.emplace_back("edge", sax::Token::TokenType::END_ELEMENT); } -void GraphToXMLComposer::Visit(void *data, const UndirectedEdge &edge) const +void GraphToXMLComposer::composeEdge(std::deque<sax::Token>& out, const UndirectedEdge &edge) const { - std::deque<sax::Token> &out = *static_cast<std::deque<sax::Token>*>(data); - out.emplace_back("edge", sax::Token::TokenType::START_ELEMENT); out.emplace_back("first", sax::Token::TokenType::START_ELEMENT); - edge.getFirstNode().Accept(static_cast<void*>(&out), *this); + composeNode(out, edge.getFirstNode()); out.emplace_back("first", sax::Token::TokenType::END_ELEMENT); alib::xmlApi<label::Label>::compose(out, edge.getName()); out.emplace_back("second", sax::Token::TokenType::START_ELEMENT); - edge.getSecondNode().Accept(static_cast<void*>(&out), *this); + composeNode(out, edge.getSecondNode()); out.emplace_back("second", sax::Token::TokenType::END_ELEMENT); out.emplace_back("edge", sax::Token::TokenType::END_ELEMENT); @@ -108,7 +102,7 @@ void GraphToXMLComposer::composeNodes(std::deque<sax::Token> &out, const std::se { out.emplace_back("nodes", sax::Token::TokenType::START_ELEMENT); for (const Node &node : nodes) { - node.Accept(static_cast<void*>(&out), *this); + composeNode(out, node); } out.emplace_back("nodes", sax::Token::TokenType::END_ELEMENT); } @@ -118,7 +112,7 @@ void GraphToXMLComposer::composeEdges(std::deque<sax::Token> &out, const std::se { out.emplace_back("edges", sax::Token::TokenType::START_ELEMENT); for (const auto &edge : edges) { - edge.Accept(static_cast<void*>(&out), *this); + composeEdge(out, edge); } out.emplace_back("edges", sax::Token::TokenType::END_ELEMENT); } @@ -128,7 +122,7 @@ void GraphToXMLComposer::composeNodeValues(std::deque<sax::Token> &out, const T { out.emplace_back("nodevalues", sax::Token::TokenType::START_ELEMENT); for (auto i : graph.nodeValues) { - i.first.Accept(static_cast<void*>(&out), *this); + composeNode(out, i.first); out.emplace_back("value", sax::Token::TokenType::START_ELEMENT); out.emplace_back(std::itos(i.second), sax::Token::TokenType::CHARACTER); out.emplace_back("value", sax::Token::TokenType::END_ELEMENT); @@ -141,7 +135,7 @@ void GraphToXMLComposer::composeEdgeValues(std::deque<sax::Token> &out, const T { out.emplace_back("edgevalues", sax::Token::TokenType::START_ELEMENT); for (auto &i : graph.edgeValues) { - i.first.Accept(static_cast<void*>(&out), *this); + composeEdge(out, i.first); out.emplace_back("value", sax::Token::TokenType::START_ELEMENT); out.emplace_back(std::itos(i.second), sax::Token::TokenType::CHARACTER); out.emplace_back("value", sax::Token::TokenType::END_ELEMENT); diff --git a/alib2data/src/graph/GraphToXMLComposer.h b/alib2data/src/graph/GraphToXMLComposer.h index 1e7c1b136ea0445247f1feaf7ad55d2b22937743..ca2a72d2da57a210a2984a562fd3ff9f82061b38 100644 --- a/alib2data/src/graph/GraphToXMLComposer.h +++ b/alib2data/src/graph/GraphToXMLComposer.h @@ -12,7 +12,6 @@ #include <deque> #include "GraphFeatures.h" -#include "common/GraphElement.h" #include "../sax/Token.h" namespace alib { @@ -25,15 +24,15 @@ struct xmlApi; namespace graph { // This class contains methods to print XML representation of graph to the output stream. -class GraphToXMLComposer : public GraphElement::const_visitor_type +class GraphToXMLComposer { public: GraphToXMLComposer() {} private: - void Visit(void *data, const Node &node) const; - void Visit(void *data, const DirectedEdge &edge) const; - void Visit(void *data, const UndirectedEdge &edge) const; + void composeNode(std::deque<sax::Token>& out, const Node &node) const; + void composeEdge(std::deque<sax::Token>& out, const DirectedEdge &edge) const; + void composeEdge(std::deque<sax::Token>& out, const UndirectedEdge &edge) const; void compose(std::deque<sax::Token> &out, const GraphBase &graph) const; void compose(std::deque<sax::Token> &out, const Graph &graph) const; diff --git a/alib2data/src/graph/common/GraphElement.cpp b/alib2data/src/graph/common/GraphElement.cpp deleted file mode 100644 index 6f8e7b2387cc98f219fe75b1188f9081c9d89e76..0000000000000000000000000000000000000000 --- a/alib2data/src/graph/common/GraphElement.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * GraphFeatures.h - * - * Created on: Jun 19, 2014 - * Author: David Rosca - */ - -#include "GraphElement.h" - -namespace graph { - -GraphElement::GraphElement() -{ -} - -} // namespace graph diff --git a/alib2data/src/graph/common/GraphElement.h b/alib2data/src/graph/common/GraphElement.h deleted file mode 100644 index 45144351f63ab5a02c140ed56baa52491f119467..0000000000000000000000000000000000000000 --- a/alib2data/src/graph/common/GraphElement.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * GraphFeatures.h - * - * Created on: Jun 19, 2014 - * Author: David Rosca - */ - -#ifndef GRAPH_ELEMENT_H_ -#define GRAPH_ELEMENT_H_ - -// for std::hash -#include <string> -#include <functional> - -#include "../../common/visitor.hpp" -#include "../../object/Object.h" - -namespace graph { - -class GraphElement; - -class Node; -class DirectedEdge; -class UndirectedEdge; - -// Abstract class representing element in the graph. Can be node or edge. -typedef std::acceptor_base<GraphElement, Node, DirectedEdge, UndirectedEdge> VisitableGraphElement; - -class GraphElement : public alib::base<GraphElement, Node, DirectedEdge, UndirectedEdge>, public VisitableGraphElement -{ -public: - explicit GraphElement(); -}; - -} // namespace graph - -#endif // GRAPH_ELEMENT_H_ diff --git a/alib2data/src/graph/common/Node.cpp b/alib2data/src/graph/common/Node.cpp index b529fdff68ace78a26e90c18671a309e525aa06e..90efd477ec0e0b0e862d9d97da4030c23e7a1c6b 100644 --- a/alib2data/src/graph/common/Node.cpp +++ b/alib2data/src/graph/common/Node.cpp @@ -42,45 +42,31 @@ Node::Node(const std::string &name) { } -Node::Node(const Node &other) - : name(other.name) +Node *Node::clone() const { + return new Node(*this); } -Node::Node(Node &&other) noexcept - : name(std::move(other.name)) +Node *Node::plunder() && { + return new Node(std::move(*this)); } -Node &Node::operator=(const Node &other) +const label::Label &Node::getName() const { - if (this == &other) { - return *this; - } - - *this = Node(other); - return *this; + return name; } -Node &Node::operator=(Node &&other) noexcept -{ - std::swap(name, other.name); - return *this; +bool Node::operator < (const Node& other) const { + return this->compare(other) < 0; } -GraphElement *Node::clone() const -{ - return new Node(*this); +bool Node::operator == (const Node& other) const { + return this->compare(other) == 0; } -GraphElement *Node::plunder() && -{ - return new Node(std::move(*this)); -} - -const label::Label &Node::getName() const -{ - return name; +bool Node::operator != (const Node& other) const { + return !(*this == other); } int Node::compare(const Node &other) const diff --git a/alib2data/src/graph/common/Node.h b/alib2data/src/graph/common/Node.h index fd5d4f444e9adeba14d3b57c01a9942f780609ba..c9a5ae748e59da6fd2485917c8de4ade2719e067 100644 --- a/alib2data/src/graph/common/Node.h +++ b/alib2data/src/graph/common/Node.h @@ -2,12 +2,11 @@ #define NODE_H_ #include "Macros.h" -#include "GraphElement.h" #include "../../label/Label.h" namespace graph { -class Node : public std::acceptor<Node, GraphElement, GraphElement> +class Node { public: explicit Node(); @@ -17,31 +16,19 @@ public: explicit Node(char character); explicit Node(const std::string &name); - Node(const Node &other); - Node(Node &&other) noexcept; - Node &operator=(const Node &other); - Node &operator=(Node &&other) noexcept; - - GraphElement *clone() const override; - GraphElement *plunder() && override; + Node *clone() const; + Node *plunder() &&; const label::Label &getName() const; - int compare(const GraphElement &other) const override - { - return -other.compare(*this); - } - - int compare(const Node &other) const override; - - void operator>>(std::ostream &out) const override; + bool operator < (const Node& other) const; + bool operator == (const Node& other) const; + bool operator != (const Node& other) const; + int compare(const Node &other) const; - explicit operator std::string() const override; + void operator>>(std::ostream &out) const; - int selfTypeId() const override - { - return typeId<Node>(); - } + explicit operator std::string() const; friend std::ostream &operator<<(std::ostream &out, const Node &node); diff --git a/alib2data/src/graph/directed/DirectedEdge.cpp b/alib2data/src/graph/directed/DirectedEdge.cpp index cf5e89a4341e8d7b86c5c62dba0d4030b0d91b52..027830cb9794ae45e7abd4fa476a65d6287a0687 100644 --- a/alib2data/src/graph/directed/DirectedEdge.cpp +++ b/alib2data/src/graph/directed/DirectedEdge.cpp @@ -86,44 +86,12 @@ DirectedEdge::DirectedEdge(Node &&from, Node &&to, const std::string &name) { } -DirectedEdge::DirectedEdge(const DirectedEdge &other) - : from(other.from) - , to(other.to) - , name(other.name) -{ -} - -DirectedEdge::DirectedEdge(DirectedEdge &&other) noexcept - : from(std::move(other.from)) - , to(std::move(other.to)) - , name(std::move(other.name)) -{ -} - -DirectedEdge &DirectedEdge::operator=(const DirectedEdge &other) -{ - if (this == &other) { - return *this; - } - - *this = DirectedEdge(other); - return *this; -} - -DirectedEdge &DirectedEdge::operator=(DirectedEdge &&other) noexcept -{ - std::swap(from, other.from); - std::swap(to, other.to); - std::swap(name, other.name); - return *this; -} - -GraphElement *DirectedEdge::clone() const +DirectedEdge *DirectedEdge::clone() const { return new DirectedEdge(*this); } -GraphElement *DirectedEdge::plunder() && +DirectedEdge *DirectedEdge::plunder() && { return new DirectedEdge(std::move(*this)); } @@ -143,16 +111,25 @@ const label::Label &DirectedEdge::getName() const return name; } +bool DirectedEdge::operator < (const DirectedEdge& other) const { + return this->compare(other) < 0; +} + +bool DirectedEdge::operator == (const DirectedEdge& other) const { + return this->compare(other) == 0; +} + +bool DirectedEdge::operator != (const DirectedEdge& other) const { + return !(*this == other); +} + int DirectedEdge::compare(const DirectedEdge &other) const { int res = name.getData().compare(other.name.getData()); - if (res == 0) { - res = from.compare(other.from); - if (res == 0) { - res = to.compare(other.to); - } - } - return res; + if (res != 0) return res; + res = from.compare(other.from); + if (res != 0) return res; + return to.compare(other.to); } void DirectedEdge::operator>>(std::ostream &out) const diff --git a/alib2data/src/graph/directed/DirectedEdge.h b/alib2data/src/graph/directed/DirectedEdge.h index 1679a9b0f2dcff9caabc3aeb959b1972a51a90ba..9276ad1a0b079830add2c94ad11948a8a30e4c02 100644 --- a/alib2data/src/graph/directed/DirectedEdge.h +++ b/alib2data/src/graph/directed/DirectedEdge.h @@ -8,14 +8,12 @@ #ifndef DIRECTED_EDGE_H_ #define DIRECTED_EDGE_H_ -#include "../common/GraphElement.h" #include "../common/Macros.h" #include "../common/Node.h" namespace graph { -class DirectedEdge : public std::acceptor<DirectedEdge, GraphElement, GraphElement> -{ +class DirectedEdge { public: explicit DirectedEdge(); explicit DirectedEdge(const Node &from, const Node &to); @@ -29,33 +27,21 @@ public: explicit DirectedEdge(const Node &from, const Node &to, const std::string &name); explicit DirectedEdge(Node &&from, Node &&to, const std::string &name); - DirectedEdge(const DirectedEdge &other); - DirectedEdge(DirectedEdge &&other) noexcept; - DirectedEdge &operator=(const DirectedEdge &other); - DirectedEdge &operator=(DirectedEdge &&other) noexcept; - - GraphElement *clone() const override; - GraphElement *plunder() && override; + DirectedEdge *clone() const; + DirectedEdge *plunder() &&; const Node &getFromNode() const; const Node &getToNode() const; const label::Label &getName() const; - int compare(const GraphElement &other) const override - { - return -other.compare(*this); - } - - int compare(const DirectedEdge &other) const override; - - void operator>>(std::ostream &out) const override; + bool operator < (const DirectedEdge& other) const; + bool operator == (const DirectedEdge& other) const; + bool operator != (const DirectedEdge& other) const; + int compare(const DirectedEdge &other) const; - explicit operator std::string() const override; + void operator>>(std::ostream &out) const; - int selfTypeId() const override - { - return typeId(*this); - } + explicit operator std::string() const; friend std::ostream &operator<<(std::ostream &out, const DirectedEdge &node); diff --git a/alib2data/src/graph/undirected/UndirectedEdge.cpp b/alib2data/src/graph/undirected/UndirectedEdge.cpp index 5b7db96e511c4538771f349cdc23bf146315762c..3ea39761111b3da2697d41c6171fdd92a0770348 100644 --- a/alib2data/src/graph/undirected/UndirectedEdge.cpp +++ b/alib2data/src/graph/undirected/UndirectedEdge.cpp @@ -86,44 +86,12 @@ UndirectedEdge::UndirectedEdge(Node &&first, Node &&second, const std::string &n { } -UndirectedEdge::UndirectedEdge(const UndirectedEdge &other) - : first(other.first) - , second(other.second) - , name(other.name) -{ -} - -UndirectedEdge::UndirectedEdge(UndirectedEdge &&other) noexcept - : first(std::move(other.first)) - , second(std::move(other.second)) - , name(std::move(other.name)) -{ -} - -UndirectedEdge &UndirectedEdge::operator=(const UndirectedEdge &other) -{ - if (this == &other) { - return *this; - } - - *this = UndirectedEdge(other); - return *this; -} - -UndirectedEdge &UndirectedEdge::operator=(UndirectedEdge &&other) noexcept -{ - std::swap(first, other.first); - std::swap(second, other.second); - std::swap(name, other.name); - return *this; -} - -GraphElement *UndirectedEdge::clone() const +UndirectedEdge *UndirectedEdge::clone() const { return new UndirectedEdge(*this); } -GraphElement *UndirectedEdge::plunder() && +UndirectedEdge *UndirectedEdge::plunder() && { return new UndirectedEdge(std::move(*this)); } @@ -143,22 +111,28 @@ const label::Label &UndirectedEdge::getName() const return name; } +bool UndirectedEdge::operator < (const UndirectedEdge& other) const { + return this->compare(other) < 0; +} + +bool UndirectedEdge::operator == (const UndirectedEdge& other) const { + return this->compare(other) == 0; +} + +bool UndirectedEdge::operator != (const UndirectedEdge& other) const { + return !(*this == other); +} + int UndirectedEdge::compare(const UndirectedEdge &other) const { auto a = sortedNodes(); auto b = other.sortedNodes(); int res = name.getData().compare(other.name.getData()); - if (res == 0) { - if (a.first.compare(b.second) == 0 && a.second.compare(b.first) == 0) { - return 0; - } - res = a.first.compare(b.first); - if (res == 0) { - res = a.second.compare(b.second); - } - } - return res; + if (res != 0) return res; + res = a.first.compare(b.first); + if (res != 0) return res; + return a.second.compare(b.second); } void UndirectedEdge::operator>>(std::ostream &out) const diff --git a/alib2data/src/graph/undirected/UndirectedEdge.h b/alib2data/src/graph/undirected/UndirectedEdge.h index 3a751cea1ff161e4b1144fc458b86fada6ebc49f..c92aebf5592130ec9d29e05c3ad6af7c3dd85bdf 100644 --- a/alib2data/src/graph/undirected/UndirectedEdge.h +++ b/alib2data/src/graph/undirected/UndirectedEdge.h @@ -8,14 +8,12 @@ #ifndef UNDIRECTED_EDGE_H_ #define UNDIRECTED_EDGE_H_ -#include "../common/GraphElement.h" #include "../common/Macros.h" #include "../common/Node.h" namespace graph { -class UndirectedEdge : public std::acceptor<UndirectedEdge, GraphElement, GraphElement> -{ +class UndirectedEdge { public: explicit UndirectedEdge(); explicit UndirectedEdge(const Node &first, const Node &second); @@ -29,33 +27,21 @@ public: explicit UndirectedEdge(const Node &first, const Node &second, const std::string &name); explicit UndirectedEdge(Node &&first, Node &&second, const std::string &name); - UndirectedEdge(const UndirectedEdge &other); - UndirectedEdge(UndirectedEdge &&other) noexcept; - UndirectedEdge &operator=(const UndirectedEdge &other); - UndirectedEdge &operator=(UndirectedEdge &&other) noexcept; - - GraphElement *clone() const override; - GraphElement *plunder() && override; + UndirectedEdge *clone() const; + UndirectedEdge *plunder() &&; const Node &getFirstNode() const; const Node &getSecondNode() const; const label::Label &getName() const; - int compare(const GraphElement &other) const override - { - return -other.compare(*this); - } - - int compare(const UndirectedEdge &other) const override; - - void operator>>(std::ostream &out) const override; + bool operator < (const UndirectedEdge& other) const; + bool operator == (const UndirectedEdge& other) const; + bool operator != (const UndirectedEdge& other) const; + int compare(const UndirectedEdge &other) const; - explicit operator std::string() const override; + void operator>>(std::ostream &out) const; - int selfTypeId() const override - { - return typeId(*this); - } + explicit operator std::string() const; friend std::ostream &operator<<(std::ostream &out, const UndirectedEdge &node);