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