A
alan
Hello all, I'd like to know if there was some way I could assure the
order of base classes.
To be specific, I have a basemost class Generic:
class Generic{
private:
Generic* to_ptr;
public:
virtual size_t get_size() const =0;
virtual Generic* clone(Semispace&) const =0;
// gets bits of the class that refer to other
// Generic objects.
virtual void get_refs(std::stack<Generic**>&){/*default no
references*/}
virtual ~Generic(){}
/*....*/
}
This base class is used intrusively by a copying-GC memory manager
I've built which handles Generic* objects in a heap, and is complex
enough that I would prefer to post it only if it's deemed really,
really necessary.
I've recently thought of some way to reduce memory consumption, mostly
by specifying that some objects are trivially reusable - an operation
which would create a new version of the object could actually reuse
the old object. Of course, an object might be referred to by some
other non-reusable object, so it must be marked as "nonreusable" and
can't be reused.
So I've defined a Reusable class which specifies that certain classes
are potentially reusable:
class Reusable{
private:
bool nonreusable;
public:
bool reusable(void){return !nonreusable;}
void banreuse(void){
//ignore call if already nonreusable
if(nonreusable) return;
nonreusable = 1;
Generic* tmp = dynamic_cast<Generic*>(this);
if(tmp){
//ban also the references
std::stack<Generic**> refs;
tmp->get_refs(refs);
while(!refs.empty()){
Reusable* tmp2 =
dynamic_cast<Reusable*>(*stack.top());
if(tmp2) tmp2->banreuse();
stack.pop();
}
}
}
};
So for example I might have a boxed integer class that could be
reused:
class Integer : public Generic, public Reusable{
protected:
Integer(Integer const& o) : val(o.val), Generic(), Reusable(){}
public:
virtual size_t get_size(void){return sizeof(Integer);}
virtual Integer* clone(Semispace& sp) const {return new(sp)
Integer(*this);}
};
I also have a bunch of classes that *can't* be reused:
class Closure : public Generic{
protected:
std::vector<Generic*> dat;
Closure(Closure const& o) : dat(o.dat), Generic() {}
public:
virtual size_t get_size(void){return sizeof(Closure);}
virtual Closure* clone(Semispace& sp) const {return new(sp)
Closure(*this);}
virtual void get_refs(std::stack<Generic**>& st){
for(size_t i = 0; i < dat.size(); ++i){
st.push(&dat);
}
}
explicit Closure(size_t sz) : dat(sz), Generic() {}
Generic* get(size_t ind){return dat[ind];}
// enforce invariant: nonreusable object can't point to
// reusable object, so make the reusable object nonreusable
virtual Generic* set(size_t ind, Generic* val){
dat[ind] = val;
Reusable* tmp = dynamic_cast<Reusable*>(val);
if(tmp) tmp->banreuse();
}
}
And I have a reusable class that's derived from a class that can't be
reused:
class KClosure : public Closure, public Reusable{
protected:
KClosure(KClosure const& o) : Closure(o), Generic() {}
public:
virtual size_t get_size(void){return sizeof(KClosure);}
virtual KClosure* clone(Semispace& sp) const {return new(sp)
KClosure(*this);}
explicit KClosure(size_t sz) : Closure(sz), Generic(){}
// enforce the invariant only if we're not reusable
virtual Generic* set(size_t ind, Generic* val){
if(!reusable()) Closure::set(ind, val);
else dat[ind] = val;
}
};
My concern, however, is that my memory manager will work correctly
only if the start of all objects derived from Generic corresponds to a
Generic; that is:
KClosure* kp = some_valid_value();
assert(((void*) static_cast<Generic*>(kp)) == ((void*) kp));
So, I'm wondering two things:
1. is my solution above at all crazy?, and
2. can I ensure that the Generic base is at the start of the object?
I would assume that if Generic and derived-only-from-Generic is listed
as the first class in the inheritance list, it would be positioned to
the start of the object.
order of base classes.
To be specific, I have a basemost class Generic:
class Generic{
private:
Generic* to_ptr;
public:
virtual size_t get_size() const =0;
virtual Generic* clone(Semispace&) const =0;
// gets bits of the class that refer to other
// Generic objects.
virtual void get_refs(std::stack<Generic**>&){/*default no
references*/}
virtual ~Generic(){}
/*....*/
}
This base class is used intrusively by a copying-GC memory manager
I've built which handles Generic* objects in a heap, and is complex
enough that I would prefer to post it only if it's deemed really,
really necessary.
I've recently thought of some way to reduce memory consumption, mostly
by specifying that some objects are trivially reusable - an operation
which would create a new version of the object could actually reuse
the old object. Of course, an object might be referred to by some
other non-reusable object, so it must be marked as "nonreusable" and
can't be reused.
So I've defined a Reusable class which specifies that certain classes
are potentially reusable:
class Reusable{
private:
bool nonreusable;
public:
bool reusable(void){return !nonreusable;}
void banreuse(void){
//ignore call if already nonreusable
if(nonreusable) return;
nonreusable = 1;
Generic* tmp = dynamic_cast<Generic*>(this);
if(tmp){
//ban also the references
std::stack<Generic**> refs;
tmp->get_refs(refs);
while(!refs.empty()){
Reusable* tmp2 =
dynamic_cast<Reusable*>(*stack.top());
if(tmp2) tmp2->banreuse();
stack.pop();
}
}
}
};
So for example I might have a boxed integer class that could be
reused:
class Integer : public Generic, public Reusable{
protected:
Integer(Integer const& o) : val(o.val), Generic(), Reusable(){}
public:
virtual size_t get_size(void){return sizeof(Integer);}
virtual Integer* clone(Semispace& sp) const {return new(sp)
Integer(*this);}
};
I also have a bunch of classes that *can't* be reused:
class Closure : public Generic{
protected:
std::vector<Generic*> dat;
Closure(Closure const& o) : dat(o.dat), Generic() {}
public:
virtual size_t get_size(void){return sizeof(Closure);}
virtual Closure* clone(Semispace& sp) const {return new(sp)
Closure(*this);}
virtual void get_refs(std::stack<Generic**>& st){
for(size_t i = 0; i < dat.size(); ++i){
st.push(&dat);
}
}
explicit Closure(size_t sz) : dat(sz), Generic() {}
Generic* get(size_t ind){return dat[ind];}
// enforce invariant: nonreusable object can't point to
// reusable object, so make the reusable object nonreusable
virtual Generic* set(size_t ind, Generic* val){
dat[ind] = val;
Reusable* tmp = dynamic_cast<Reusable*>(val);
if(tmp) tmp->banreuse();
}
}
And I have a reusable class that's derived from a class that can't be
reused:
class KClosure : public Closure, public Reusable{
protected:
KClosure(KClosure const& o) : Closure(o), Generic() {}
public:
virtual size_t get_size(void){return sizeof(KClosure);}
virtual KClosure* clone(Semispace& sp) const {return new(sp)
KClosure(*this);}
explicit KClosure(size_t sz) : Closure(sz), Generic(){}
// enforce the invariant only if we're not reusable
virtual Generic* set(size_t ind, Generic* val){
if(!reusable()) Closure::set(ind, val);
else dat[ind] = val;
}
};
My concern, however, is that my memory manager will work correctly
only if the start of all objects derived from Generic corresponds to a
Generic; that is:
KClosure* kp = some_valid_value();
assert(((void*) static_cast<Generic*>(kp)) == ((void*) kp));
So, I'm wondering two things:
1. is my solution above at all crazy?, and
2. can I ensure that the Generic base is at the start of the object?
I would assume that if Generic and derived-only-from-Generic is listed
as the first class in the inheritance list, it would be positioned to
the start of the object.