Specifying base class order with multiple inheritance?

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.
 
E

Erik Wikström

Hello all, I'd like to know if there was some way I could assure the
order of base classes.
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.

AFAIK the only thing that the C++ standard says about how classes are
laid out in memory is the order of its members, which leaves the rest up
to the discretion of the compiler vendor. This means that you might get
your solution to work on some compiler, but you can never be sure it
will work on some other compiler or some other version of the same
compiler (or for that matter, if you change the compiler options).
 
J

James Kanze

I'd like to know if there was some way I could assure the
order of base classes.

[...]
So, I'm wondering two things:
1. is my solution above at all crazy?, and

I'm not sure I fully understand it, but...
2. can I ensure that the Generic base is at the start of the
object?

No. There are absolutely no guarantees with regards to how the
compiler lays out base classes in objects.
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.

In some implementations, that might be, provided no virtual
inheritance is involved (although in a scheme like yours,
virtual inheritance will almost certainly be involved at some
point). But it's certainly not guaranteed, and I believe that
there are (or were) some compilers that put the base class after
the first derived class. (I think the goal was to get the
object pointer pointing to the middle of the object, so you
could use negative offsets, as well as positive, and have bigger
objects with the offset only a byte.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,001
Messages
2,570,255
Members
46,853
Latest member
GeorgiaSta

Latest Threads

Top