• Advertisement
Sign in to follow this  

Class offset with its Base in Multiple inheritance

This topic is 2323 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all,

I am trying to compute the memory offset of a base class with its parents in a multiple inheritance scenario, I hope to find a general solution the would work for simple and multiple inheritance with or with out virtual function or virtual inheritance.

This is my function to compute the class offset.


template<typename ClassType, typename ParentType>
ptrdiff_t GetClassOffset()
{
static_assert(std::is_base_of<ParentType, ClassType>::value, "The parent type is not the base type of the class type.");

constexpr size_t max_size = sizeof(ClassType) + sizeof(ParentType);
const ClassType* classType = reinterpret_cast<const ClassType* >(max_size);
const ParentType* baseType = static_cast<const ParentType* >(classType);

uintptr_t classTypeOffset = reinterpret_cast<uintptr_t>(classType);

uintptr_t baseTypeOffset = reinterpret_cast<uintptr_t>(baseType);

return classTypeOffset - baseTypeOffset;
}




And I am testing with those classes



/// First Parent
class Parent1
{
int dummy;
};
/// Second Parent
class Parent2
{
int dummy;
};

/// Third Parent
class Parent3
{
int dummy;
};

/// Fourth Parent
class Parent4
{
int dummy;
};

/// Fifth Parent
class Parent5
{
int dummy;
};

class MultipleInheritance: public Parent1, public Parent2, public Parent3, public Parent4, public Parent5
{
int dummy;
};





I then tested with this code


std::cout << "Class offset of MultipleInheritance class with " << std::endl;
std::cout<< "MultipleInheritance: " << GetClassOffset< MultipleInheritance, MultipleInheritance>() << std::endl;
std::cout<< "Parent1: " << GetClassOffset< MultipleInheritance, Parent1>() << std::endl;
std::cout<< "Parent2: " << GetClassOffset< MultipleInheritance, Parent2>() << std::endl;
std::cout<< "Parent3: " << GetClassOffset< MultipleInheritance, Parent3>() << std::endl;
std::cout<< "Parent4: " << GetClassOffset< MultipleInheritance, Parent4>() << std::endl;
std::cout<< "Parent5: " << GetClassOffset< MultipleInheritance, Parent5>() << std::endl;


And the result was:


Class offset of MultipleInheritance class with
MultipleInheritance: 0
Parent1: 0
Parent2: -4
Parent3: -8
Parent4: -12
Parent5: -16


What i don't understand is why the offset of the class MultipleInheritance with Parent1 is 0 shouldn't it be -4? and thus everything else would be offseted by 4? Maby the problem is with the function getClassOffset but I don't see it.

Also is this function can cover all the scenario possible, I think it is possible but maybe i don't see it.

Anybody can help me?

Thanks alot

Share this post


Link to post
Share on other sites
Advertisement
MultipleInheritance::dummy is placed after all the inherited data. That's why you can have Parent1 at offset 0.

Share this post


Link to post
Share on other sites

I am trying to compute the memory offset of a base class with its parents in a multiple inheritance scenario, I hope to find a general solution the would work for simple and multiple inheritance with or with out virtual function or virtual inheritance.
[/quote]

I'm fairly certain that there's no portable, reproducible way to do this for all scenarios. I'm curious about what problem you're trying to solve.

Share this post


Link to post
Share on other sites


I am trying to compute the memory offset of a base class with its parents in a multiple inheritance scenario, I hope to find a general solution the would work for simple and multiple inheritance with or with out virtual function or virtual inheritance.


I'm fairly certain that there's no portable, reproducible way to do this for all scenarios. I'm curious about what problem you're trying to solve.
[/quote]

The problem i am trying to solve is a reflection system. I need the offset to make my own dynamic_cast. Really there is no portable way? Why?

thanks

Share this post


Link to post
Share on other sites

MultipleInheritance::dummy is placed after all the inherited data. That's why you can have Parent1 at offset 0.


Really? What am i missing, in those 2 static_cast:


