[comp.compilers] OOP & compact routines

ctl8588@rigel.tamu.edu (LAUGHLIN, CHET) (08/10/90)

Hello.  I am thinking about writing a rather complex program using C++;
however, I am concerned with the amount of code that I see being generated by
the compiler.  I am especially concerned with how compact the code will be to
handle many classes of similar objects that differ only in small ways when
actions are performed on them.

For example, If I was to code a routine that would generate output for three
types of objects it might look like this....

switch(curr_object) {
   case a:   printf("howdy");   break;     /* excuse the format...saving */
   case b:   printf("hello");   break;     /* space here */
   case c:   printf("hi");      break;
   case d:   printf("goodbye"); break;
  }

Now, in C++ I would have a,b,and c inherit the same characteristics from
some global class.  From the literature I've seen I am lead to believe that
all the code for an object is black boxed together...So I would see the
following effect produced by the compiler.

greeting(curr_object);        /* Tell object to say hi */
                              /* apologies for bad syntax...I've not */
.....                        /* actually programmed in C++ yet...*/

switch(curr_object) {         /* compiler translates it to this... */
   case a:  actiona();  break;
   case b:  actionb();  break;
   case c:  actionc();  break;
   case d:  actiond();  break;
  }


Someone please tell me that this doesn't happen.  This seems like a
mountain of overhead to me to perform any actions.

Apologies for any incorrect syntax/wrong usage of terms.

-Chet Laughlin
-- 
Send compilers articles to compilers@esegue.segue.boston.ma.us
{spdcc | ima | lotus| world}!esegue.  Meta-mail to compilers-request@esegue.

burley@world.std.com (James C Burley) (08/10/90)

In article <1990Aug09.180436.18715@esegue.segue.boston.ma.us> ctl8588@rigel.tamu.edu (LAUGHLIN, CHET) writes:

   Hello.  I am thinking about writing a rather complex program using C++;
   however, I am concerned with the amount of code that I see being generated by
   the compiler.  I am especially concerned with how compact the code will be to
   handle many classes of similar objects that differ only in small ways when
   actions are performed on them.

   greeting(curr_object);        /* Tell object to say hi */
				 /* apologies for bad syntax...I've not */
   .....                        /* actually programmed in C++ yet...*/

   switch(curr_object) {         /* compiler translates it to this... */
      case a:  actiona();  break;
      case b:  actionb();  break;
      case c:  actionc();  break;
      case d:  actiond();  break;
     }


   Someone please tell me that this doesn't happen.  This seems like a
   mountain of overhead to me to perform any actions.

Don't worry; "curr_object->greeting()" (rather than the notation you showed)
produces a direct call to the desired function, unless the function is
declared virtual.  Without "virtual", C++ calls the function specified by
what it knows at compile time about the type of "curr_object".  So it should
not be any different.

In your specific case, however, I suspect you would WANT to use "virtual",
which allows "late-binding".  This means that while C++ still has a concept
of which type "curr_object" is, when calling "greeting" for it, it allows
for the possibility that "curr_object" is, at run time, some other object
DERIVED from the base object (type of "curr_object" as known at compile time).
This has the EFFECT of your switch statement, but is actually implemented
(in most implementations I've heard of) using an indirect function call:

(*curr_object.some_offset)();

This is, on most architectures, faster than the switch statement with a
function call on each possible branch of the statement, and also smaller
in terms of code size.  It does, however, mean that for every virtual
("changeable for each derived class") function, there will be a pointer
maintained to the appropriate function.  This may be an a per-object
(instance) basis; or, as I think is the case with most C++
implementations, on a per-class basis, but in that case, the call is a bit
more complicated:

(*curr_object.class_pointer->some_offset)();

This would be a bit slower (but save data memory space), but I still think
it competes well with switch/dispatch on many architectures.

In case you're wondering, the non-virtual-laden function declaration I
first mentioned, providing "early binding", is not provided for in some
"pure" OOP languages like Smalltalk.  Conceptually, "late-binding" provides
for run-time polymorphism (which function actually gets invoked by an
operation depending on the run-time types of the operators involved).  As I
understand it, C++ defaults to early binding to provide the kind of
souped-up performance C programmers have come to expect.

You'll get better answers from others, I'm sure.  I haven't actually written
a single line of C++ yet, and only dabbled in Smalltalk.  (But I watch these
areas very closely as a reader of JOOP, C++ Report, Smalltalk books, other
theory and practice books, and by doing my own OOP-related language designs,
so I'm not a total neophyte :-)

James Craig Burley, Software Craftsperson    burley@world.std.com
-- 
Send compilers articles to compilers@esegue.segue.boston.ma.us
{spdcc | ima | lotus| world}!esegue.  Meta-mail to compilers-request@esegue.