senloa
senloa

Reputation: 311

Visitor pattern for tree mutation and shared pointer problem

I'm trying to implement visitor pattern for n-ary tree mutation. Currently i'm stuck with shared pointers. Mutator operates on each tree node and can return either pointer to a node itself w/o any changes, or a pointer to a copy of modified node (original node cannot be modified directly). But obviously i can't build a shared pointer from a raw pointer inside Visit method, neither i can't make a shared pointer from this inside a Mutate method. The only idea that comes into my mind, is to create another class which will be holding pointer and pass this class pointer to visitor instead, but perhaps there is a better solution?


// Forward declarations
class ExpressionTreeMutator;

struct BaseExpr;

enum class ExpressionType {
  kString,
  kInt,
};

using ExprPtr = std::shared_ptr<BaseExpr>;

struct BaseExpr {
  explicit BaseExpr(ExpressionType expression_type) : expression_type_(expression_type) {}

  virtual ~BaseExpr() = default;

  virtual ExprPtr Mutate(ExpressionTreeMutator* visitor) const = 0;

  ExpressionType expression_type_;
};

// Curiously recurring template pattern
template<typename T>
struct Expr : public BaseExpr {
  explicit Expr(ExpressionType expression_type) : BaseExpr(expression_type) {}

  ~Expr() override = default;

  ExprPtr Mutate(ExpressionTreeMutator* visitor) const override;
};

struct String : public Expr<String> {
  explicit String(std::string value) : Expr(ExpressionType::kString),
                                       value_(std::move(value)) {}

  std::string value_;
};

struct Int : public Expr<Int> {
  explicit Int(int64_t value) : Expr(ExpressionType::kInt),
                                value_(value) {}

  int64_t value_;
};

class ExpressionTreeMutator {
public:
  virtual ~ExpressionTreeMutator() = default;

protected:
  template<typename T>
  friend
  struct Expr;

  virtual ExprPtr Visit(Int const* expr) {
//    return expr ??? (in some cases return ptr as it is)
  };

  virtual ExprPtr Visit(String const* expr) {
//     return String::Make(expr) ??? (make a copy)
  };
};

template<typename T>
ExprPtr Expr<T>::Mutate(ExpressionTreeMutator* visitor) const {
  return visitor->Visit(dynamic_cast<T const*>(this));
}

Upvotes: 3

Views: 216

Answers (1)

jlanik
jlanik

Reputation: 939

As it is tagged C++20, I'd suggest to use std::variant and std::visit instead.

Otherwise, you can inherit from std::enable_shared_from_this, which allows to create shared_ptr from within methods of X.

You can also use mutate not to do the actual mutation, but to return appropriate function object that does the mutation and then pass the shared_ptr to this object.

Upvotes: 1

Related Questions