Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the meaning and use of the sentence "C++ compilers use a binary object layout"

While going through this C++ FAQ https://isocpp.org/wiki/faq/mixing-c-and-cpp#cpp-objs-passed-to-c I encountered the statement

Most C++ compilers use a binary object layout that causes this conversion to happen with multiple inheritance and/or virtual inheritance.

I could not understand the meaning and application of it. As per the C++ FAQ this object layout mechanism helps C++ compiler in the below mentioned check

In C++ it is easy to check if a Derived* called dp points to the same object as is pointed to by a Base* called bp: just say if (dp == bp) .... The C++ compiler automatically converts both pointers to the same type, in this case to Base*, then compares them. Depending on the C++ compiler’s implementation details, this conversion sometimes changes the bits of a pointer’s value.

Could any one help to understand the binary object layout of any popular C++ compilers and what possible changes and the corresponding mechanism for changes in the bits of a pointer’s value. and How it helps in comparing the pointers of Base/Derived classes.

Edit : Please explain why the below is also a valid statement.

NOTE: you must be especially careful when converting both to void* since that conversion will not allow either the C or C++ compiler to do the proper pointer adjustments! The comparison (x == y) might be false even if (b == d) is true:

like image 783
Krishna Oza Avatar asked Feb 11 '23 15:02

Krishna Oza


1 Answers

In this context, "binary object layout" means "how the binary data comprising the object is layed out in memory."

Consider this C++ code:

struct Left
{
  int ll;
};

struct Right
{
  int rr;
};

struct Derived : Left, Right
{
  int dd;
};

One possible way to organise these in memory (conceptually) is as follows:

+ Derived ----------------+
| + Left +  + Right +     |
| | ll   |  | rr    |  dd |
| +------+  +-------+     |
+-------------------------+

Of course, the class is just 3 ints, so with the above conceptual layout, the real layout would be just this:

+ Derived------+
| ll | rr | dd |
+--------------+

Now imagine this code:

Derived d;
Dervied *pd = &d;
Left *pl = &d;
Right *pr = &d;

pd points to the start of d, which is the same as the start of its ll member.

pl will point at the Left subobject of d. The start of Left is the start of the ll member. When comparing pl == pd, pd needs to be converted to type Left*. Remember that pd already points to the start of ll, so no change of the value of pd needs to happen. It's purely a conceptual conversion (a change of type).

pr points to the Right subobject of d. Since a Right object starts with the rr member, pr points to rr. Again, doing pr == pd requires pd to be converted to type Right*. The Right subobject of d starts with the rr member, but pd points to the address of the ll member. Threfore, the value (= bits) of pd must be changed by this conversion (incremented by the size of one int) to point to rr instead. Actually, exactly this conversion already happened once when converting &d from Derived* to Right* in order to initialise pr with it.

This should also explain why comparing in void* type doesn't work. Clearly, &d.ll != &d.rr, even though pl == pr.

like image 165
Angew is no longer proud of SO Avatar answered Apr 30 '23 16:04

Angew is no longer proud of SO