const ClassType* classType = reinterpret_cast<const ClassType* >(max_size);
const ParentType* baseType = static_cast<const ParentType* >(classType[font="monospace"][/font][color=#666600][font=CourierNew, monospace][size=2]);[/font]



They both return the same address in both cases of GetClassOffset(), for Parent1 and MultipleInheritance. but I am pretty sure that if I do:


int dummyOfClass = classType->dummy;
int dummyOfBase = baseType->dummy;


I wont get the same dummy variable. So I must not think of something.

thanks for you answer!

What I didn't understand is why the offset with Parent1 is 0 aswell as the offset with itself. Edited by monamimani

Share this post


Link to post
Share on other sites
You are right they are two different variables. What I meant was that MultipleInheritance and Parent1 can have the same address because the Parent1 data is located at the start of MultipleInheritance. If you print the address of the dummy variables in MultipleInheritance you will see that they are ordered as Parent1::dummy, Parent2::dummy, Parent3::dummy, Parent4::dummy, Parent5::dummy, MultipleInheritance::dummy in memory.

Share this post


Link to post
Share on other sites

You are right they are two different variables. What I meant was that MultipleInheritance and Parent1 can have the same address because the Parent1 data is located at the start of MultipleInheritance. If you print the address of the dummy variables in MultipleInheritance you will see that they are ordered as Parent1::dummy, Parent2::dummy, Parent3::dummy, Parent4::dummy, Parent5::dummy, MultipleInheritance::dummy in memory.


So that mean that MultipleInheritance contain all it's parent. Ok I understand that.

I guess the way I compute the offset is wrong since it give me a negative number, i should do baseTypeOffset - classTypeOffset instead of classTypeOffset - baseTypeOffset?

This way if i want to cast from MultipleInheritance* to Parent2* I add the offset (4) and reinterpret_cast the resulting address? This would mean that a cast to Parent1 and MultipleInheritance would result to the same address and no offset?

sorry, I have a hard time to believe it, even thought it most be true....

Share this post


Link to post
Share on other sites
All this pointer casting is a bit over my head, I don't even understand why you need max_size but using a null pointer doesn't work. hmm.

What's wrong with the built in dynamic_cast? I can't see what you will be able to do with this.

Share this post


Link to post
Share on other sites

[quote name='Telastyn' timestamp='1315783317' post='4860477']

I am trying to compute the memory offset of a base class with its parents in a multiple inheritance scenario, I hope to find a general solution the would work for simple and multiple inheritance with or with out virtual function or virtual inheritance.


I'm fairly certain that there's no portable, reproducible way to do this for all scenarios. I'm curious about what problem you're trying to solve.
[/quote]

The problem i am trying to solve is a reflection system. I need the offset to make my own dynamic_cast. Really there is no portable way? Why?

thanks
[/quote]

Because I thought that memory arrangement in that scenario is compiler/system dependent. A quick search can't confirm or deny that.

Share this post


Link to post
Share on other sites

All this pointer casting is a bit over my head, I don't even understand why you need max_size but using a null pointer doesn't work. hmm.

What's wrong with the built in dynamic_cast? I can't see what you will be able to do with this.


I don't think I need max_size, I saw that on a post on this forum. I saw befor using -1 instead of max_size I think the value is not important put the type of max_siaze should be enough to represent a type.

dynamic_cast rely on RTTI to be enabled, and I don't want to enable it because it is not complet for my need. Like exposing member of a class to a editor for a data driven engin.

Share this post


Link to post
Share on other sites

[quote name='monamimani' timestamp='1315785556' post='4860482']
[quote name='Telastyn' timestamp='1315783317' post='4860477']

I am trying to compute the memory offset of a base class with its parents in a multiple inheritance scenario, I hope to find a general solution the would work for simple and multiple inheritance with or with out virtual function or virtual inheritance.


I'm fairly certain that there's no portable, reproducible way to do this for all scenarios. I'm curious about what problem you're trying to solve.
[/quote]

The problem i am trying to solve is a reflection system. I need the offset to make my own dynamic_cast. Really there is no portable way? Why?

thanks
[/quote]

Because I thought that memory arrangement in that scenario is compiler/system dependent. A quick search can't confirm or deny that.
[/quote]

Some part are compiler dependant, like if the vtable is at the beginning or end of the class, but i think (read i am not sure), offset should be portable because it is really just pointer arithmetic.

Share this post


Link to post
Share on other sites

The problem i am trying to solve is a reflection system. I need the offset to make my own dynamic_cast. Really there is no portable way? Why?


Depending on any ABI (application binary interface) specific is not standard and thus not guaranteed portable.
And if you really need to do it, be sure to test on all major compilers you have, such as VC 2008, 2010, GCC, etc.
Even different VC compilers may have different ABI.

Share this post


Link to post
Share on other sites

Like exposing member of a class to a editor for a data driven engin.
[/quote]
To do that, you only need to list the relevant members, their types and some way to access them. The natural way to handle the access in C++ is to use pointers to members. Is there a reason this would be insufficient?

Why do you think you need the offset of the members?

Share this post


Link to post
Share on other sites
If you haven't already, you should probably read this:

http://stackoverflow.com/questions/41453/how-can-i-add-reflection-to-a-c-application

Share this post


Link to post
Share on other sites


Like exposing member of a class to a editor for a data driven engin.

To do that, you only need to list the relevant members, their types and some way to access them. The natural way to handle the access in C++ is to use pointers to members. Is there a reason this would be insufficient?

Why do you think you need the offset of the members?
[/quote]

Yes you are right, I'll need that to.

For me a reflection system enable you to cast pointer from one type to an other related type, like dynamic_cast but I can't use it since I don't enable RTTI and it as to have a list of the reflected members of the class.
For the reflected member of the class, yes i only need the pointer but to cast between type i need offset of classes between them in their herarchie.

I guess there was confusion because we werre talking how a multiple inheritance map to memory.

Share this post


Link to post
Share on other sites

All this pointer casting is a bit over my head, I don't even understand why you need max_size but using a null pointer doesn't work. hmm.

What's wrong with the built in dynamic_cast? I can't see what you will be able to do with this.


During the night i realized what I couldn't understand. I remembered that casting a pointer also change the view on that memory. So that if I would take a pointer to a class MultipleInheritance apply the offset to Parent2 and reinterpret_cast this address as Parent2, I could only access Parent2 members, same thing as Parent1. So it is not only a matther of offset but also the type of the pointer.

thanks for you help!

Share this post


Link to post
Share on other sites


template<typename ClassType, typename ParentType>
ptrdiff_t GetClassOffset()
{
static_assert(std::is_base_of<ParentType, ClassType>::value, "The parent type is not the base type of the class type.");

constexpr size_t max_size = sizeof(ClassType) + sizeof(ParentType);
const ClassType* classType = reinterpret_cast<const ClassType* >(max_size);
const ParentType* baseType = static_cast<const ParentType* >(classType);

uintptr_t classTypeOffset = reinterpret_cast<uintptr_t>(classType);

uintptr_t baseTypeOffset = reinterpret_cast<uintptr_t>(baseType);

return classTypeOffset - baseTypeOffset;
}




Hey Hi all again, sorry to wake this post again but I got a question that is related to my quoted function.

As you can see in my results in my first post the offset that i get are negative, which they shouldn't. I implemented my dynamic_cast and it work but the offset need to be positive. What i think is the problem is that the ptrdiff_t overflow from the substraction of the tow uintptr_t.

I tought the ptrdif_t was big enough for the substraction of 2 pointers, is it that uintptr is bigger than ptrdiff_t?

thanks

Share this post


Link to post
Share on other sites

I am trying to compute the memory offset of a base class with its parents in a multiple inheritance scenario, I hope to find a general solution the would work for simple and multiple inheritance with or with out virtual function or virtual inheritance.


For what purpose? to call methods? or to access data? or both?

There are some really easy ways of doing this. Your approach isn't one of them ;)

[color="#1C2837"]Because I thought that memory arrangement in that scenario is compiler/system dependent. A quick search can't confirm or deny that.[/quote]

[color="#1c2837"]Correct. It's undefined. However offsets can work, ish (i.e. if you are extremely careful!)

[color="#1C2837"]
[color="#1C2837"]So that mean that MultipleInheritance contain all it's parent. Ok I understand that.[/quote]

[color="#1C2837"]*MIGHT* contain all of it's parents, or it may contain multiple versions of the same class. Virtual inheritance tends to fsck things up quite a bit - although frankly, that's just multiple inheritance.

[color="#1C2837"]
Some part are compiler dependant, like if the vtable is at the beginning or end of the class, but i think (read i am not sure), offset should be portable because it is really just pointer arithmetic. [color="#1C2837"][/quote]

[color="#1c2837"]Offsets are portable. Bugs due to multiple inheritance/virtual inheritance can be ported to other platforms too ;)

[color="#1C2837"]
[color="#1C2837"]As you can see in my results in my first post the offset that i get are negative, which they shouldn't. I implemented my dynamic_cast and it work but the offset need to be positive. What i think is the problem is that the ptrdiff_t overflow from the substraction of the tow uintptr_t. [/quote]

[color="#1C2837"]The problem, as always, is one of logic.

[color="#1C2837"]Derived* derived = 0; // start at zero.
Base* base = derived; // starts at possibly +4/+8 etc.


[color="#1C2837"]Then you do,

[color="#1C2837"]derived - base, aka 0 - 4

[color="#1c2837"]Btw, that max_size is cruft. It's sole purpose is to induce chants of WTF in your co-workers. Get rid of it and just use 0. A few more comments wouldn't be out of place either! (Trust me, you'll have quite a few WTF moments when you look back at that code next year)

Share this post


Link to post
Share on other sites


For what purpose? to call methods? or to access data? or both?

There are some really easy ways of doing this. Your approach isn't one of them ;)


Really i only know with offset. What are the other way. What I am doing is a reflection system with macros "trickery" as some people call it. Each metaclass has a list of parents (C3 linearization) and each parent have the class offset. I'll also have a list of exposed members of the class and those offset are relative to this offcourse. I am curious how else it can be acheived?


[color="#1c2837"]Correct. It's undefined. However offsets can work, ish (i.e. if you are extremely careful!)
[color=#1C2837][size=2][/quote]

Careful in what way? I know there is special case like virtual inheritance. but For one case it will always work. Like in the case of simple inhertance.


[color="#1C2837"]*MIGHT* contain all of it's parents, or it may contain multiple versions of the same class. Virtual inheritance tends to fsck things up quite a bit - although frankly, that's just multiple inheritance.
[/quote]
I think you refer to the case where i would have a hierarchy of multiple inheritance with multiple occurrence of the same classe in the base classes but without qualifying each of them as virtual inheritance. This is considered really really bad coding practices and I think even standart dynamic cast doesn't handle that it just return the firs one it found.

By the way would know know how to get the offset of a class that you derive virtually from ?



[color="#1C2837"]

[color="#1c2837"]Offsets are portable. Bugs due to multiple inheritance/virtual inheritance can be ported to other platforms too ;)
[color="#1c2837"][/quote]
[color="#1c2837"]Sorry I don't get it?

[color="#1C2837"]
[color="#1C2837"]As you can see in my results in my first post the offset that i get are negative, which they shouldn't. I implemented my dynamic_cast and it work but the offset need to be positive. What i think is the problem is that the ptrdiff_t overflow from the substraction of the tow uintptr_t. [/quote]

[color="#1C2837"]The problem, as always, is one of logic.

[color="#1C2837"]

[color="#1C2837"]Then you do,

[color="#1C2837"]derived - base, aka 0 - 4
[color="#1c2837"][/quote]
Which give you -4 byte but I need to add 4.

[color="#1C2837"]

[color="#1c2837"]Btw, that max_size is cruft. It's sole purpose is to induce chants of WTF in your co-workers. Get rid of it and just use 0. A few more comments wouldn't be out of place either! (Trust me, you'll have quite a few WTF moments when you look back at that code next year)
[/quote]
Ya I realized that I am using -1 it seams more meaninfull when I look at it in the debugger.

Thanks a lot RobTheBloke for your comments.

Share this post


Link to post
Share on other sites
I'm not confident enough to comment on the source, because I really don't see how you can find a general solution this way... so I'll just think aloud and tell what I see..

Your constexpr should be just const?
Size of a single base class is the same in all cases.
Size of the derived class (MultipleInheritance) is same in all cases.

max_size is the size of base class plus size of the derived class. This size is then casted to be the pointer to the derived class, an invalid pointer. This might not matter, it's not used for access. MSDN says static_cast from derived to base is safe even without RTTI, given the types are really valid. (and they are verified by your static_assert).


I have to say the multiple inheritance is not useful. Even with standard RTTI I'd strongly advice against using it. It is _very_ rarely needed, and more elegant solutions can be done with composition and single inheritance. It might be better to just drop multiple inheritance if you're about to implement custom RTTI, and specially drop virtual inheritance.


But maybe you find this rather old ABI documentation useful how many compilers did stuff.

Share this post


Link to post
Share on other sites
Sorry, been sunning myself on holiday....


[quote name='RobTheBloke' timestamp='1316701121' post='4864682']
For what purpose? to call methods? or to access data? or both?

There are some really easy ways of doing this. Your approach isn't one of them ;)


Really i only know with offset. What are the other way. What I am doing is a reflection system with macros "trickery" as some people call it. Each metaclass has a list of parents (C3 linearization) and each parent have the class offset. I'll also have a list of exposed members of the class and those offset are relative to this offcourse. I am curious how else it can be acheived?[/quote]

1. There is the fubi style system. Effectively you export methods to the reflection system by adding a DLL export flag to each method. At the startup of your app, you read the PE header file (of your exe), get to the DLL export table, and use dbghelp.dll to build up a list of all the methods, their arguments, and their return types. This can be rather useful for auto generating script bindings at runtime (as well as auto generating stuff for DLL's you load), although it's it requires quite a bit of asm faffery, and would restrict you to get/set methods for data.

2. You can use something such as gcc xml to generate an xml description of the classes, their methods, and their variables. It's fairly easy to then auto gen the reflection system code. This has the advantage that you can start simple (single inheritance), and fairly easily build up additional support for virtual and multiple inheritance if it's ever needed. The resulting (generated) code also has the advantage of being much cleaner, much less hackier, and doesn't require a (manual) sprinkling of macros everywhere.

3. An alternative solution is the 'Maya style' system. You declare a class as type info, eg:


ClassDescription desc( "Bar", Bar::kClassType );
desc.inheritsFrom( Foo::kClassType );
desc.addInt("IntData", 0 /*default val */ );
desc.addVector3("Vec3Data", 0 /*default val */ );
/* etc */
system.registerType(desc);


When you initialise an object, you allocate a block of memory that will hold the data methods, and then use placement new to intialise the data blocks. In effect you've reversed the problem so that you specify the reflection info, and then generate the objects from that. It sounds hideous, but it's my preferred solution these days (for many other reasons I won't go into, and probably aren't relevant to your needs).


By the way would know know how to get the offset of a class that you derive virtually from ?
[/quote]

It's not something I've ever needed to do. If one of my co-workers virtually inherited any class in the codebase, chances are the rest of the dev team would be lining up to give them a kicking! In general I've only ever cared about what members and methods a class has. Getting access to the virtual base class doesn't help with that (unless you are trying to do everything with macros of course!). As i said in my last post, there are easier approaches ;)

Share this post


Link to post
Share on other sites
I believe if you're about to roll you'r own RTTI, you have to code for it. The casting trickery just isn't portable enough. RobTheBloke's first 2 solutions are really neat, but they tie you to a OS or compiler. I have to agree with the 3rd solution though.

I once did a (hobby) custom RTTI, it was by generating type classes for value classes, and then registering bunch of boost::functions into a registry. Then from that registry, by template typename traits the possible functions were fetched, and correct function was decided by custom logic for overload resolution. It got really complex really fast, and compiler errors were cryptic as hell. Not to talk about how it messed up the optimizer.

You can create little type-safe containers by using curiously recurring template pattern.

For more system wide solution, custom class ids (or enums) aren't a bad choice.

But really drop the virtual inheritance, nobody in their right mind uses it. Also multiple inheritance can be done better by composition. Single inheritance should suffice for custom RTTI.

By being against multiple inheritance, I mean I'm against implementation inheritance. Implementing multiple abstract base classes (interfaces) is of course good.

Share this post


Link to post
Share on other sites

RobTheBloke's first 2 solutions are really neat, but they tie you to a OS or compiler. I have to agree with the 3rd solution though.

The first solution is platform dependent, however both option 2 and 3 are portable and compiler independent. I think you might be confusing 'gcc xml' with 'gcc'. The latter being a compiler, the former being a parser.

Share this post


Link to post
Share on other sites
Thanks alot, it is very interesting to hear your ideas.

@ RobTheBloke:

I never implemented solution 1 and 2, but i work and implemented solution 3. To me because i have experience with the third solution it seams that solution 1 and 2 are more complexe, more involving and depend on a complexe third party code. What i like about solution 3 is that it can be very elegant and even less verbose that what you provided given that you use macros.

@ Codarki

constexpr is a new qualifier for c++0x but I realized that xcode allow the term but doesn't do anything at the moment. I think it will be a good optimization for the reflection system.

I also decided to drop virtual inheritance first because it is impossible to know classes offset in this case. (not that i know of) And that it is usually bad design and can be expressed in better terms. I'll keep the multiple inheritance because it is already working and I don't expect to cost more because of the constexpr qualifier. Everything is know at compile-time so it can be executed at compile time.

SO what i have is a reflection system and each class that need to be reflected add a macros to it's declaration to declare itself and its parents and macros to define the reflected member of the class.

thanks alot

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement