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