Ancient history: OOPC 1988
table of contents: http://www.math.utah.edu/pub/tex/bib/toc/byte1980.html search my name "linowes" on that page
i was in good company in that issue-- Kernighan & Ritchie, Bjarne Stroustrup
article is here: http://www.krsaborio.net/research/1980s/88/880801_e.htm
looks like they OCR'd it or something.
Here's the full text (just in case that link goes away)
In Depth: The C Language It's an Attitude
There's a lot of extra coding, but you can do object-oriented programming in conventional C
Jonathan S. Linowes
BYTE
August 1, 1988
Object-oriented programming is not dependent on any given programming language; it's an attitude. Structured programming asks you to answer the questions, ''How is my data transformed?'' and ''What are my inputs and outputs?'' Object-oriented programming asks, ''What are the things I will be working with?'' and ''What do I expect these things to do?'' It is becoming a popular alternative to conventional structured techniques for organizing and thinking about programs.
OOPC is a mechanism for object-oriented programming using standard C. It's not a programming language in itself, nor is it a preprocessor. Rather, it's a collection of conventions and programming techniques that, if followed, give you many of the benefits of today's object-oriented programming languages--benefits such as data encapsulation, modularity, and inheritance.
Through OOPC, I introduce the concepts of object-oriented programming in a practical and familiar context, rather than complicating the issue with a new language and a new syntax. To demonstrate OOPC techniques in action, I have implemented the MyShape example adapted from The C++ Programming Language by Bjarne Stroustrup (Addison-Wesley, 1986), pages 213 to 221.
Object-oriented programming techniques are very good for prototyping applications. In fact, OOPC was developed for the MIT Media Laboratory UseIT project, a user-interface test-bed system. The UseIT system lets you easily build components of a user interface, modify an interaction technique (such as changing a pop-up menu into a pull-down), and quickly program entirely new objects. This was a fairly complex project with multiple programmers and required the ability to make quick code modifications. Using object-oriented programming techniques made the UseIT system easy to understand and modify. Objects as Virtual Devices You can think of object-oriented systems as a collection of independent virtual devices communicating with each other, each with its own internal structure. These virtual devices, called objects, consist of properties (private data) and methods (the operations applied to that data). The internal structure of an object is not accessible to any other object or program. This concept of data encapsulation is key to object-oriented programming, as it separates the object's implementation from its use.
Objects communicate with other objects through messages. You send messages to create new objects, to modify and inquire about an object's property values, and to request the object to perform specific actions. In a sense, messages are like op codes to the object. An object can send messages to any other object, including itself.
You create objects by instantiating classes. Classes act as templates for objects, which have a particular set of properties and methods. All instances of a given class have the same properties and methods, although the values of each property will vary.
The distinction between object classes and instances is analogous to the relationship between structure definitions and structure variables. For example, in C, struct sname open-brace ... close-brace; declares a template for structure sname, and struct sname varname; declares varname as an instance of sname.
Object classes are organized into a hierarchical taxonomy of subclasses and superclasses. With subclasses you can define increasingly specialized object types. Superclasses let you share generalizations among separate classes.
An object inherits all the properties and methods of its superclass. A class can distinguish itself from its superclass by assigning different default property values, substituting methods for specific messages, and adding new properties, methods, and messages.
When you send a message to an object, the object's message handler receives the message and checks for a local method for that message. If it has none, the message is forwarded to the object's superclass message handler, and the method there is used. If the superclass doesn't recognize the message, it sends the message along to its superclass, and so forth, until a method is found. The message is declared invalid if no method is found. Using OOPC Objects The OOPC programmer interface consists of a header file, the functions New and Send, and a library of message-handler functions. Message handlers receive messages and invoke the corresponding methods. Every object class in the object library has a message handler.
The header file, OOPC.H, contains common definitions for standard data types in OOPC, such as class message-handler function declarations and message definitions.
Instance objects are identified with a reference, or handle, of type Object, defined in OOPC.H, returned when a new instance is created. You must use this identifier whenever you reference the object--for example, when sending it messages. When you use the Object handle, applications need never know the internal structure of the object.
You create a new instance with the function New: object = New( handler ); where handler is the message-handler function for the class of the object you are creating. The function New returns the handle of the instance into whatever variable name you've specified for the object. When you create an object, New allocates memory for the object's properties and initializes this memory to default values. It returns a null object if it can't create the instance.
Objects are sent messages with the Send macro function: error = Send( object, message, ¶meters, &result ); where object receives the message, message enumerates it, and the last two arguments are pointers to the parameter and result blocks whose contents depend on the particular message specified (they could be null). The function Send returns an error code (0=ok).
A message is actually just an enumerated integer type, such that each message has a unique value. Some examples include MSG-SET-RECTANGLE-p, which sets a rectangle's corner coordinates; MSG-ASK-NORTH-r, which requests the top-center point of a rectangle; and MSG-DRAW-p, which tells the object to draw itself. The name extensions -p, -r, and -pr remind you that the object requires a parameter block, result block, or both, along with the message.
When a message requires parameters, it has an associated data structure for stuffing the parameter values, named, by convention, message-Param. For example, Msg-Draw-Param is the parameter block for the MSG-DRAW-p message (Msg-Draw-Param specifies the screen on which to draw the object). Similarly, results are returned in the structure message-Result. For example, structure Msg-North-Result contains the point returned by MSG-NORTH-r. These parameter and result blocks are the same regardless of which object receives the message.
Unfortunately, because OOPC requires that you be able to send different data structures to the same function, you can't perform type checking to verify that the correct blocks are specified for a given message. However, the message-naming convention, combined with the parameter- and result-block structures, form a satisfactory interim solution without a preprocessor. The naming conventions make it easier to visually verify the code, and the C compiler will verify the types of each specific parameter/result field within the blocks. An Example The class hierarchy in figure 1 shows the superclasses and subclass used for the MyShape example. The class Rectangle includes coordinates defining its two corners. MyShape is a subclass of Rectangle that implements a picture of a face. It uses the Rectangle properties to define its border and adds properties for the eyes and mouth (defined as horizontal lines). Messages and methods regarding the border of a MyShape object are inherited from its superclass, Rectangle; other messages are handled by the local method, MyShape. In OOPC, all objects are subclasses of class Common.
The main program, shown in listing 1, creates three shapes--a rectangle, a horizontal line, and an instance of MyShape (a face)--and draws them on the screen. Then, the line is stacked on top of the face, the rectangle is stacked on top of the line, and the drawing is refreshed.
When an instance of a class is created, its properties are set to default values, leaving it to the application to set them to desired values. For example, new rectangles are created at (0,0)-(0,0), which is not very useful. The new object must then be set, such as at (0,0)-(10,10), as in the example.
The MyShape example shows how you can write procedures that manipulate objects without knowledge of the objects they will be manipulating. For example, the stack-on-top function rearranges arbitrary objects, as long as they understand the messages MSG-NORTH, MSG- SOUTH, and MSG-MOVE, to get their top-center and bottom-center points and to change their positions. Similarly, the function refresh-shapes sends MSG- DRAW to the objects in the display list, without knowing anything about the objects there. In fact, you can code and compile these functions before writing the object classes they are manipulating. Implementing OOPC Objects OOPC uses an #include chaining technique that requires that each object be compiled separately. Each class is made of at least two source files: class.C and class.P. The .C file contains the message handler and individual methods. The .P file contains the class properties. You must include the header file OOPC.H at the top of every source (.C) file in addition to the object's class property file.
Using nested file inclusion, the .P files provide the mechanism for property inheritance. For example, listing 2 shows the Rectangle-Object data structure. The fields of this structure are included from the RECTANGLE.P file. The first statement of the file is another include file, SHAPE.H, containing the properties from Rectangle's superclass.
Superclass properties appear in memory before subclass properties, as shown in figure 2, which facilitates method inheritance. When a message is deferred by a class to its superclass, the object's handle is passed. Since this handle is actually a pointer to the object's property data, the superclass can access the properties it knows of, yet need not (and should not) access the subclass properties.
Each object class has a single message-handler function having the same name as the object itself. Thus, rectangles have a message handler called Rectangle.
The handler is a ''switch'' statement with cases for each message recognized locally by the class, calling the corresponding method--a static local function. If the message handler does not recognize the message, the message is forwarded up to the superclass message handler. Listing 2 is an excerpt of the file RECTANGLE.C, showing the chain of inheritance an unrecognized message will follow.
Even if an object has a method for a message, it can still inherit the superclass method and then augment the result. In the initialization method, for example, the object first inherits its superclass method to initialize the superclass properties and then initializes its own properties.
Because the topmost class is Common, the first property of every object is a pointer to the object's message-function handler. The Send macro function takes advantage of this fact by simply accessing the first field of the object. The following is an excerpt from OOPC.H showing how Send is implemented: typedef int (*Functionp)(); typedef struct open-brace Functionp
dispatch; close-brace *Object; #define Send(obj,msg,param,
result)?((*(obj- greater than dispatch))(obj,msg,
param,result)) To implement Send, I first define the data type Functionp, which is a pointer to a function. Then I define a data structure that declares as its member an item called dispatch, which is of type Functionp. Finally, I give this structure the tag *Object, so that Object is a data type that is a pointer to a structure whose first member is a pointer to a function. In the Send macro, obj is of type Object, so obj- greater than dispatch will fish out the pointer to the desired function. The rest of this line executes the function.
Finally, the function New is an interface for sending a MSG-NEW message to a dispatch function. New creates an instance for an object class that you can then use with the function Send. typedef struct open-brace Object object; close-brace
Msg-New-Result; Objec-New-Result new;
(*dispatch)( NULL,
MSG-NEW-r, NULL, &new );
return new.object; close-brace Limitations of OOPC As you can see, there is a lot of extra coding you must tend to yourself, such as setting up the include chain and making sure that you pass the correct parameters to an object. These are details that an object-oriented language can manage for you. Object-oriented languages differ in their support for memory management.
The advantage of using a conventional language is that it doesn't require the additional investment of buying and learning a new language. Plus, it's code-compatible with any existing libraries and tools you currently use. Editor's note: The source code for the OOPC object classes and the MyShape example are available in a variety of formats. See page 3 for further details.
-- Jonathan S. Linowes is a founding partner of Sirlin Computer Corp. in Hudson, New Hampshire, specializing in database and computer graphics systems. He can be contacted on BIX as ''editors.''
Figure 1: Class hierarchy in the MyShape example.
Figure 2: Memory organization of MyShape class properties.
Listing 1: OOPC code for the MyShape example program. This code creates several shapes--a rectangle, a line, and a face (MyShape)--and then stacks them on top of each other.
Listing 2: The chain of inheritance for the rectangle class. If a rectangle object is not able to recognize a message, the Base macro will pass it up to a superclass, Shape, to see if the message is defined there. If Shape can't decode the message, it will pass it up to Common. If Common can't understand the message, an error is returned.
Copyright 1988 McGraw-Hill, Inc
There are no comments attached to this item.



