[comp.lang.c++] C

daveb@geac.UUCP (David Collier-Brown) (04/27/88)

In article <1988Apr24.004842.3251@utzoo.uucp>, henry@utzoo.uucp (Henry Spencer) writes:
|  Remember that there are two separate issues here:  what you write, and the
|  code the compiler generates for it.  With modern compilers, the two are
|  often quite different.

In article <2606@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
| This may be highly undesireable in a language used for programming operating
| systems, where the programmer needs something approaching WYSIWYG capability.
| C is eminently such a language.

  Well, C is probably the best current choice for that language (I
learned on FORTRAN II: now **there's** a WYSIWYG language).
  Regrettably, C is a high-level language, and certain optimizations
are perfectly reasonable if one is a human writing non-special code
in it.  They are less reasonable if you are generating C from a
higher-level language, and much less so if you are writing device
drivers.

  This dichotomy has been discussed before (but I forget who to
cite! mea culpa).

  Therefor I raise the question:


			Do we need C--?


  C-- is WYSIWYG C, and is explicitly intended for the following
purposes:
	1) writing critical machine-specific functions,
	2) as a target of source-to-source transformers (including
	   certain classes of optimizers),
	3) as a target for VHLLs, including C++, database
	   embedded-SQL processors, ML and Ada(tm).

  I'm working with people using I-SQL, and want to implement an ML:
I need predictable behavior, but can live with some forms of
optimization (notably code motions, constant folding and the like).
  In fact, I could handle any (correctness preserving!)
source-to-source transformation so long as I could ask for the
generated output to read.

  What say others?

--dave (ah, if only I had my 1130/1800 back) c-b
-- 
 David Collier-Brown.                 {mnetor yunexus utgpu}!geac!daveb
 Geac Computers International Inc.,   |  Computer Science loses its
 350 Steelcase Road,Markham, Ontario, |  memory (if not its mind) 
 CANADA, L3R 1B3 (416) 475-0525 x3279 |  every 6 months.

langley@bigbird.rtp.dg.com (Mark L Langley) (02/15/90)

I enjoyed Peter Kriens CO2 article that was posted recently to
this group.  <406@wmt.uucp>

In it, he proposed a clever way to solve, in C, a chronic problem
that does indeed arise in C++.  Namely, that the abstraction for
data hiding does not hide anything from the compiler, consequently
changes to private member definitions require recompilation of code
depending only on public definitions.  In fact, I would add to that
a suggestion that the current idea of visibility is flawed, because a 
field is ambiguous if it collides with something that is invisible.  

Consider

  struct Base1 { public: int MyData; };

  class Base2 { private: int MyData; };

  class Derived : public Base1, public Base2 { };

  foo() { Derived d; d.MyData; }

This means that every time you change the private implementation of a class,
you may affect descendant classes adversely.  This is quite antithematic.

But unlike Mr. Kriens, I do not favor throwing the baby out with the
bathwater.  With much less work, you can apply this basic idea in C++,
at no additional run-time cost.  (Note that he had to do all his
member look-ups at run-time.)

Further, Mr. Kriens' article overlooked some clear advantages that C++
holds over C.  For example operator and function overloading, more 
rigorous type checking, and pass by reference.  Furthermore, using
only the CO2 techniques in C, you are working with only half the 
primitives that you get with C++!  Namely, the class metaphor involves 
methods and instance variables.  CO2 is a step back to flavors (with
its getable-setable instance variables) because it only has methods.

Let me show you how you can do CO2 more profitably in C++, using
C++ features. 

Consider the following schematic for an object.  An object, ship, for
example, has ship.h, shipI.h and shipI.c.  In this scheme you can 
completely hide the definitions/declarations of the private members 
by encapsulating them in their own stylized class (shipI).  By using 
references you can allow read-write, read-only or write-only access 
through a variables interface, and completely hide the implementation.

// ship.h
   struct ShipI;
   
   class Ship {
    private:
     ShipI *shipI;
    public:
     int &x, &y, &velocity;
     Ship();
     void print();
     void update();
   };

// shipI.h
   class Ship;
   
   class ShipI {
     friend Ship;
     operator ShipI *();	// How to make a ShipI from a *ShipI
     ShipI(Ship *);		// How to construct a ShipI
     //
     // write through instance variables
     int x, y, velocity;
     //
     // completely private instance variable
     int time;
   };

// shipI.c
   #include "ship.h"
   #include "shipI.h"
   #include <stdio.h>
   
   Ship::Ship() :
        shipI(ShipI(0)),
        x(shipI->x),
        y(shipI->y),
        velocity(shipI->velocity)
   {
     x = 10;
     y = 20;
     velocity = 100;
     shipI->time = 1;
     }

   void Ship::print() {
     int factor = velocity * shipI->time;
     printf("x=%d y=%d velocity=%d\n",
   	 x*factor,
   	 y*factor,
   	 velocity);
   }
   
   void Ship::update() {
     shipI->time ++;
     }
   
   ShipI::ShipI(Ship *) {
     printf("Creating Ship(I)\n");
   }
   
   ShipI::operator ShipI*() { return new ShipI(0); }

The only drawback is that access to local class members from member functions 
must be named explicitly.  That is, to access a private instance variable 
from ship (that is really in shipI) you must access it as shipI->name.
(See Ship::update for an example of this)

By the way, I just noticed that I put in an old version of ship, 
and the above has a (subtle?) bug in it.  It calls the constructor
for Ship twice every time it creates a ship. This is disastrous
if there are derivative classes of Ship. Do you see why?

Mark Langley
langley@dg-rtp.dg.com

  The owl of Minerva flys only at dusk...
    - Hegal