help-bison
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: %union, C++, symbol types


From: Hans Aberg
Subject: Re: %union, C++, symbol types
Date: Tue, 19 Jul 2005 17:57:59 +0200


On 19 Jul 2005, at 15:00, Evan Lavelle wrote:

Thanks for the input on this. I can't really use the pointer solutions, so my current fix is to define YYSTYPE as a struct, rather than a union, and to pass around classes in the struct. This seems to work well, though it's inefficient.

I use:

class semantic_type {
public:
  long number_;
  std::string text_;
  my::ref<my::object> object_;

  semantic_type() : number_(0) {}
};

#define YYSTYPE semantic_type

Here, number_ is used exclusively for token numbers, and text_ for token names. The class hierarchy, with base my::object, is defined without reference counts, as it turns out to be easier to class derivations that way. When using references, I instead invoke ref<T>, where T is a class derived from my::object.

template<class A>
class ref {
protected:
  mutable A* data_;
  static typename A::null_type null_;

public:
  typedef A&         reference;
  typedef A*         pointer;
  typedef const A&   const_reference;
  typedef const A*   const_pointer;

  typedef ref<A> This;

  ref() : data_(0) {}
  ~ref() { shed(); }

  ref(const ref& x) : data_(x.copy()) {}
  ref& operator=(const ref& x) {
    if (data_ != x.data_) { shed(); data_ = x.copy(); }
    return *this;
  }

  // Conversion constructors.
  ref(A* ap) : data_(ap) {}
  ref(const A* ap) : data_(ap->copy()) {}
  ref(const A& a) : data_(a.copy()) {}

  template<class B>
  explicit ref(B* bp, bool dynamic = true)
   : data_(dynamic? dynamic_cast<A*>(bp) : static_cast<A*>(bp)) {}

  template<class B>
  explicit ref(const B& br, bool dynamic = true)
: data_(dynamic? dynamic_cast<A*>(br.copy()) : static_cast<A*> (br.copy())) {}

  ref<A> clone() const { return (data_ == 0)? 0 : data_->clone(); }
  A* copy() const { return (data_ == 0)? 0 : data_->copy(); }

  void shed() { if (data_ != 0)  data_->shed(); }

  bool is_null() const { return (data_ == 0); }

  // Operators that return pointer 0 when applicable:

  operator A*() { return data_; }
  operator const A*() const { return data_; }

  // Operators that return reference to an object A() when applicable:

// Return a pointer to the referenced object, or if 0, the A::null_ object:
  A* operator->() { if (data_ == 0) return &null_; else return data_; }
const A* operator->() const { if (data_ == 0) return &null_; else return data_; }

// Return a reference to the referenced object, or if 0, the A::null_ object:
  A& operator*() { if (data_ == 0) return null_; else return *data_; }
const A& operator*() const { if (data_ == 0) return null_; else return *data_; }

  // Create an independent copy of the referenced object.
  ref<A> detach() const {
if (data_ != 0 && data_->count() > 1) { data_->shed(); data_ = data_->clone(); }
    return copy();
  }

// If 0, mutate to new A(). Return a reference to an independent copy of the
  // referenced object.
  A& operator+() const {
    if (data_ == 0)  data_ = new A();
else if (data_->count() > 1) { data_->shed(); data_ = data_- >clone(); }
    return *data_;
  }
};


template<class A, class B>
A* cast_pointer(ref<B>& ar) { return dynamic_cast<A*>((B*)ar); }

template<class A, class B>
const A* cast_pointer(const ref<B>& ar) { return dynamic_cast<const A*>((const B*)ar); }

template<class A, class B>
A& cast_reference(ref<B>& ar) { return dynamic_cast<A&>(*(B*)ar); }

template<class A, class B>
const A& cast_reference(const ref<B>& ar) { return dynamic_cast<const A&>(*(const B*)ar); }

#define ref_null(A) A::null_type ref<A>::null_

// Use the clone_declare/clone_source if clone he
#define clone_class(A) virtual A* clone() const { return new A (*this); } #define copy_class(A) virtual A* copy() const { increment_count(); return const_cast<A*>(this); }

#define clone_declare(A)  virtual A* clone() const
#define clone_source(A)  A* A::clone() const { return new A(*this); }

#define copy_declare(A)  virtual A* copy() const
#define copy_source(A) A* A::copy() const { increment_count(); return const_cast<A*>(this); }

class object {
  typedef unsigned long count_type;
  mutable count_type count_;
public:
  typedef object null_type;

  object() : count_(1) {}
  virtual ~object() {}

  object(const object&) : count_(1) {}

  void increment_count() const { ++count_; }
  count_type count() const { return count_; }

  clone_declare(object);
  copy_declare(object);

  void shed() { if (--count_ == 0)  delete this; }

virtual void write(std::ostream& os, write_style) const { os << "object"; }
};

inline std::ostream& operator<<(std::ostream& os, const object& a) {
  a.write(os, write_default);  return os;
}

The reason that reference counting is required here is that the lexer produces a token class, rather than AST nodes, and a single token might eventually appear multiple times (or not at all) in the AST.

Derived classes T from my::object, should look something like:

class T : public object {
public:
  typedef unsigned long size_type;
  typedef formula_null null_type;

  clone_declare(formula) = 0;
  copy_declare(formula) = 0;
  ...
};


class T_null : public T {
public:
  clone_declare(formula_null);
  copy_declare(formula_null);
  ...
};

Two points: If one wants to use static_cast, instead of dynamic_cast, then the base classes cannot be virtual in derivation. And the class T_null above is used to implement special behavior of ref<T>().

  Hans Aberg






reply via email to

[Prev in Thread] Current Thread [Next in Thread]