[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: too many warnings from Bison CVS for Pike
From: |
Hans Aberg |
Subject: |
Re: too many warnings from Bison CVS for Pike |
Date: |
Sat, 25 Feb 2006 21:01:48 +0100 |
On 25 Feb 2006, at 17:36, Akim Demaille wrote:
There is no problem with storing a polymorphic pointer in a
variant.
The idea is to use the traditional C++ virtual hierarchy. This is
the model that was introduced into C++ as an extension of the
corresponding one in C.
Which is precisely what I refer to as a polymorphic pointer.
I used the word polymorphy wrongly (because I have not given so much
thought to the "why", as I already have my working setup). It can be
static (compile time only), as in the template system. The 'union'
construct provides a dynamic one, essentially untyped, polymorphy,
which avoids free store and its allocation time. When I started to
use the polymorphic class hierarchy, it was recommended as a
replacement of the union construction.
I fail to see the point of your comment.
You probably need to learn this C++ programing first, then.
Yeah, right.
I will need to check out the boost::variant a bit more :-); it may
perhaps fill that gap, providing a C++ typed (POD type admitting)
union. But I do not think it will be enough in itself in a more
general setting.
I use a base class "object" with a reference count in it, by which
other classes are derived as "public", (but not "virtual", if one
should use static_cast). After playing around with different
implementation models, I have found the one below the easiest to use,
because when I need a new polymorphic base class, I only use ref<>
defined below. Perhaps boost has something similar. But the stuff
below is tailored to my needs, so I do not have any reason right now
to look for something else.
#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); }
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_class(object);
clone_class(object);
void shed() { if (--count_ == 0) delete this; }
virtual void write(std::ostream& os, write_style) const { os <<
"object"; }
};
Add a polymorphic class:
class X : public object {
public:
...
clone_class(X);
clone_class(X);
...
};
Then, if I should create a polymorphic base class based on the
interface of X, I use a template class ref<X>, which keeps track of a
pointer to X, plus has some conversion operators in it, making the
interface of X accessible.
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_;
}
};
Then ref<X> becomes this polymorphic base class; some of these base
classes X are in fact virtual (abstract), making it impossible to
store a value of them (as in a boost::variant), but only usable as a
pointer in ref<X>.
Then ref<X> is the class to be used in the Bison static type system.
The Bison semantic type will then typically just contain ref<object>,
as conversions between ref<object> and ref<X> will be possible. One
should then be able to use the static_cast conversions of this class.
But in the subsequent runtime program, after the parse has been done,
dynamic_cast must be used; this is the reason there are static_cast/
dynamic_cast pairs in some of the conversion operators. In the code
above, I use some special stuff that should probably be removed or
simplified at some point, but which has to do with the origin of the
code. For example, the object null_ above is used to specify the
behavior of ref<X>() by implementing a special class. The class X
must the contain something like
class X : public Y {
public:
typedef X_null null_type;
...
};
class X_null : public X {...};
If not absolutely needed, null_ should be removed.
As it happens, I do not need the function clone() in the parser
actions, as new objects will be created by new dynamic allocations. I
think I may use it somewhere in the program though. Also, this
program does not need a handle; but I have another program that
requires it, as objects can self-mutate to one of a different static
type, and it is also using a union together with a type in the form
of an 'enum' as well, to choose between different function pointers,
a pointer and a handle.
Hans Aberg
- Re: too many warnings from Bison CVS for Pike, (continued)
- Re: too many warnings from Bison CVS for Pike, Akim Demaille, 2006/02/21
- Re: too many warnings from Bison CVS for Pike, Hans Aberg, 2006/02/21
- Re: too many warnings from Bison CVS for Pike, Joel E. Denny, 2006/02/21
- Re: too many warnings from Bison CVS for Pike, Hans Aberg, 2006/02/21
- Re: too many warnings from Bison CVS for Pike, Joel E. Denny, 2006/02/21
- Re: too many warnings from Bison CVS for Pike, Hans Aberg, 2006/02/22
- Re: too many warnings from Bison CVS for Pike, Akim Demaille, 2006/02/22
- Re: too many warnings from Bison CVS for Pike, Akim Demaille, 2006/02/22
- Re: too many warnings from Bison CVS for Pike, Hans Aberg, 2006/02/24
- Message not available
- Re: too many warnings from Bison CVS for Pike, Hans Aberg, 2006/02/25
- Message not available
- Re: too many warnings from Bison CVS for Pike,
Hans Aberg <=
- Message not available
- Re: too many warnings from Bison CVS for Pike, Hans Aberg, 2006/02/25
- Re: too many warnings from Bison CVS for Pike, Hans Aberg, 2006/02/25