July 92 - Software Review - Component Workshop and OODLs
Software Review - Component Workshop and OODLs
Jeff Alger
This year's WWDC was by most accounts pretty shy of substance. Last year we had System 7 (at last) and QuickTime, for example, while this year we were told that AppleScript would go beta sometime in the fall; a cross-platform MacApp strategy would be announced a month later at PC Expo; sometime in 1993 QuickDraw GX would hit the streets, sometime; somewhere Taligent will need us . . . in short, lots of promises but not much delivered. It was against this backdrop that BAMADA held its monthly meeting on Thursday night of that week. Despite holding the meeting off-site in Cupertino, rather than in San Jose near the convention, the auditorium was packed to overflowing, and it is a good bet that almost everyone there came to hear Steve Weyl expand on Apple's cross-platform strategy or Larry Tesler talk about Dylan, Apple's new object-oriented dynamic language (OODL). Also on the program were two tiny companies, Component Software demonstrating their Component Workshop development environment and Nick Nallick demonstrating his view editor, Ad Lib. Sandwiched in between the heavyweights, the little guys stole the show. Looking around after Component Software's presentation, it was clear to me that only a vague sense of protocol prevented a standing ovation. This, from a company that has yet to ship a product and has only nine employees.
This article was prompted by the audience response to Component Workshop and, more generally, the sudden upsurge of interest in OODLs following Larry Tesler's keynote talk at the MADA conference in February. In the next issue of FrameWorks I will provide an in-depth review of the tools and class library of Component Workshop. This article is devoted to the particular implementation of C++ in Component Workshop and even more to the issues and position of this kind of tool in the world of OODLs.
What's An OODL?
Despite its C++ syntax, Component Workshop is best classified as what Tesler has coined an "object-oriented dynamic language," or OODL. It belongs in the same class as Smalltalk and CLOS, environments in which it is all but impossible during development to separate the idea of running your application from the development environment itself. Add a class? Just a few clicks and keystrokes and it is ready to use. Add a method? Type in the source and a couple of seconds later it is compiled and linked. Change something? Again, the only real time taken is in typing the source. And without a Quadra with 64M of RAM disk; all of the modern OODLs work just fine on much smaller, IIci-class machines.
It is easy to get swept away at this point and equate OODLs to faster compiling and linking. That is certainly attractive and probably in itself explains much of the current interest in OODLs: it is not the language we want, just faster compile-link turnaround. But that is only scratching the surface of the power of an OODL: The entire paradigm of the development environment is fundamentally different. I was first captivated by Macintosh Common Lisp three years ago when Andrew Shallit of Apple demonstrated it for me at Boston MacWorld. He started developing his application by simply creating some objects and adding a menu bar to the set of menu bars available in the environment. When he needed an Edit menu, he switched menu bars to the development environment's menu bar, copied the Edit menu to the clipboard, switched back to his application, pasted it in and it worked immediately! Think about this for a moment: it is almost as if you started from the complete development tool suite, then tacked your own classes on top. Anything used to create the development environment itself is also available from the outset for use in your own applications. In most OODLs, this even includes the compiler, so you can write programs that read text from some source and invoke the compiler at run-time to extend the program.
The other difference concerns the way you do development. The following might be a typical way to start creating an application using an OODL.
- Double click your OODL application icon.
- Go get a cup of coffee while the splash screen is up and the environment (called an image) loads and initializes itself. This is the longest you will wait for anything from here on out.
- Click somewhere and type in a subclass of TApplication (I'll use MacApp analogies here; don't take them literally).
- In a single line, instantiate your subclass. You now have an application up and running, just like that.
- Add a method to your application with a click and a little typing of interface and implementation code. Compile the new method, and don't blink because you'll miss the busy cursor if you do.
- Test your new method by sending the application object that message. Don't "run" you application; it is already running. Don't wait until you've implemented ten bezillion other methods. Just write, then test.
- Add more classes and methods in this way, gradually increasing the number of objects from your application in the environment. You won't often "shut down" the application. Instead, you will develop as the application runs.
- If you need something from the development environment, it's already there. Just send it messages, subclass it, or whatever.
- When things are stable, save out your new classes and code. This can be as simple as saving the entire then-current image, which is what you would do while still developing, or writing your application-specific code to a text file, or, depending on the environment, stripping out unnecessary stuff (e.g., the compiler) and generating a standalone double-clickable application.
This is a far cry from the current MacApp environment, in which we have development tools on the left and running apps on the right and never the twain shall meet. You have to perform elaborate rituals to get back and forth: compiling, linking, launching, shutting down, repeat. To me, this is the single most striking difference between development with OODLs and development with statically compiled languages like C++.
Another consideration is garbage collection. Dynamic languages have them, statically compiled languages like C++ as a rule do not. It has been estimated that as much as 50% of a C++ programmer's time is spent tracking down dangling pointers, double deletions of the same object, memory leaks when objects are not deleted before their addresses are thrown away, and so forth. Lisp, Smalltalk, Prolog, Scheme, and most other dynamic languages do away with this problem altogether by turning memory management over to the underlying environment. When an object is no longer pointed to by anyone, it is automatically deleted.
So far, we've talked about dynamic languages in general, and this same discussion, with a suitable replacement of terminology, might have applied to non-object-oriented dynamic language environments as well. Object-oriented dynamic languages typically add some additional features that preserve the object paradigm at run-time, rather than just compile it out of existence, as happens with C++ and to a large degree Object Pascal and others. Smalltalk and CLOS, to name two such languages, provide run-time dispatch of methods based on the actual type of an object. This is a deep subject that we'll return to later in this article; I mention it now to set up the apparent paradox of Component Software: C++ is generally hostile to dynamic features, yet Component Workshop is a true OODL that uses C++ syntax and semantics. This, more than anything else, is the intriguing innovation of Component Workshop.
A C++ OODL?
The traditional knocks against OODLs go something like this:
- They are slow.
- They consume huge amounts of memory.
- They have weird syntax that normal programmers (an oxymoron if ever I heard one) won't accept.
- The integration of development and application environment is great for development, but you can't separate out your own application when you're done.
- Garbage collection may be efficient overall, but it is annoying to a user because it takes place at unpredictable times while the user stares at a bulldozer cursor for fifteen seconds.
The truth traditionally has been this:
- They are slow.
- They consume huge amounts of memory.
- They have weird syntax and normal programmers won't use them.
- You can't separate your application from the development environment when you are done developing.
- Garbage collection is annoying to users.
Most of the research that has gone into OODLs over the years has been directed at the first of these problems and I am happy to say slow performance in OODLs is now largely a myth, at least for the major-league products. The second, fourth and fifth issues are also being worked on feverishly, but don't seem to get the mindshare of the engineers working on OODLs, most of whom, I have found, wonder why anyone would be bothered by a 2.5M memory partition for the Nothing sample. The technology for addressing these issues is well-understood, however, and it is only a matter of time before they enter the mainstream. In particular, a technique called generational garbage collection tosses out a little garbage all the time, rather than waiting for it to pile up and overflow onto your kitchen floor.
However, if these get short shrift, the down-to-earth marketing consideration that people don't like all those parentheses and brackets seems altogether lost on the people who can do something about it. Tesler made the point in his keynote talk that Lisp and the like can be made to look like any other, "normal" syntax if that is what you want. It is, in fact, a standard undergraduate Lisp project to add this sort of syntactic sugar, to turn
(+ 3 4 5)
into
3 + 4 + 5
or
(defun f (a b c) (+ a b c))
into
f(a, b, c)
{ return a + b + c; }
It is easy, but never seems to be taken seriously. Also, this translation is at best a crude approximation, akin to translating poetry from one language to another by using a multilingual dictionary one word at a time without regard to overall texture and meaning. The second of these examples only looks like C; it isn't really because C has different semantics than Lisp that Lisp can only approximate without an inordinate amount of effort. You still have to learn a new language, even if it looks a lot like your old one.
Let's face it: what people really want is C++ without the headaches and broken hearts. Beyond a small but zealous band of Lisp and Smalltalk fanatics willing to overlook the problems (myself among them, I should add in all fairness), C++ has already won the war without even having to fire a shot. Perhaps Dylan or other languages can achieve widespread acceptance in the next generation of programmers, but C++ is now the Cobol of the 90s: even if you don't like it, even if it isn't the best language possible or even available, that's where the money is. So that leaves us not questioning whether C++ is the dominant programming language but rather how to best write in C++. And the first place to start is with possible cross-fertilization from the best of what OODLs have to offer. But on the surface this looks almost hopeless.
C++ can arguably be called an object-oriented translator, rather than an object-oriented language. This is regardless of whether you use a native compiler or one that produces C code as its output. There is very little object orientation left once the compiler is through. You can't, for example, look at a non-PascalObject instance in memory at run time and tell (unless you are a Steve Jasik willing to crawl about through RAM reading nuances into memory addresses of vtables) what class it is an instance of, what the class hierarchy above the concrete class looks like, or even that classes ever existed. Operator overloads are all resolved at compile time, which means that the actual type of the instance isn't used, only the type of pointer that existed at the spot in your code where you used the operator. Typecasting in C++, on the surface so elegant through user-defined conversions, in practice degenerates into a schoolyard baseball game. The skinny rich kid, the C++ compiler, isn't picked for a team until last and, in a fit of pique, picks up his ball and bat and goes home. If you do anything with constructors, assignment operators, or typecasting, C++ leaves in a huff.
Furthermore, there are mechanical, implementation details that defy any rational attempts at garbage collection. If you use multiple inheritance and typecast from base class to derived or vice versa, C++ actually pulls the slimy trick of moving the pointer to the object to a different offset. (This, by the way, is why handle-based objects can't use multiple inheritance in C++: the old pointer-into-the-middle-of-a-handle problem.) Try to implement any sort of garbage collection and compaction with that as a starting point. It can be done, but one can admire a ten foot high scale model of the Statue of Liberty made of used bottlecaps while at the same time wondering why the artist chose that particular medium.
I haven't even gotten warmed up on the list of reasons why C++ simply can't be an OODL, now or ever. Unfortunately, no one told Component Software in time that it can't be done. Now it's too late, because they've done it. For all of us who expect to earn most of our money with C++ while pining for a real OODL, it is time to sit up and take notice.
What's A Component Workshop?
Component Workshop first and foremost is a C++ development environment. There has been a lot of misunderstanding on this point. It is not the sort of syntactic sugar on top of, say, Lisp, that we talked about earlier. With a few exceptions, Component Workshop's C++ is real, honest-to-goodness C++ version 2.1. It just happens to be incrementally compiled and linked. And it just happens to be fully integrated with its development environment in the same sense as Smalltalk and the other "real" OODLs. Fascinating.
The development cycle I talked about above, creating and testing as you go, is fully implemented in Component Workshop. You create C++ classes on the fly in the time it takes to read this sentence. Same for methods. Even though that was a pretty short sentence. And they aren't just immediately checked for syntax errors; they are immediately compiled, linked and ready to respond to messages for all instances of that class. Rather than compile the entire program in order to run it on a carefully prepared test bed, you simply create instances as you program and test methods by firing them off for the instances you have created. Add a new method to a class and all instances of that class in your environment instantly respond to the new method. Same for changes to method implementations.
In your mental checklist for OODL features, start now by checking off "dynamic environment."
Next up is garbage collection. Imagine never again having to explicitly call "delete aGarbage" again. When an object is no longer referenced, it is simply tossed out with the old lettuce, automatically. And Component Workshop's garbage collection is of the generational sort we talked about earlier, always being done silently in the background without having to interrupt workflow with a steaming coffee cup cursor. Toss in a compaction scheme similar to the Macintosh Memory Manager handle strategy, but independent of the MMM, that reduces memory fragmentation. This is all implemented in Component Workshop.
So check off "garbage collection."
Let's talk efficiency. In theory, their dispatching of messages should be no worse than any other OODL and usually as good as C++. (I say in theory, because I have not yet been able to run benchmarks, although we have gotten down and dirty in discussions of how it is implemented.) In some cases, they should be able to do better than C++ in the final, extruded code, since they can perform global optimizations, while C++ optimizes method-at-a-time. For example, one classic problem with C++ is that it is difficult to optimize a monomorphic method out of vtable dispatching. This is because the vtable is set up locally, while to be sure that it is never overridden you have to look at the entire program. Object Pascal does this only with the cooperation of the linker and at the cost of slower dispatching everywhere else. Overall, Component Workshop's dispatching should be comparable to or better than pure C++.
Check off "fast."
Now for space. Component Workshop is like other OODLs in that while developing you have to carry around not just your application, but the entire development environment as well. Having gotten used to setting my MPW partition to 8M to allow the linker to limp to the finish line carrying MacApp on its back, that doesn't really bother me. Component Workshop, in fact, runs in a much, much smaller partition on a IIci and still has time to stop for a nap along the way. But that's never been the real concern with OODLs. It is the size of the finished, shippable app that is typically huge. With MCL, 2.5M is a reasonable starting point, although Apple is working hard to get that down to a trim half meg or so. Component Workshop has a feature called extrusion that, again on paper, does much better. Their claim is that a minimal app like a simple text editor should be about 150K. Now we're talking. Extrusion involves stripping unneeded stuff like the compiler and only spitting out the essential code needed to actually run the application. Right now the code that comes out is ANSI C, which is then compiled using your favorite neighorhood C compiler to produce the finished application. I have not yet used the extruder, so I can't say for sure whether it is as good as they claim, but again, based on talks with the folks at Component Software I, for one, think they aren't just blowing smoke.
Next to "small," write "check your September Frameworks for Jeff's test drive."
Component Software dispatches methods based on the type of the instance, not the type of the pointer. This means true, run-time object-oriented dispatch as one would expect in a conventional OODL. It also means changing some of the semantics of C++, a subject I'll return to in a minute, but in my opinion for the better.
Next to "true object-oriented code" put a big bold checkmark.
That's about the end of my OODL checklist and Component Workshop passes with flying colors.
But Is It Real C++?
This combination obliterates any doubt that Component Workshop is a true OODL environment, but is it true C++? After all the reason for doing this in the first place is to gain commercial acceptance that will likely always be denied to the likes of Lisp and Smalltalk. The answer is, about 90% yes, 95% if you include in your definition of "C++" idioms for the use of C++ that are widely recommended but not strictly required.
There are three areas of deviation from the language standard:
- Things that just don't make sense in the Component Workshop environment.
- Things that are difficult to do and have marginal value in the Component Workshop environment and, therefore, have been postponed to later releases.
- Things that will always be different.
As an example of the first, consider this arcane syntax in C++:
TClass::AMethod (args) { implementation }
Suppose you could graphically imply the class, rather than having to state it directly? Specifically, suppose that the window in which you are editing the code has the class name in its title? Why would you want to have to type TClass:: just for the sake of form, since neither the compiler, the linker or the user need it? There are several such "features" of the C++ language standard that are really not part of the semantics of the language, but rather conveniences for compilers and linkers. Component Workshop rather sensibly does away with these. There aren't many; they do not change any of the semantics of C++; and they aren't burdensome.
As to the second list, there are a lot of those right now, unfortunately. For example, the initially shipping version will not support #define macros or any other preprocessing, operator overloading, or templates. They all wreak havoc with incremental compilation and, though generally useful, have a marginal enough value that Component Software has chosen to ship a useful version to earn some money while working to shore up the weak spots. I have been told that becoming fully compliant with C++ v3.1 is a high priority, but we will have to wait and see. I suspect that CS themselves do not yet know just how strong the demand will be for some of the more arcane features of the language (did you know that you can declare classes inside classes or even inside statement blocks?) once people get used to what they can do without them.
The third list is the one likeliest to cause controversy. To some extent, Component Software has decided to "fix" C++. They have borrowed some concepts from other languages like CLOS and Eiffel and imported those ideas into C++. These in some cases extend the language in non-standard ways. In other cases, they have retained the original syntax but changed the semantics. From a purist's viewpoint, the changes all seem to make good sense. For example, CLOS has the concept of linearizing inheritance. Rather than throw its hands up at inheriting the same function name twice, as C++ does, CLOS has a well-defined search order that follows the order of what in C++ would be the derivation list. This allows the use of the keyword inherited even in otherwise ambiguous situations. Component Workshop has imported this concept from CLOS. You can use inherited and otherwise expect CW to do the right thing based on the order in which you set up the derivation. This also allows much, much cleaner implementation of multiple inheritance. Good idea, but goes against the grain of one of the design philosophies of C++: faced with even slight ambiguity, pick up your ball and bat and go home.
Another example: virtual data members. You say there's no such thing? There is now! In Eiffel, you can't tell whether a data member is an actual piece of data or a computation, because the name of the data member is hidden behind accessor (Get/Set) functions of the same name. This idea has been imported into Component Workshop in the form of a virtual data member. Virtual data members allow abstract classes to access a data member as if it is a simple piece of data, all the while deferring the decision of how to actually implement that data member until you create a concrete class. If you want to retain accessor functions, you can do so transparently. If you want the efficiency of direct access to the data members, you can do that just as easily. This is a sensible notion, and it is one of the keys to efficient implementation in Component Workshop, but it isn't C++ semantics.
Another example: all member functions are virtual. You can't decide otherwise. This, too, makes sense in this environment. The keyword virtual only exists in C++ so that the compiler can optimize the calls. It is a programmer's minefield and, absent efficiency considerations, you wouldn't want to even consider using non-virtual member functions. In Component Workshop, the extruder can tell which functions should be treated as virtual and which should not, so the need for non-virtual member functions disappears. Again, however, this breaks off a small corner of C++ semantics.
Another example: all derivation is virtual. In C++, virtual base classes must be explicitly set up by the programmer, otherwise multiple copies of a base class are set up inside each instance. Multiply inheriting the same class is one of the most questionable design practices in C++, anyway, and the only reason for not using virtual base classes is that C++ is pretty dumb about how it implements them. You can't typecast to a virtual base class then typecast back down to a derived class, to name only one annoying "feature." In Component Workshop, the equivalent to C++ virtual base classes is implemented quite cleanly and without taking away the ability to typecast downward. Once again, great idea, but a deviation from standard semantics.
Any of these taken individually would make a lot of sense. Taken together, they start to push the limits for anyone who has a purchasing checklist that insists on full C++ compatibility. Putting it bluntly, the people most likely to appreciate these improvements are the ones most likely to be happy with Smalltalk or Lisp in the first place. Component Workshop will have to come up with a strong case to overcome bureaucratic obstacles on this one, even though their changes, in my opinion, really are improvements.
Design Paradigms
There are also design paradigms unfamiliar to most C++ programmers. These seem to also be culled from a variety of sources. First, there is their use of pointer classes as the only means of accessing objects. The idea goes something like this:
class Foo {
// class declaration here
};
class PFoo {
private:
Foo* fFoo;
public:
// Constructors & etc.
Foo* operator -> (void)
{ return fFoo; }
Foo& operator . (void)
{ return *fFoo; }
};
// In the program somewhere
f(PFoo pf)
{ pf->DoSomething(); }
This is a crude approximation to the way one would actually use this paradigm, but the basic trick is to treat a pointer to an object as itself an object. PFoo has been called various things in the literature: Dewhurst and Stark (C++, Prentice Hall, 1989) called them "smart pointers," while more recently James Coplien (Advanced C++ Styles and Idioms, Addison Wesley, 1992) has called the PFoo classes "envelopes" and the Foo classes they point to "letters." I will use Coplien's terms, since that is what Component Software has chosen as well.
The advantages of always accessing through envelopes are so compelling that it is becoming a quasi-standard design strategy for those who really know the language. One can easily, for example, test for null pointers and recover gracefully, either through a failure or by returning some sort of default object when the pointer is null. If strict discipline is used, it is possible to implement lots of other very advanced memory management strategies within this framework:
- Reference counts. Allocate an extra couple of bytes in Foo's operator new for a reference count. In each constructor of PFoo, increment the count. In the destructor, decrement the count and, if it goes to zero, free the Foo.
- Caching schemes. Consider the possibility that fFoo can represent, not a memory address, but an address off on disk or elsewhere in a network of the persistent storage of a Foo. Operators -> and . can read it in when requested or, if it is already cached, simply return the address.
- Compaction. Since Foo is always accessed through PFoo, it can be moved around to compact memory, similar to the MMM's handles. There are a lot of nitpicky details to attend to in implementing this, but it isn't hard, just tedious. If you are really clever about it, you can create subclasses of PFoo to parallel those of Foo and even use multiple inheritance while still being able to compact, something you can't do with MMM handles. The overhead is slightly higher, but again it isn't rocket science.
And this is just the tip of the iceberg. The reason I am belaboring this idiom is that it is the single most critical key to the memory management strategy of Component Workshop and, therefore, something that a programmer in that environment must be comfortable with. CW does a great job of hiding all the gory details from you, but there are some curious semantics that you have to get used to. For example, in CW when you declare a variable like this
MyClass anInstance;
you haven't declared an instance, just an envelope. You separately must create the instance pointed to using the next design idiom, factory objects. Again, I want to emphasize that these concepts are considered by experts to be sound design practice for anyone using C++, but your run-of-the-mill C++ programmer hasn't heard of them.
The concept of a class in C++ is pretty amorphic: it can mean anything you want it to mean. We have already seen an example of this: the same basic syntax applies equally to envelopes and letters, even though their semantics are quite different. In other object-oriented environments, notably Smalltalk and Eiffel, conventions for categorizing classes have arisen out of the necessity to make sense of big, complicated programs. Some of this has been imported into the world of C++ by Component Software.
Classes in Component Workshop are one of three types:
- Factory classes create instances at run time. They are analogous to class objects in more traditional object-oriented languages like Smalltalk and Lisp. There is one instance of each factory class.
- Abstract classes principally exist to define interfaces to methods and interfaces to data members through the magic of virtual data members. They may or may not also provide implementation code for methods; an interface-only method is a pure virtual function in C++. Abstract classes are not instantiated but exist only as software engineering devices for taking advantage of polymorphism and code-sharing.
- Concrete classes specify implementations for all member functions and storage or functional implementations for the virtual data members. These are the classes that are actually instantiated by the factory class objects.
Factory classes are familiar to anyone who has worked with any metaobject protocol (CLOS) or class object (Smalltalk) environments, but do not have any direct counterpart in pure C++. Here is perhaps the largest single deviation from standard C++ in Component Workshop: in place of operator new and constructors, you have methods of the factory class.
MyClass anInstance = MyClass->new;
This is the way one would create a new instance of MyClass, but the syntax does not quite make plain what is really happening here. On the left-hand side of the equal sign we are declaring a new envelope; on the right-hand side we are sending a message to the factory object for MyClass, asking it to create an instance- the letter - and return its address. anInstance then points to that instance. Constructors are not called as a result of this. Instead, you are free to implement whatever methods of the factory class that you like. In this case we used new, but there can be any number of methods that create and initialize instances and some or all can take arguments. For example, we could use the following instead:
MyClass anInstance = MyClass->MakeBigOne(<arglist>);
where <arglist> is some set of arguments according to your needs. You implement factory methods like methods of any other class, so the set of "constructor" methods of the factory for a given class is up to you.
The syntax of this is close to that of pure C++, but not exactly the same. The semantics also compare closely to those of pure C++, including default "constructors" used in the event you don't specify which one to use, but the whole idea of a factory class is new.
One can argue whether this is a good or bad idea from a design point of view, but it clearly isn't standard C++. It appears to me that there is no technical reason why Component Workshop could not simply implement the same ideas within the standard C++ operator new/constructor syntax, leaving fewer buying objections and closer conformance to training books and classes in its wake, but neither is the current approach particularly difficult to learn or teach.
Destructors are for the most part unnecessary with good garbage collection in place. If you need to do special processing upon deletion of an object, the object receives a message when the garbage collector is about to toss it. Again, this is a non-standard treatment but one that is consistent with the overall semantics of C++. And again, there appears to be no good reason why the death notification could not have been implemented using C++ destructor syntax.
As to the distinction between abstract and concrete classes, this, too, represents good design practice with or without Component Workshop. In Component Workshop, concrete classes do not add additional member functions or data members, they only specify implementations. It is not burdensome and should probably be done in any program for reasons of modularity and code reuse, but it is an unfamiliar design strategy for most C++ programmers. Component Workshop takes specific advantage of the knowledge that concrete classes do not add to their inherited interfaces to perform some very, very clever optimizations in the way the instance is actually represented in memory, which is much of the reason it ends up so fast and so flexible.
changes/limitations. One would hope so, but we're talking about bureaucracies here.
Can I Import and Export Pure C++?
No. Let's not beat around that bush. As of right now, the import/export facilities do not appear to support the direct import and export of standard C++ code. However, the marketing staff of Component Software has made clear that complete compatibility is a high priority for future versions, with a target date of Q2 of 1993 for a fully ANSI C++-compliant product. In the meantime, translating from standard C++ to the subset supported by their initial product doesn't look too bad. The differences should mostly involve junking code that doesn't apply and most of the rest looks like very dull, mechanical translations that can perhaps even be scripted. I will be doing a benchmark of this in the near future by porting a subset of a substantial C++/MacApp application and will share the results.
What About the Class Library?
OOP languages are one thing, application frameworks another matter altogether. As of this writing, Component Software is putting the finishing touches on their class library, having drawn lessons from the best of what the rest of the world has to say on the matter: MacApp, Windows, the various X environments (Motif, Open Look, News), Smalltalk, etc., etc., etc. I will be doing an in-depth review of their class library and tool suite in the September issue of Frameworks, so stay tuned. Based on verbal discussions and some code, I am so far very impressed. Those of you who follow my meanderings and public crabbiness on MacApp3Tech$ know that I don't impress easily, and I'm still impressed. Stay tuned.
What About the "W" Word?
Come on, admit it: you love the Mac, but your biggest market is Windows, right? You aren't alone. Apple is feeling intense pressure right now to provide cross-platform solutions and an annoucement is due before you read this as to Apple's strategy for cross-platform object-oriented development. Component Software is also aware of this and has already begun parallel development of a Windows version of their product. The Mac version is to ship this September and, according to their current schedule, a Windows version will hopefully follow by year end. The key here, of course, is the structure of the class library, not the language itself, and in the September issue of FrameWorks I hope to have an update for those of you for whom Windows compatibility alone is reason enough to switch development environments. In particular, I will compare it to MacApp and to whatever cross-platform product Apple has announced by that time.
Who Is Component Software?
Component Software is a spinoff of ON Technology, the company Mitch Kapor (of Lotus fame and fortune) started with great fanfare to push the limits of software technology throught the miracle of OOP. CS is backed by Kapor and Kleiner, Perkins, one of the biggest and most experienced venture capital funds. ON Technology has been in and out of the news for years and has clearly struggled to get product to market and meet its lofty initial goals, but it has never hurt for talent in OOP technology. CS is much more narrowly targeted and has been working on their product for three years now. The engineers I have talked to have been first-rate, and Stonewall ("Stony") Ballard, the CEO and technical visionary, is one of the sharpest technical minds it has been my pleasure to meet in the world of OOP. It takes a lot to launch a successful startup, only part of which is to have a great product, and they are no cinch to succeed. However, to the extent that having an innovative, well-executed idea, great people and great backing are important, they are ahead of most startups, especially in the still-nascient world of OOP. As of this writing, they have a staff of nine, four of whom are engineers and the rest marketing and administrative personnel. The company is headquartered in Lexington, Massachusetts.
The Bottom Line
So far, I have dealt mostly with the facts and relatively few opinions. So how do I feel overall about what this means to you, as a beleagered MacApp/MPW/C++ vict . . . er, developer? Frankly, I think they have a hard sell to shops that are looking for C++ first and quality software development second. Unfortunately, that's a lot of large companies. And I don't think they are going to win over CLOS and Smalltalk diehards, other than those forced to do it by bosses and customers that insist on a "real" language, not one of those weird computer sciency thingamabobs. Face it, if elegance and efficiency were the prime considerations for choosing a language computer science probably should have stopped forty years ago when Lisp first came out. There are other forces at work in the market.
For everyone else, particularly the MacApp community, I think this is a breakthrough product. Despite some of the deviations from the C++ standard, it is still at least 90% standard C++ and many of the deviations simply drop stuff you don't want to do, anyway, in an environment without source files. It is a lot easier to learn this than, say, Lisp or Smalltalk, if you already know C, C++, or Object Pascal. This should translate into an easier time staffing, managing and maintaining projects. Component Workshop also makes it a lot easier to get the OODL you want while justifying to your boss or client that "it's really C++, it's just packaged a little different. Don't worry about it."
I also am waiting to see the class library. The technology of OODLs is now well-advanced and their biggest innovation is not in the area of OODLs, but in how to turn C++ into one. Class libraries, however, are still very much in their infancy and there are few guideposts on the path to making a great one. Today, an OOP language, no matter how great, can be no more powerful than the surrounding tools and class library. There is reason to be hopeful and so far the technical track record is pretty good, so let's see what it all looks like in a few months. n
How to contact Component Software.
Nancy Benovich is the product manager and, in a refreshing departure from marketing tradition, also holds the title "Senior Developer."
She can be reached at (617)862-9700.
Component Software is located at
420 Bedford Street
Lexington, Massachusetts 02173