I have 3 classes, and I'd like each one to print out differently to the terminal, I have a node class that represents a vertex in a BDD graph, right now I'm trying to write code to do logical operations on the nodes.
The Node class is setup as such:
class Node {
char name;
public:
Node() { name = '0'; }
Node(char c) { name = c; }
Node(const Node& n) { name = n.name; }
friend ostream& operator<<(ostream& stream, const Node& n);
};
ostream& operator<<(ostream& stream, const Node& n) {
return stream << "{ Node " << n.name << " }";
}
The operator classes are setup as such:
class Operation {
public:
Node result;
friend std::ostream& operator<<(std::ostream& stream, const Operation& op);
Operation() {}
Operation(const Operation& op) { cout << "Copying " << *this << endl; }
virtual ~Operation() { cout << "Destroying " << *this << endl; }
virtual Node compute() {
cout << "Computing " << *this << endl;
result = Node('1');
return result;
}
};
std::ostream& operator<<(std::ostream& stream, const Operation& op) {
return stream << "Operation { Unspecified }";
}
class UnaryOperation : public Operation {
public:
Node arg1;
UnaryOperation(const Node& arg1) { this->arg1 = arg1; }
UnaryOperation(const UnaryOperation& op) : Operation::Operation(op) {
arg1 = op.arg1;
}
virtual ~UnaryOperation() {}
friend ostream& operator<<(ostream& stream, const UnaryOperation& op);
};
ostream& operator<<(ostream& stream, const UnaryOperation& op) {
return stream << "Operation { arg1: " << op.arg1 << " }";
}
class BinaryOperation : public UnaryOperation {
public:
Node arg2;
BinaryOperation(const Node& arg1, const Node& arg2) : UnaryOperation(arg1) {
this->arg2 = arg2;
}
BinaryOperation(const BinaryOperation& op) : UnaryOperation::UnaryOperation(op) {
arg2 = op.arg2;
}
virtual ~BinaryOperation() {}
friend ostream& operator<<(ostream& stream, const BinaryOperation& op);
};
ostream& operator<<(ostream& stream, const BinaryOperation& op) {
return stream << "Operation { arg1: " << op.arg1 << ", arg2: " << op.arg2;
}
For debugging reasons, these messages need to print out as such, but when I run this
Node apply(Operation& op) {
cout << "Performing apply operation on " << op << endl;
op.compute();
return op.result;
}
int main() {
Node a('a'), b('b');
UnaryOperation uop(a);
BinaryOperation bop(a, b);
cout << uop << endl;
cout << bop << endl;
apply(uop);
apply(bop);
}
I get
Operation { arg1: { Node a } }
Operation { arg1: { Node a }, arg2: { Node b }
Performing apply operation on Operation { Unspecified }
Computing Operation { Unspecified }
Performing apply operation on Operation { Unspecified }
Computing Operation { Unspecified }
Destroying Operation { Unspecified }
Destroying Operation { Unspecified }
Needless to say, this is not very helpful for debugging.
Why is it doing this, and how do I fix it?
The friend function is not virtual. It is selected according to the type of the second argument.
In this function
Node apply(Operation& op) {
cout << "Performing apply operation on " << op << endl;
op.compute();
return op.result;
}
the type of the argument is Operation &
. So in this statement
cout << "Performing apply operation on " << op << endl;
there is called the friend function for an object of the type Operation &
.
You could make the friend function "virtual" the following way as it is shown in the demonstrative program below.
#include <iostream>
class Operation
{
public:
virtual ~Operation() = default;
private:
virtual std::ostream & out( std::ostream &os = std::cout ) const
{
return os << "This is an Operation";
}
friend std::ostream& operator <<( std::ostream &stream, const Operation &op )
{
return op.out( stream );
}
};
class UnaryOperation : public Operation
{
std::ostream & out( std::ostream &os = std::cout ) const override
{
return os << "This is an Unary Operation";
}
friend std::ostream& operator <<( std::ostream &stream, const UnaryOperation &op )
{
return op.out( stream );
}
};
class BinaryOperation : public UnaryOperation
{
std::ostream & out( std::ostream &os = std::cout ) const override
{
return os << "This is a Binary Operation";
}
friend std::ostream& operator <<( std::ostream& stream, const BinaryOperation &op )
{
return op.out( stream );
}
};
void f( const Operation &op )
{
std::cout << op << '\n';
}
int main()
{
BinaryOperation bop;
f( bop );
return 0;
}
Its output is
This is a Binary Operation