0

I am learning the design pattern now and I read the book Ruminations on C++. The following example is how to use the handle class to do some application.

#include <iostream>
#include <string>
#include <utility>

using namespace std;

class Expr_node;
class Int_node;
class Unary_node;
class Binary_node;

class Expr {
  friend ostream& operator<<(ostream&, const Expr&);

  Expr_node* p;
 public:
  Expr(int);
  Expr(const string&, Expr);
  Expr(const string&, Expr, Expr);
  Expr(const Expr&);
  Expr& operator=(const Expr&);
};

Expr::Expr(int n) {
  p = new Int_node(n);
}

Expr::Expr(const string& op, Expr t) {
  p = new Unary_node(op, t);
}

Expr::Expr(const string & op, Expr left, Expr right) {
  p = new Binary_node(op, left, right);
}

class Expr_node {
    friend ostream& operator<< (ostream&, const Expr_node&);

protected:
    virtual void print(ostream&) const = 0;
    virtual ~Expr_node() { }
};

ostream& operator<< (ostream& o, const Expr_node& e) {
    e.print(o);
    return o;
}

class Int_node: public Expr_node {
  friend class Expr;

  int n;

  explicit Int_node(int k) : n(k) {}
  void print(ostream& o) const override { o << n;}
};

class Unary_node: public Expr_node {
  friend class Expr;
  string op;
  Expr opnd;
  Unary_node(const string& a, const Expr& b): op(a), opnd(b) {}
  void print(ostream& o) const override {o << "(" << op << *opnd << ")";}
};

class Binary_node: public Expr_node {
  friend class Expr;
  string op;
  Expr left;
  Expr right;
  Binary_node(const string& a, const Expr& b, const Expr& c): op(a), left(b), right(c) {}
  void print(ostream& o) const override { o << "(" << left << op << right << ")";}
};

In this example, I want to implement three different kinds of operation based on inheritance from Expr_node class. It is obvious that the Int_node is not well defined until its full definition. I am not sure how to solve this problem. It seems that there are a lot of errors involved in this book.

Sean
  • 901
  • 2
  • 11
  • 30
  • "the design pattern" ? there is more than one, which one are you dealing with? – 463035818_is_not_an_ai Apr 28 '20 at 13:16
  • 1
    you want to learn more about [*forward declarations*](https://stackoverflow.com/questions/4757565/what-are-forward-declarations-in-c). Pro tip: Reduce your code amount before posting it. I haven't read it, it's too much. – pasbi Apr 28 '20 at 13:19
  • Simply move `Expr::Expr(int)` so that it is after the defintion of `Int_node`. – john Apr 28 '20 at 13:20
  • You are implementing the Strategy Pattern. friends and forward declarations are a cludge. Look for a different example. – jiveturkey Apr 28 '20 at 13:21
  • @pasbi The OP is using forward declarations in their code. – john Apr 28 '20 at 13:21
  • @john indeed, they do. However, they do it wrong. And that's why they want to learn more about it. – pasbi Apr 28 '20 at 13:24

2 Answers2

1

To answer the question (beside the other considerations that are in the various comments) :

you need to implement the definition of the constructor taking a int parameter

Expr::Expr(int n) {
  p = new Int_node(n);
}

after the definition of the constructor Int_node(int n) which is in your example inlined in the declaration of the Int_node class here :

class Int_node: public Expr_node {
  friend class Expr;

  int n;

  explicit Int_node(int k) : n(k) {}
  void print(ostream& o) const override { o << n;}
};
sandwood
  • 2,038
  • 20
  • 38
  • You may want to note that this traditionally occurs by splitting the program into multiple files, with declarations in headers. – Caleth Apr 28 '20 at 13:52
  • Yes, but in the other hand this example have the benefit to make understand when you must have a function defined (i.e before using it) and when having it declare is enough. (and that is de-corelated from header and implementation files) – sandwood Apr 28 '20 at 14:04
0

This is one of the few things that I don't like in C++...

While this code is not okay

// a.h
struct B;
struct A {
    B f() { return B(); } // error: incomplete result type 'B'
};
struct B { int x; };

the following is fine if you put your code in two files

// a.h
struct B;
struct A {
    B f();
};
struct B { int x; };
// a.cpp
B A::f() { return B(); }

If you insist, you can put the definition of A::f() in the header file, as long as it's not in-class...

// a.h
struct B;
struct A {
    B f();
};
struct B { int x; };
// only after the complete definition of B
inline B A::f() { return B(); }

I've been telling myself it is what it is since the day I learned C++ in college.

oukore
  • 76
  • 1
  • 5