469,126 Members | 1,276 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,126 developers. It's quick & easy.

C++ Object model

From 'Inside the C++ Object Model' by 'Stanley B. Lippman'

'The primary strength of the C++ Object Model is its space and runtime
efficiency. Its primary drawback is the need to recompile unmodified
code that makes use of an object of a class for which there has been
an addition, removal, or modification of the nonstatic class data
members.'

Here I can understand the strength of C++ object model but can anybody
elaborate the primary drawback of it.
Jun 27 '08 #1
14 1610
sumsin <su*******@gmail.comwrites:
From 'Inside the C++ Object Model' by 'Stanley B. Lippman'

'The primary strength of the C++ Object Model is its space and runtime
efficiency. Its primary drawback is the need to recompile unmodified
code that makes use of an object of a class for which there has been
an addition, removal, or modification of the nonstatic class data
members.'

Here I can understand the strength of C++ object model but can anybody
elaborate the primary drawback of it.
Imagine you have:

class Widget :public Object {
private:
Rect bounds;
public:
virutal Rect& getBounds(void);
};

and a thousand of subclasses of Widget.
And suddenly, the user requets that the widgets may be oriented by
some angle on the screen. So you add a field:

class Widget :public Object {
private:
Rect bounds;
float angle;
public:
virutal Rect getBounds(void);
virutal float getAngle(void);
};

