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
_______________________________________________
Help-bison@gnu.org http://lists.gnu.org/mailman/listinfo/help-bison