I have a polymorphic tree and I'm trying to do add functionality like comparing two nodes without using RTTI like dynamic_cast The approach I'm taking is the visitor pattern.
The issue I'm having is that the visitor pattern isn't letting me operate on any parameters or getting return types out of the functions.
For example if I wanted to write a visitor that compares two nodes
class AbstractDispatch{
public:
virtual void visit(NodeFoo &operand) = 0;
virtual void visit(NodeBar &operand) = 0;
//...for all types.
};
class CompareVisitor{
public:
void visit(NodeFoo &operand) override;
//...
};
class SetVisitor{
public:
void visit(NodeFoo &operand) override;
//...
};
void CompareVisitor::visit(NodeFoo &operand){
//compare operand to what?
//get result of comparison how?
}
void SetVisitor::visit(NodeFoo &operand){
//set operand to what?
}
My current idea is to add other functions and members to the visitor classes. This would let me do something like this:
Base *object = new NodeFoo();
CompareVisitor compare;
compare.set_parameters(NodeFoo(/* */));
object->accept(compare);
bool result = compare.get_result();
I could set the parameters of the compare visitor and traverse the tree with it checking for the nodes and doing other such operations in this fashion.
Another solution would be to store node-type information in the node and do a get_type() check for safe-casting.
dynamic_cast is slow but if the node-type hierarchy is extremely simple could it be faster? Are there better design patterns for doing something like this?
You could write a visitor that compares the current node with the previous one.
class Node{...}
class NodeFoo : public Node {...}
class NodeBar : public Node {...}
class visitor{
public:
void visit( const NodeFoo& node ){
//First node: store relevant comparison information to private variables
if( foo == nullptr ){
foo = &node;
}
//Other nodes: Compare to the stored data and store comparison result
else {
...
}
}
void visit( const NodeBar& node ){
...
}
bool result() const{ return result; };
private:
bool result = false;
//variables for comparison, could be this simple but also a variant type
// or plain Node*
NodeFoo* foo = nullptr;
NodeBar* bar = nullptr;
}
You would use it like
Node node1;
Node node2;
Visitor v;
node1.accept( v );
node2.accept( v );
v.result();
Sure, this is a very basic implementation, you could use a plain Node* to store the first node, if you presume that alle visited nodes have the same type. You could also use a variant type or store the type as string... ( you know the type by the executed visit function )
If the Node lifetime is not sure, you can store some Node depending data, that you need for the comparisson instead of a pointer to the node.... there are hundreds of possibilities, this is just a small sketch of the basic framework
It can be done with double visit ( = two virtual calls) if you allow CompareVisitor to have a state. I don't think there's a way around that given your visitor API.
#include <iostream>
#include <string>
#include <vector>
#include <memory>
struct FooNode;
struct BarNode;
struct Visitor
{
virtual void visit(FooNode&)=0;
virtual void visit(BarNode&)=0;
};
struct Node{
virtual void accept(Visitor& v) = 0;
};
struct FooNode: public Node{
virtual void accept(Visitor& v) override { v.visit(*this);}
const char* print(){return "FooNode";}
};
struct BarNode: public Node{
virtual void accept(Visitor& v) override { v.visit(*this);}
const char* print(){return "BarNode";}
};
using ret_type=std::string;
//Feel free to specialize or overload
//Or create comparator class that allows partial specializations
template<typename Left, typename Right>
ret_type compare(Left &left, Right& right){
return std::string(left.print()) + "<=>" + right.print() + '\n';
}
//Compares (visited) and (rightNode)
class RightCompareVisitor : public Visitor {
public:
RightCompareVisitor(Node& right):rightNode(right){}
void visit(FooNode &left) override
{
visitRightNode(left);
}
void visit(BarNode &left) override
{
visitRightNode(left);
}
ret_type getRes() { return std::move(result);}
private:
template<typename Left>
void visitRightNode(Left& left){
struct CompareVisitor: Visitor
{
ret_type& result;
Left& left;
CompareVisitor(ret_type& result, Left& left):result(result), left(left){}
void visit(FooNode &right) override final{
result = compare(left, right);
}
void visit(BarNode &right) override final{
result = compare(left, right);
}
};
CompareVisitor v(result, left);
rightNode.accept(v);
}
ret_type result;
Node& rightNode;
};
//If you add this then you can always just use 'compare' to compare any two
//nodes.
template<>
ret_type compare<Node,Node>(Node& left, Node& right){
RightCompareVisitor rC{right};
left.accept(rC);
return rC.getRes();
}
int main()
{
std::vector<std::unique_ptr<Node>> nodes;
nodes.emplace_back(std::make_unique<FooNode>());
nodes.emplace_back(std::make_unique<BarNode>());
nodes.emplace_back(std::make_unique<FooNode>());
for(auto&& left : nodes)
for(auto&& right: nodes)
std::cout<<compare(*left,*right);
}
Add consts where you want. If you want RightCompareVisitor to be reused then use pointers for nodes.
Output:
FooNode<=>FooNode
FooNode<=>BarNode
FooNode<=>FooNode
BarNode<=>FooNode
BarNode<=>BarNode
BarNode<=>FooNode
FooNode<=>FooNode
FooNode<=>BarNode
FooNode<=>FooNode
Related
I was looking at some tutorials on how to make an unordered_set for a class/struct. I found this easy-to-understand code (as a Java developer) which does the trick:
#include <iostream>
#include <unordered_set>
using namespace std;
struct Node{
int val;
bool operator==(const Node& n) const{
return (this->val == n.val);
}
};
class HashFunction{
public:
size_t operator()(const Node& n) const{
return n.val;
}
};
int main(){
Node n1 = { 1 }, n2 = { 2 },
n3 = { 3 }, n4 = { 4 };
unordered_set<Node, HashFunction> us;
us.insert(n1);
us.insert(n2);
us.insert(n3);
us.insert(n4);
for (auto node : us){
cout << node.val << " ";
}
cout << endl;
return 0;
}
I was wondering if we can make the struct Node a class, make the int val a private field and add unordered_set<Node, HashFunction> neighbours as a field to the Node class.
If not, what is a good practice to keep classes/structs well-encapsulated and have sets/maps fields for classes?
There are a few questions here, so I'll try to answer them in order:
can make the struct Node a class
Yes, struct and class only differ in their default permissions (in struct things are public unless stated otherwise, in class they are private)
So this is identical code to what you wrote:
class Node{
public:
int val;
bool operator==(const Node& n) const{
return (this->val == n.val);
}
};
make the int val a private field and add unordered_set<Node, HashFunction> neighbours as a field to the Node class
Yes, you can. The easiest way I can think of to make that transition from the code that you wrote is to make HashFunction be a sub-class of Node.
For example:
class Node {
class HashFunction {
public:
size_t operator()(const Node& n) const{
return n.val;
}
};
public:
Node(int _val) : val(_val) {}
bool operator==(const Node& n) const{
return (this->val == n.val);
}
// More methods here
private:
int val;
unordered_set<Node, HashFunction> neighbours;
};
If not, what is a good practice to keep classes/structs well-encapsulated and have sets/maps fields for classes?
I guess that is somewhat of a mute question in this case - but the general answer is to expose only the minimum required interface. For example, right now HushFunction is aware of the internal content of Node. in order to increase encapsulation we could have added a hush method to Node and have HushFunction invoke that method. This way if the content of Node changes, nothing outside of Node needs to be aware of it.
There are no similar concept of (Java) Collection in C++.
I can understand the reason, but I want to know whether there is any way to fake it elegantly.
Example
I have implemented many custom Collections.
They all have Iterator that works correctly, similar to std::vector, std::unordered_set, etc.
They are MyArray<T> , MyBinaryTree<T> and MySet<T>.
Here I will show a working code that show the location I want to fake it.
Let's say that I have 2 levels of program : library and user.
It does only one thing - User commands Library to eat all Orange*s in a bucket.
Library.h
class Library{
public: static void eatAll(const MyArray<Orange*>& bucket);
};
Library.cpp
#include "Orange.h"
void Library::eatAll(const MyArray<Orange*>& bucket){
for(auto orange:bucket){
orange->eaten();
}
}
User.h
MyArray<Orange*> bucket;
Library::eatAll(bucket);
It is OK.
Now, I want Library::eatAll to also support MyBinaryTree<Orange*>, I have some not-so-desirable approaches as below.
My poor solution
1. Java way
Make MyBinaryTree<T> and MyArray<Orange*> (and their iterator) inherit from a new class Collection<T> (and CollectionIterator<T>).
change signature to Library::eatAll(const Collection<T>&)
Disadvantage : performance penalty from "virtual" of some functions in Collection<T>.
2. Template v1
//Library.h
template<class T> void eatAll(const T&t ){
for(auto orange : t){
orange->eaten();
}
}
make eatAll a template function
Disadvantage : The implementation of the eatAll must be in header.
I have to #include orange.h in Library.h.
Sometimes, I really want to just forward declaration.
3. Template v2
//Library.h
template<class T> void eatAll(const T&t ){
for(auto orange : t){
eatAnOrange(orange)
}
}
private: void eatAnOrange(Orange* orange){
//below implementation is inside Library.cpp
orange->eaten();
}
create a middle-man function eatAnOrange
Disadvantage :
Code is less readable, not concise, cause a little maintainability problem.
If there are a lot of other functions e.g. squeezeAll(), I probably have to create a lot of middle-man functions, e.g. squeezeAnOrange().
4. Operator=()
Create converter among the 3 collection classes via implicit constructor.
Disadvantage : Suffer performance from creating a new instance of collection.
//Here is what it will do, internally (roughly speaking)
MyBinaryTree<Orange*> bucket;
Library::eatAll(MyArray<Orange*>(bucket));
I believe my solutions are inelegant.
Are there any solutions that don't suffer the mentioned disadvantage?
Edit:
Both of current answers are elegant than my approaches (thank!), but still has disadvantage :-
- Oliv's requires #include "orange.h" in User.h.
- Richard Hodges's has virtual function calling.
In C++, collections are traversed using the iterator design pattern. The entire STL is designed around this concept. It may fit your needs:
You could define eatAll as a function that accept two iterators:
template<class Iterator,class Sentinel>
void eatAll(Iterator it, Sentinel s){
for (;it!=s;++it)
it->eaten();
}
Or the range like algorithm interface:
template<class Range>
void eatAll(Range& r){
for (auto& v:r)
v.eaten();
}
You will have to define you binary tree as a range (it must implement begin() and end()). Hopefully trees are kind of graph that can be linearised. All the smart work will then go in the iterator implementation!
If you want it truly polymorphic, then we have to deal with 2 things:
the actual type of the container
The fact that the result of dereferencing a map is a pair containing key and value references.
My view is that the answer to this is not to derive from containers, which is limiting, but to create a polymorphic "value iterator", which models all iterators and extracts their values correctly.
Then we can write code like this:
int main()
{
std::vector<Orange> vo {
Orange(), Orange()
};
std::map<int, Orange> mio {
{ 1, Orange() },
{ 2, Orange() },
{ 3, Orange() }
};
std::cout << "vector:\n";
auto first = makePolymorphicValueIterator(vo.begin());
auto last = makePolymorphicValueIterator(vo.end());
do_orange_things(first, last);
std::cout << "\nmap:\n";
first = makePolymorphicValueIterator(mio.begin());
last = makePolymorphicValueIterator(mio.end());
do_orange_things(first, last);
}
To get this:
vector:
Orange
Orange
map:
Orange
Orange
Orange
Here's a minimal, complete implementation:
#include <typeinfo>
#include <memory>
#include <iostream>
#include <vector>
#include <map>
#include <iterator>
// define an orange
struct Orange {
};
// a meta-function to get the type of the value of some iterated value_type
template<class ValueType> struct type_of_value
{
using type = ValueType;
};
// specialise it for maps and unordered maps
template<class K, class V> struct type_of_value<std::pair<K, V>>
{
using type = V;
};
template<class ValueType> using type_of_value_t = typename type_of_value<ValueType>::type;
// function to extract a value from an instance of a value_type
template<class ValueType> struct value_extractor
{
template<class V>
auto& operator()(V&& v) const {
return v;
}
};
// specialised for maps
template<class K, class V> struct value_extractor<std::pair<K, V>>
{
template<class Arg>
auto& operator()(Arg&& v) const {
return std::get<1>(v);
}
};
template<class Iter>
auto extract_value(Iter const& iter) -> auto&
{
using value_type = typename std::iterator_traits<Iter>::value_type;
auto e = value_extractor<value_type> {};
return e(*iter);
}
// a polymorphic (forward only at the moment) iterator
// which delivers the value (in the case of maps) or the element (every other container)
template<class ValueType>
struct PolymorphicValueIterator {
using value_type = type_of_value_t<ValueType>;
private:
struct iterator_details {
std::type_info const &type;
void *address;
};
struct concept {
virtual std::unique_ptr<concept> clone() const = 0;
virtual value_type& invoke_deref() const = 0;
virtual void invoke_next(std::size_t distance = 1) = 0;
virtual iterator_details get_details() = 0;
virtual bool is_equal(const iterator_details &other) const = 0;
virtual ~concept() = default;
};
template<class Iter>
struct model final : concept {
model(Iter iter)
: iter_(iter)
{}
std::unique_ptr<concept> clone() const override
{
return std::make_unique<model>(iter_);
}
virtual value_type& invoke_deref() const override {
return extract_value(iter_);
}
void invoke_next(std::size_t distance = 1) override
{
iter_ = std::next(iter_, distance);
}
iterator_details get_details() override {
return {
typeid(Iter),
std::addressof(iter_)
};
}
bool is_equal(const iterator_details &other) const override {
if (typeid(Iter) != other.type) {
return false;
}
auto pother = reinterpret_cast<Iter const*>(other.address);
Iter const& iother = *pother;
return iter_ == iother;
}
Iter iter_;
};
std::unique_ptr<concept> concept_ptr_;
public:
bool operator==(PolymorphicValueIterator const &r) const {
return concept_ptr_->is_equal(r.concept_ptr_->get_details());
}
bool operator!=(PolymorphicValueIterator const &r) const {
return not concept_ptr_->is_equal(r.concept_ptr_->get_details());
}
PolymorphicValueIterator &operator++() {
concept_ptr_->invoke_next(1);
return *this;
}
value_type& operator*() const {
return concept_ptr_->invoke_deref();
}
template<class Iter>
PolymorphicValueIterator(Iter iter)
{
concept_ptr_ = std::make_unique<model<Iter>>(iter);
}
PolymorphicValueIterator(PolymorphicValueIterator const& r)
: concept_ptr_(r.concept_ptr_->clone())
{}
PolymorphicValueIterator& operator=(PolymorphicValueIterator const& r)
{
concept_ptr_ = r.concept_ptr_->clone();
return *this;
}
};
template<class Iter>
auto makePolymorphicValueIterator(Iter iter)
{
using iter_value_type = typename std::iterator_traits<Iter>::value_type;
using value_type = type_of_value_t<iter_value_type>;
return PolymorphicValueIterator<value_type>(iter);
}
// a test
void do_orange_things(PolymorphicValueIterator<Orange> first, PolymorphicValueIterator<Orange> last)
{
while(first != last) {
std::cout << "Orange\n";
++first;
}
}
int main()
{
std::vector<Orange> vo {
Orange(), Orange()
};
std::map<int, Orange> mio {
{ 1, Orange() },
{ 2, Orange() },
{ 3, Orange() }
};
std::cout << "vector:\n";
auto first = makePolymorphicValueIterator(vo.begin());
auto last = makePolymorphicValueIterator(vo.end());
do_orange_things(first, last);
std::cout << "\nmap:\n";
first = makePolymorphicValueIterator(mio.begin());
last = makePolymorphicValueIterator(mio.end());
do_orange_things(first, last);
}
My code looks like that:
class Node : public bitset<BITS_COUNT> {
public:
Node(string s) : bitset<BITS_COUNT>(s) {}
void setGroup(unordered_set<Node>* newSet) { currentGroup = newSet; }
unordered_set<Node>* getCurrentSet() { return currentGroup; }
private:
unordered_set<Node>* currentGroup = nullptr;
};
But complier doesn't allow me to do this, since there is no hash function defined for class Node. I'd like it to use hash function from base class, so I did this:
namespace std
{
template<>
struct hash<Node>
{
size_t operator()(const Node& k) const
{
return k.hash();
}
};
}
But it still doesn't work. If I put this before Node delcaration, k.hash() is undefined (and I can't forward declare Node: public bitset<> since it isn't possible). If I put this after class declaration I get error that there is no hash function for class Node.
How can I solve this problem?
Thanks Frank, your comment is actually a solution for me. If someone needs the code it looks like this:
namespace std
{
template<>
struct hash<Node>
{
size_t operator()(const Node& k) const;
};
}
class Node : public std::bitset<BITS_COUNT> {
public:
Node(std::string s) : bitset<BITS_COUNT>(s) {}
void setGroup(std::unordered_set<Node>* newSet) { currentGroup = newSet; }
std::unordered_set<Node>* getCurrentSet() { return currentGroup; }
private:
std::unordered_set<Node>* currentGroup = nullptr;
};
namespace std
{
size_t hash<Node>::operator()(const Node& k) const
{
return k.hash();
}
}
I have a template Node which returns data of type T.
template <class T> Node
{
public:
virtual const T& GetData() = 0;
};
And I want to have derived classes RefNode, and ValueNode that contain Pointers to data, and actual data. So that I can choose whether to work with a copy of data or to work on actual data in a node.
template<class T> class RefNode : public Node<T>
{
public:
RefNode(T *_data) : data(_data) { }
const T& GetData() { return *data; }
protected:
DataType *data;
};
template<class T> class ValueNode : public Node<T>
{
public:
ValueNode(const T&_data) : data(_data) { }
const T& GetData() { return data; }
protected:
T data;
};
I know that templates can't have virtual methods, but I just wanted to illustrate the effect that I wanted to get. The effect that I wanted to get is:
//for class Vector
Vector v, *c;
c = new Vector();
Node<Vector>* node = new RefNode<Vector>(c);
Node<Vector>* node2 = new ValueNode<Vector>(a);
node2->GetData(); //calls ValueNode<Vector>'s GetData();
node->GetData(); //calls RefNode<Vector>'s GetData();
Is there any way in C++ to achieve this kind of behaviour?
EDIT:
I would use GetData() like this:
Vector *vecarr[9];
Node<Vector>* nodes[10];
nodes[0] = new RefNode<Vector>(vecarr[0]);
nodes[1] = new ValueNode<Vector>(Vector(2,3)); //non reference vector
nodes[2] = new RefNode<Vector>(vecarr[1]);
nodes[3] = new RefNode<Vector>(vecarr[2]);
.....
void processPositionNodes(Node<Vector> **nodes, int n)
{
for(int i=0; i< n; i++) //iterate over all nodes
{
Vector vec = nodes[i]->GetData();
//do something with vec
}
}
I want to be able to change the type of data the Node contains, because I want to implement several graph algorithms dealing with different types of data, (Vectors, scalars..)
This code works just fine (there are some minor and unrelevant changes to your version):
#include <iostream>
using namespace std;
template <class T>
class Node
{
public:
virtual const T& GetData() = 0;
};
template<class T>
class RefNode : public Node<T>
{
public:
RefNode(T *_data) : data(_data) { *data = 5; }
const T& GetData() { return *data; }
protected:
T *data;
};
template<class T> class ValueNode : public Node<T>
{
public:
ValueNode(const T&_data) : data(_data) { data = 5; }
const T& GetData() { return data; }
protected:
T data;
};
int main(){
double data;
Node<double>* rn = new RefNode<double>(&data);
Node<double>* rv = new ValueNode<double>(data);
const double& a = rn->GetData();
const double& b = rv->GetData();
cout << a << '\t' << b << endl;
}
However, there are some potential issues with this code: lack of virtual destructor in class Node, lack of copy ctor, dtor, and operator= in class RefNode
As pointed out in the comments, templates can indeed have virtual functions. However this is not going to solve your problem. In fact, you would need those functions to have different return types.
This is a possible template-based solution to your problem, it might not be the most elegant but you (we) can work on this basis
#include <iostream>
using namespace std;
template <typename T, template<typename> class Accessor, template<typename> class Traits>
class Node{
public:
Node() : data(5.){}
typedef typename Traits<T>::type getType;
getType get(){
return static_cast<Accessor<T>*>(this)->implementation();
}
virtual ~Node(){}
protected:
T data;
};
template <typename T>
struct TraitsP{
typedef T* type;
};
template <typename T>
class PointerAccessor : public Node<T, PointerAccessor, TraitsP>{
public:
typename TraitsP<T>::type implementation(){
return &(Node<T, PointerAccessor, TraitsP>::data);
}
};
template <typename T>
struct TraitsD{
typedef T type;
};
template <typename T>
class DirectAccessor : public Node<T, DirectAccessor, TraitsD>{
public:
typename TraitsD<T>::type implementation(){
T ret = Node<T, DirectAccessor, TraitsD>::data;
return ret;
}
};
int main(){
auto np = new PointerAccessor<double>();
double* p = np->get();
cout << *p << endl;
auto np2 = new DirectAccessor<double>();
double d = np2->get();
cout << d << endl;
}
What might seem strange about this solution is PointerAccessor being derived by Node\<..., PointerAccessor, ...>. This is the so called Curiously Recurring Template Pattern (CRTP)
Sorry for the unclear title, actually I couldn't think of a title that describes my problem concisely.
But the question is simple to state. I have a Node class. I want to maintain order among its objects by its id_ field. I know that making a multiset<Node> will correctly maintain the order in the container if I overload < operator in Node class or provide a Comparator object in multiset. But I want to declare a multiset<Node*> container and want to achieve the same behaviour.
Here is my Node class definition:
class Node {
int id_;
...
public:
Node() {
...
}
int getId() {
return id_;
}
void setId(int id) {
id_ = id;
}
...
bool operator<(const Node &input) {
return (this->id_ < input.id_);
}
};
What do I do?
I think what you mean and what you need is this:
template <typename T, typename Pred = std::less<T>>
struct ptr_compare : Pred
{
ptr_compare(Pred const & p = Pred()) : Pred(p) { }
bool operator()(T const * p1, T const * p2) const
{
return Pred::operator()(*p1, *p2);
}
};
typedef std::multiset<Node*, ptr_compare<Node>> node_ptr_set;
You can use the ptr_compare template for any container that requires a binary predicate and you want to apply the predicate indirectly.