and lo, you have to recompile all the thousand of subclasses, because
now the field layout has changed, for all these classes.
Contrast that with CLOS:
(defclass widget () ((bounds :initarg :bounds :accessor bounds)))
#1=#<STANDARD-CLASS WIDGET>
(defclass button (widget) ((title :initarg :title :accessor title)))
#1=#<STANDARD-CLASS BUTTON>
(defvar *button-1* (make-instance 'button :bounds #(10 20 110 40) :title "Test"))
*BUTTON-1*
(inspect *button-1*)
#<COMMON-LISP-USER::BUTTON #x000334202008>: standard object
type: COMMON-LISP-USER::BUTTON
0 [BOUNDS]: #<ARRAY T (4) #x000334201F10>
1 [TITLE]: "Test"
INSPECT-- type :h for help; :q to return to the REPL ---:q

We created the superclass and a subclass, and made an instance of the subclass.
Suddenly, we modify the superclass:
(defclass widget () ((bounds :initarg :bounds :accessor bounds) (angle :initarg :angle :accessor angle :initform 0.0)))
WARNING: DEFCLASS: Class BUTTON (or one of its ancestors) is being redefined, instances are obsolete
#1=#<STANDARD-CLASS WIDGET :VERSION 1>

Of course new instances of the subclasses will take into account the
changes (nothing has to be done about the subclasses themselves):
(defvar *button-2* (make-instance 'button :bounds #(10 20 110 40) :angle (/ pi 3) :title "At an angle"))
*BUTTON-2*
(inspect *button-2*)
#<COMMON-LISP-USER::BUTTON #x00033426C9A0>: standard object
type: COMMON-LISP-USER::BUTTON
0 [BOUNDS]: #<ARRAY T (4) #x00033426C878>
1 [ANGLE]: 1.0471975511965977461L0
2 [TITLE]: "At an angle"
INSPECT-- type :h for help; :q to return to the REPL ---:q
But what's more, the old instances of the subclasses have been updated too:
(inspect *button-1*)
#<COMMON-LISP-USER::BUTTON #x000334202008>: standard object
type: COMMON-LISP-USER::BUTTON
0 [BOUNDS]: #<ARRAY T (4) #x000334201F10>
1 [ANGLE]: 0.0
2 [TITLE]: "Test"
INSPECT-- type :h for help; :q to return to the REPL ---:q
>

In CLOS, it's even possible to define (and compile) subclasses before having defined the superclass:
(defclass window (view) ())
#1=#<STANDARD-CLASS WINDOW :INCOMPLETE>
(defclass view (widget) ())
#1=#<STANDARD-CLASS VIEW>
(make-instance 'window :bounds #(0 0 512 342))
#<WINDOW #x0003342E24C0>
(inspect *)
#<COMMON-LISP-USER::WINDOW #x0003342E24C0>: standard object
type: COMMON-LISP-USER::WINDOW
0 [BOUNDS]: #<ARRAY T (4) #x0003342E2400>
1 [ANGLE]: 0.0
INSPECT-- type :h for help; :q to return to the REPL ---:q
>
--
__Pascal Bourguignon__
Jun 27 '08 #2
Pascal J. Bourguignon wrote:
In CLOS, [...]
That's comparing apples and oranges. C++ is basically a macro assembler,
just like C. It only has more features than C but the basic mode of
operation is the same. Once the code is compiled, the language is gone.
However, it works pretty well in that role (imho). You can't compare
that to a dynamic language (lisp, smalltalk?, obj-C) and complain how
inflexible that model is. One of the stated goals of C++ is to remain
compatible with C's language model as a portable assembler -- not to
introduce any (substantial) language middle layer between the program
and the hardware. For that, it works quite ok but of course this brings
some limitations (aswell as advantages).
Jun 27 '08 #3
On Jun 11, 4:56 pm, Michael DOUBEZ <michael.dou...@free.frwrote:
sumsin a écrit :
From 'Inside the C++ Object Model' by 'Stanley B. Lippman'
'The primary strength of the C++ Object Model is its space and runtime
efficiency. Its primary drawback is the need to recompile unmodified
code that makes use of an object of a class for which there has been
an addition, removal, or modification of the nonstatic class data
members.'
Here I can understand the strength of C++ object model but can anybody
elaborate the primary drawback of it.

The drawback is in terms of impact upon change: if you change the
definition of a class (its members) ,you have to recompile every piece
of code that uses this class even if it has not been modified.

A solution to this drawback is the pimpl idiom.

--
Michael
You mean if I add any:
- static data member and/or
- non-static member function and/or
- static member function and/or
- virtual member function, even then also the unmodified code will
recompile!!

But that is not the meant of statement I think.
Jun 27 '08 #4
On Jun 11, 4:54 pm, p...@informatimago.com (Pascal J. Bourguignon)
wrote:
sumsin <sumsin...@gmail.comwrites:
From 'Inside the C++ Object Model' by 'Stanley B. Lippman'
'The primary strength of the C++ Object Model is its space and runtime
efficiency. Its primary drawback is the need to recompile unmodified
code that makes use of an object of a class for which there has been
an addition, removal, or modification of the nonstatic class data
members.'
Here I can understand the strength of C++ object model but can anybody
elaborate the primary drawback of it.

Imagine you have:

class Widget :public Object {
private:
Rect bounds;
public:
virutal Rect& getBounds(void);

};

and a thousand of subclasses of Widget.

And suddenly, the user requets that the widgets may be oriented by
some angle on the screen. So you add a field:

class Widget :public Object {
private:
Rect bounds;
float angle;
public:
virutal Rect getBounds(void);
virutal float getAngle(void);

};
but suppose if user request only for some member functions not for
data members, in that case what will happen? Do we still need to
recompile thousand of the subclasses?
and lo, you have to recompile all the thousand of subclasses, because
now the field layout has changed, for all these classes.
Jun 27 '08 #5
sumsin <su*******@gmail.comwrites:
but suppose if user request only for some member functions not for
data members, in that case what will happen? Do we still need to
recompile thousand of the subclasses?
If it's not a virtual member, then it should be more or less ok not to
recompile, but if it's a virtual member you will have to recompile
because it changes the layout of the vtable, and the numbering of all
the other virtual members following in that class, and in the
subclasses.

--
__Pascal Bourguignon__
Jun 27 '08 #6
On Jun 11, 7:04 pm, p...@informatimago.com (Pascal J. Bourguignon)
wrote:
sumsin <sumsin...@gmail.comwrites:
but suppose if user request only for some member functions not for
data members, in that case what will happen? Do we still need to
recompile thousand of the subclasses?

If it's not a virtual member, then it should be more or less ok not to
recompile, but if it's a virtual member you will have to recompile
because it changes the layout of the vtable, and the numbering of all
the other virtual members following in that class, and in the
subclasses.

--
__Pascal Bourguignon__
Ok, apart from the sub-classing concept, let we talk in some other
way.
Supose we have two classes say 'foo' and 'bar'.
And class bar uses some instance of class foo then what will happen?

I mean in what condition the class bar will recompile?
Jun 27 '08 #7
sumsin <su*******@gmail.comwrites:
On Jun 11, 7:04 pm, p...@informatimago.com (Pascal J. Bourguignon)
wrote:
>sumsin <sumsin...@gmail.comwrites:
but suppose if user request only for some member functions not for
data members, in that case what will happen? Do we still need to
recompile thousand of the subclasses?

If it's not a virtual member, then it should be more or less ok not to
recompile, but if it's a virtual member you will have to recompile
because it changes the layout of the vtable, and the numbering of all
the other virtual members following in that class, and in the
subclasses.

--
__Pascal Bourguignon__

Ok, apart from the sub-classing concept, let we talk in some other
way.
Supose we have two classes say 'foo' and 'bar'.
And class bar uses some instance of class foo then what will happen?

I mean in what condition the class bar will recompile?
It's the same, since the selection of the method to be called is done
at the call site, with a virtual method index, and that's this virtual
method index that needs to be globally computed.

Basically, o->m() is implemented as o->vtable[ vindex of method m in class o ]();
and the vindexes are computed so that whatever the subclass the same
index is used for the same virtual method.

The vindex is a literal that is hard coded in all call sites.

--
__Pascal Bourguignon__
Jun 27 '08 #8
On Jun 11, 7:59 pm, p...@informatimago.com (Pascal J. Bourguignon)
wrote:
sumsin <sumsin...@gmail.comwrites:
On Jun 11, 7:04 pm, p...@informatimago.com (Pascal J. Bourguignon)
wrote:
sumsin <sumsin...@gmail.comwrites:
but suppose if user request only for some member functions not for
data members, in that case what will happen? Do we still need to
recompile thousand of the subclasses?
If it's not a virtual member, then it should be more or less ok not to
recompile, but if it's a virtual member you will have to recompile
because it changes the layout of the vtable, and the numbering of all
the other virtual members following in that class, and in the
subclasses.
--
__Pascal Bourguignon__
Ok, apart from the sub-classing concept, let we talk in some other
way.
Supose we have two classes say 'foo' and 'bar'.
And class bar uses some instance of class foo then what will happen?
I mean in what condition the class bar will recompile?

It's the same, since the selection of the method to be called is done
at the call site, with a virtual method index, and that's this virtual
method index that needs to be globally computed.

Basically, o->m() is implemented as o->vtable[ vindex of method m in class o ]();
and the vindexes are computed so that whatever the subclass the same
index is used for the same virtual method.

The vindex is a literal that is hard coded in all call sites.

--
__Pascal Bourguignon__
Ok, lets we don't talk about virtual member functions. So if I add any
normal function then will it force the recompilation?
Jun 27 '08 #9
In article <066658ab-70b5-4d70-9a51-01dbd471d5c3
@r37g2000prm.googlegroups.com>, su*******@gmail.com says...

[ ... ]
Ok, lets we don't talk about virtual member functions. So if I add any
normal function then will it force the recompilation?
Most of these questions are almost entirely theoretical, and even in
theory don't have much in the way of solid answers.

In practice, you use make, which only looks at the last modified time on
the _file_ -- if you make _any_ change in a header (even something like
adding a comment that can't possibly have any real effect) it'll re-
compile everything that you've said depends on that file, unless you use
touch (or something similar) to prevent if from doing so.

Keeping track of which real changes to code require recompiling which
dependent code is next to impossible for anything but the most trivial
program -- that's why make exists. Worse, you only get one "last
modified" date per file, where the header dependency has an almost
arbitrary level of complexity.

The result is that for most practical purposes, when you change a
header, you really have two possibilities. The first possibility is that
you've made a truly trivial change that can't possibly affect anything
else. If so, you use touch to prevent other code from being recompiled.
Otherwise, you've made a change that might mean something, and you let
make sort out what needs to be recompiled. In the process you live with
the fact that it's only looking at things on a file-by-file basis, so it
can (and often will) recompile things that aren't really affected by the
change you made. While adding finer granularity to the process could
certainly help some things under some circumstances, to do it you'd just
about have to build a parser for each supported language into the make
tool.

Realistically, unless you're going to try to create a new development
environment that works quite a bit differently from most (any?)
currently in use, there's not a lot of point in trying to explore the
subject in drastically more detail. Even if you are going to try to do
such a thing, I'm pretty sure there are a lot more productive ways to
spend your time than on what's likely to be a rather minimal improvement
in build times.

--
Later,
Jerry.

The universe is a figment of its own imagination.
Jun 27 '08 #10
Jerry Coffin wrote:
In article <066658ab-70b5-4d70-9a51-01dbd471d5c3
@r37g2000prm.googlegroups.com>, su*******@gmail.com says...

[ ... ]
>Ok, lets we don't talk about virtual member functions. So if I add any
normal function then will it force the recompilation?

Most of these questions are almost entirely theoretical, and even in
theory don't have much in the way of solid answers.

In practice, you use make, which only looks at the last modified time on
the _file_ -- if you make _any_ change in a header (even something like
adding a comment that can't possibly have any real effect) it'll re-
compile everything that you've said depends on that file, unless you use
touch (or something similar) to prevent if from doing so.
This isn't the only reason. This compilation issue is caused by the
object model itself. Nobody's really touched on this. Imagine:

class Object
{
int x;
public:
bool f() const; // implementation in .cpp file
};

Now, you need to change the implementation of Object so that x is no
longer an int. You're not actually changing ANYTHING about the
interface; you're not adding functionality. The changes to f() occur
inside the .cpp file so that it does what it did but with x being a
double now. No other object should need to know that x has changed its
type...you've followed correct design and there is no coupling in your
design here.

The problem is that any time you do this:

Object obj;

The compiler needs to know what Object looks like: how much memory to
allocate on the stack, does it have a virtual function table, etc...

Therefor, if you change x to a double now all areas of code that use the
Object class in any way much be recompiled. This isn't just a make
issue, it really does need to recompile all that code.

The pimpl does fix this problem by hiding the internals of the class in
an opaque type but this adds heap allocation to the object and that can
be unwanted. There are many performance issues associated with heap
allocation.

The only way that the language could fix this problem that I can think
of would be to do exactly that: implement class objects as pimpls and
heap allocate everything. This "problem" is actually an advantage that
C++ has over other languages.

Personally, I feel that the pimpl idiom is actually more important as a
fix to exception safety issues and the compilation time save is just a
bonus. There are several instances where exception safety can only be
provided through the use of a pimpl.
Jun 27 '08 #11
On Jun 11, 7:17 pm, Jerry Coffin <jcof...@taeus.comwrote:
In article <066658ab-70b5-4d70-9a51-01dbd471d5c3
@r37g2000prm.googlegroups.com>, sumsin...@gmail.com says...
[ ... ]
Ok, lets we don't talk about virtual member functions. So if
I add any normal function then will it force the
recompilation?
Most of these questions are almost entirely theoretical, and
even in theory don't have much in the way of solid answers.
In practice, you use make, which only looks at the last
modified time on the _file_ -- if you make _any_ change in a
header (even something like adding a comment that can't
possibly have any real effect) it'll re- compile everything
that you've said depends on that file, unless you use touch
(or something similar) to prevent if from doing so.
Keeping track of which real changes to code require
recompiling which dependent code is next to impossible for
anything but the most trivial program -- that's why make
exists. Worse, you only get one "last modified" date per file,
where the header dependency has an almost arbitrary level of
complexity.
I believe that Visual Age did this somewhat differently. I
think it actually did a partial compile of the code when you
checked it, and also kept track of exactly what aspects each bit
of client code depended on, and only recompiled the parts where
recompilation was really necessary. I think it also triggered
this recompilation automatically when you checked the header in,
so that it always had up to date versions in its data base. (It
didn't keep the code, object files, etc. in the file system, but
in a data base.) On the other hand, it did enough things
differently that I don't think it was really conform---in
particular, you could forget all of the includes, and it would
still compile.

I don't know if Visual Age is still available for C++. (IBM
bought Rational, and I think that there C++ compiler today
derives from the old Rational Apex C++, and not Visual Age C++.
But I'm far from sure.)

For all other systems I've heard of, your description fits the
bill. (And theoretically: according to the standard, any change
which changes the token sequence or the name binding in the
class in any way requires recompilation of all components which
include the class definition, according to the one definition
rule.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jun 27 '08 #12
On Jun 11, 7:28 pm, Noah Roberts <u...@example.netwrote:
Jerry Coffin wrote:
In article <066658ab-70b5-4d70-9a51-01dbd471d5c3
@r37g2000prm.googlegroups.com>, sumsin...@gmail.com says...
[ ... ]
Ok, lets we don't talk about virtual member functions. So
if I add any normal function then will it force the
recompilation?
Most of these questions are almost entirely theoretical, and
even in theory don't have much in the way of solid answers.
In practice, you use make, which only looks at the last
modified time on the _file_ -- if you make _any_ change in a
header (even something like adding a comment that can't
possibly have any real effect) it'll re- compile everything
that you've said depends on that file, unless you use touch
(or something similar) to prevent if from doing so.
This isn't the only reason. This compilation issue is caused
by the object model itself. Nobody's really touched on this.
That's what everyone has been talking about. Change a
non-static member, and you have to recompile everything.
Imagine:
class Object
{
int x;
public:
bool f() const; // implementation in .cpp file
};
Now, you need to change the implementation of Object so that x
is no longer an int. You're not actually changing ANYTHING
about the interface; you're not adding functionality. The
changes to f() occur inside the .cpp file so that it does what
it did but with x being a double now. No other object should
need to know that x has changed its type...you've followed
correct design and there is no coupling in your design here.
This is a known "weakness" of C++. It's the price we pay for
being able to allocate objects on the stack (and still use the
usual tools for linking, etc.), rather than being forced to
allocate everything dynamically.

If you really, really know the compiler, you can sometimes play
tricks (although I wouldn't do so except for quick and dirty
tests). If you change the int to a float, for example, there
are a number of compilers where you can get away with
back-dating the header and reinvoking make. I actually did this
once: faced with a six hour build time if I followed the rules,
and knowing that the only data in the class was a pointer, and
that all I'd done was add some private non-virtual functions and
changed the type of the pointer (in fact, the entire
representation of the class was changed, but that was hidden
behind the pointer), I back-dated the header, rebuilt, and ran a
few tests. After they worked, I touched the header again,
started the build, and went home (it was late Saturday
afternoon).

This requires pretty good knowledge about the compiler, however,
and even then, shouldn't be done in a production
environment---it's not worth the risk.

[...]
Personally, I feel that the pimpl idiom is actually more
important as a fix to exception safety issues and the
compilation time save is just a bonus. There are several
instances where exception safety can only be provided through
the use of a pimpl.
I presume you are talking globally about exception safety; using
the pimpl idiom doesn't affect exception safety in the
constructors, but it certainly makes it easier in the assignment
operator.

As for compile times... You must work on very small projects if
it doesn't make a significant difference. In the case
mentionned above (admittedly, some time ago, on significantly
older equipment), it made a difference between something like
six hours, and less than a minute. (The total code base on the
project was something over a million lines, and every single
module used the class I'd just modified.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jun 27 '08 #13
James Kanze wrote:
As for compile times... You must work on very small projects if
it doesn't make a significant difference.
That depends on your definition of "small". You seem to like to use
different definitions of words than their common use so I have no idea.
Jun 27 '08 #14
In article <9c7b56ba-6d57-4cf2-8999-6900c4bbf526
@e53g2000hsa.googlegroups.com>, ja*********@gmail.com says...

[ ... how make works ]
I believe that Visual Age did this somewhat differently. I
think it actually did a partial compile of the code when you
checked it, and also kept track of exactly what aspects each bit
of client code depended on, and only recompiled the parts where
recompilation was really necessary. I think it also triggered
this recompilation automatically when you checked the header in,
so that it always had up to date versions in its data base. (It
didn't keep the code, object files, etc. in the file system, but
in a data base.) On the other hand, it did enough things
differently that I don't think it was really conform---in
particular, you could forget all of the includes, and it would
still compile.
True, VAC++ worked differently, but the important point is that it still
automated the process. It was enough smarter about its automation to do
a better job of minimizing recompilation that wasn't really required --
but you still just did your editing and the build system figured out
what needed to be recompile based on what you did. Trying to manually
track what changes forced recompilation of what dependent source was
still entirely unnecessary.

--
Later,
Jerry.

The universe is a figment of its own imagination.
Jun 27 '08 #15

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

4 posts views Thread by Davíð Þórisson | last post: by
15 posts views Thread by randyr | last post: by
2 posts views Thread by oh.i.love.spam | last post: by
reply views Thread by =?Utf-8?B?SmVhbi1GcmFuY29pcyBCcmV0b24=?= | last post: by
3 posts views Thread by H. S. Lahman | last post: by
23 posts views Thread by tonytech08 | last post: by
reply views Thread by zhoujie | last post: by
reply views Thread by Mortomer39 | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.