TweetFollow Us on Twitter

January 94 - C++ for Gurus

C++ for Gurus

Jeff Alger

Jeff Alger is a consultant and author internationally recognized as an authority in object-oriented methods and technologies. He is coauthor of "Developing Object-Oriented Software for the Macintosh." The material in this article was inspired by his experiences developing a new seminar, "Advanced C++," now taught by him at Apple Computer Developer University. The course deals with advanced architectures for C++ programs. For information contact Developer University at (408)974-4897 or (408)974-6215.

There is a growing community of disenfranchised C++ gurus out there, and you may be among them; if so, this article is for you. No, I'm not talking to you beginning and intermediate C++ programmers; you have dozens of books, loads of training classes, multimedia-based self-help courses, magazines, mentors and cocktail parties galore. Not that one more cocktail party would be a bad idea, mind you, it's just that for one of the few times in my otherwise spotless diplomatic career, I'm going to be politically incorrect and send you packing.

For just this once, I'm talking to the rest of you, and you know who you are. The sort of person to whom an "overloaded function" isn't a boring social affair, an "overloaded operator" doesn't work for the phone company and a "collection class" isn't a seminar on raising money for charity. You cite the ARM frequently in casual conversation (always pronounced "arm"), at least half the time correctly but who cares because no one else in the room knows what the acronym means and you're not about to tell them.1 You're the sort who doesn't read those "sissy" books and, as a result, haven't found any books about C++ worth reading since you browsed through the index of Lippman's book after starting your first C++ compile.2,3 You don't just program in C++, your ideas flow onto a digital canvas using brushes and a palette from Bell Labs. Well, you have rights, too, and it's about time someone did something to keep you and others like you from going stark, raving mad. To anyone I haven't thoroughly offended yet, glad to meet you, come on in. Welcome to group therapy for C++ gurus. This is the first and possibly the last article in a series on how to learn to stop worrying and love C++. It is an outgrowth of a new three-day course I developed recently and now teach at Apple Computer's Developer University, titled "Advanced C++" and targeted, not at C++ programmers, but at C++ architects.

The Zen of C++

The natural course of one's understanding of C++ is like rising on an elevator, with each floor decorated quite differently from the one below.

Ding! First floor. C++ is a more reasonable C, strongly typed as long as you don't fool around too much, and, hey, how about those nifty // comments? All the C programmers that didn't want to go into management really needed a career path, and Bjarne Stroustrup, bless his soul, dreamed up a doozy.

Ding! Second floor. C++ is a decent but not great object-oriented language that happens to run blindingly fast by comparison to others. It's politically correct and sure to get your project funded by top management. Heck, they might double your budget if you mention the language often enough in your proposal. That's just as well, because no one really knows how to estimate and manage C++ projects and as to tools… lot of weather we're having, eh?

Ding! Top floor, everybody out. Hey, where did everyone go? Sure is drafty up here. C++ is really "yacc++,"4 not so much a language as a way of creating your own languages, elegant not for its simplicity (like "jumbo shrimp," the terms "C++" and "simple" grate on the ears when used in the same sentence) but because of its potential. Lurking behind every gnarly design problem is a nifty idiom, a nice little twist to the language that makes the problem melt away like the Wicked Witch of the West without her umbrella. That idiom solves the problem as elegantly as a real language like Lisp or Smalltalk would, but without causing smoke to rise from your CPU and the stock of companies that manufacture memory chips to rise on Wall Street. C++ isn't a language, it's an experience, a mind-altering drug.

There's that word again, "elegant." There is a Zen to designing for C++: You have to stop trying so hard to be elegant in order to achieve true elegance. From its brutish ancestor C it inherits not only compilation efficiency but also a conventional block structured grammar and a terse syntax for commonly used forms. It's got nouns, verbs, adjectives and lots of slang, like

cout<<17<<endl<<flush;

C++ programmers have been cowed into embarrassment by the language purists. The purists think that distinguishing a simple variable from a function call from a macro using nothing but parentheses is a surefire crowd-pleasing feat of prestidigitation. In real life the crowd only applauds languages in which different ideas look different. The "simple, consistent" languages have never gained much of a following outside academia, while block-structured languages have drawn all the crowds. This shouldn't be a surprise; research in linguistics predicts that learning time should be shorter and reading comprehension and retention much higher for languages that have all those supposedly "bad" properties. "i++" really is more "readable" by any verifiable measure than "i := i + 1;" and people really do have an easier time reading "x = 17 + 29;" than "(setq x (+ 17 29))". This has nothing to do with the design of the programming language and everything to do with how we are designed. C++ is ugly in large part because we are ugly. Learn to know and love the quirks and stop worrying about mathematical consistency and you are on the road to elegance in C++.

Like Lisp, Smalltalk, and other dynamic languages (and unlike C), C++ provides the hooks needed to manipulate low-level behavior of the compiler. You can make up your own data types and fool the compiler into adopting them as its own progeny. You can control how functions are called, how data members are accessed, how memory is allocated and deallocated and how and when things get initialized. And all without sacrificing efficiency or type safety (often). Unlike those other languages, a C++ program will merrily crash and burn if you use this power the wrong way. Even if it doesn't, your fellow programmers might if you don't find just the right C++ idiom for a complicated design. Deadalus and his son, Icarus, escaped imprisonment on Crete by fashioning wings out of feathers and wax. The wings helped Deadalus, the master architect, soar to distant shores. In the arms of his brash son, they led to death when he flew too close to the sun and melted the wax. Now that I think of it, there is more irony than I intended in that analogy: Deadalus was also the designer of the Labyrinth, which was so complex no one could find the way out once imprisoned. Hmm. Perhaps a more contemporary analogy is needed. Every time you use these low-level C++ features it's like the bumbling detective Sledge Hammer from the 80's TV series saying to the compiler, "Trust me, I know what I'm doing." The compiler rolls its eyes and plays along.

C++ is intriguing because of its inherent contradictions. Powerful because of tools that are easily abused. An extensible programming environment without compromising space or speed. Elegant in one set of hands while dangerous in others. Simple and complex at the same time. After years of using it, you still can't make up your mind whether to admire it or walk away in disgust. Yet, to the truly expert, there are concepts that underlie the use of the language that tip the scales in its favor. Certain architectural paradigms go best with specific features of the language; mismatch the two and the result is chaos, but get the right combination and the result is… well, elegant.

Three Great Ideas in C++

It seems to the uninitiated that mastering C++ is really about getting your own bag of tricks like the ones carried by the masters. Some of these tricks are general object-oriented design principles. The rest are specific to C++ and revolve around how to use its unique strengths and work around its unique shortcomings. To my mind, there are three basic paradigms of C++ architecture that organize these tricks into a coherent framework.
  • Indirection
  • Homomorphic type hierarchies
  • Memory spaces

Each is supported by specific syntax and features of C++ and the three principles work together to solve an amazing variety of problems. There are other C++ design principles one could add to the list, but these are the core of any advanced C++ design.

Indirection covers a wide variety of individual topics, but the concept is the same throughout: some client object makes a request of a second object, which turns right around and delegates the work to a third object. The object in the middle is where the indirection takes place. Some might argue that this is almost the dictionary definition of delegation, one of the bulwarks of general object-oriented design, but in C++ the idioms one uses with this concept and the language support for it goes beyond what is considered delegation in other languages. For example, the object-in-the-middle, which I shall call the smart pointer for the time being, may determine where in memory, on disk, or in the network the object resides; when it gets destroyed; whether it may be updated or whether it is read-only; whether it even exists or whether it is simply an abstract location in some collection or memory space waiting to be assigned to. All without the active cooperation of the target object, which may be completely oblivious to all this grubby, low-level activity.

A homomorphic type hierarchy is one in which all derived classes share the same public interface as their common abstract base class. In fact, the abstract base class is usually as pure as the driven snow: all of its methods are pure virtual placeholders. This sounds simple enough, but in C++ there are a number of powerful language and design idioms that are enabled by the paradigm.

The idea of a memory space is enabled by your ability to overload operators new and delete and certain other very language-specific features. The concept is that an object may be allocated, not just randomly in a compiler-assigned location in memory, but as part of an implicit collection of objects not directly visible to the application. The simplest example is preallocating an array big enough for 1000 Foo's, then overloading operator new to allocate from an unused slot and operator delete to return the Foo to a free space list. But this is the plumber's view of architecture; the concept of a memory space builds on these low-level mechanics to become itself one of the central tools of the C++ designer. It is difficult to separate these concepts, since each borrows from the other two. But together they solve some of the most daunting problems of design in just a few lines of code. In fact, maybe that should be a standard by which to judge how well you have used the concepts: more than a couple of hundred lines and a giant hook pulls you off the stage. In the "Advanced C++" course several of the labs would normally merit a few weeks each just for design in a typical project. While the labs aren't exactly obvious, the solutions involve from dozens to a couple hundred lines of code and actually flow very logically from the coding and design idioms presented. The labs include

  • An "undo" facility that is easily extended into multi-user transaction processing;
  • An high-performance, industrial-strength garbage collection scheme for use with existing classes; and
  • Distributing objects over a network.

These all build toward what is almost an anticlimax, the use of all three design concepts to facilitate reuse. In the remainder of this article I will skim the surface of one of these three concepts, indirection.

Indirection

Many of the techniques used by the C++ masters revolve around the idea of indirection. Consider the following code fragment.
class Foo {
public:
    void MemberOfFoo();
};

Foo* aFoo = new Foo;
aFoo->MemberOfFoo();

Forget you ever knew anything about C and the lowly ->, and take a fresh look at the subject. This is the built-in "operator->" being applied to a built-in pointer "class," in this case the address held in aFoo. The built-in operator-> is available for use with any address whose type is "pointer to struct or class." In effect, you are indirectly referring to one object, the Foo, using another, the pointer. This is as far as built-in indirection goes, but you can extend the idea of operator-> through the magic of operator overloading. Here is a simple example of something known as a "smart pointer" class.

class PFoo {
private:
    Foo* fFoo;
public:
    PFoo(Foo* f): fFoo(f) {}
    Foo* operator->() { return fFoo; }
};

PFoo pf(new Foo);
pf->MemberOfFoo(); // Works!

Here the class PFoo slips into your program in place of the built-in "class" Foo*. When the compiler sees operator->, it does not automatically spit out the member requested on the right-hand side; instead, it looks to see whether the left-hand side is a built-in type (an address) or a user-defined type. If user-defined, it invokes the overloaded operator->() and repeats the process with the return value as the new left-hand side. Of course, if you are silly enough to fail to overload operator-> or to return the address of something other than a struct or class, the compiler will reach out and slap your hand and refuse to budge. In this case, pf->() returns a Foo*, which is a legal left-hand side to the built-in operator->, so the compiler is happy and we're happy.

There is no space or performance overhead to this indirection. What's the size of a smart pointer? The sum of its data members (there are no virtual member functions and, therefore, no vtable pointer to clutter up the object). In this case, an instance of PFoo is the same four bytes as a Foo*; it can be passed by value in a machine register just as efficiently as an integer or anything else. What about performance? All methods are trivial inlines that any good C++ compiler should handle as efficiently as using a built-in Foo* directly. At the least we haven't done any damage. But so far, we really haven't done anything we couldn't have done with the built-in operator, either. How many times have you gnashed your teeth over the old dereferencing-nil problem? Here is a minor variation on the theme.

inline Foo* PFoo::operator->() {
#ifdef DEBUGGING
    if (!fFoo) {
        cerr<<"*** NULL PFoo ***"<<endl;
    fFoo = new Foo; // Create a dummy instance
    }
#endif
    return fFoo;
}

Now we have a smooth way to handle the problem: spit out an error message and return a surrogate object to allow the program to keep limping along, at least in debug mode. There are further variations on this, such as storing a single default surrogate as a static data member of the class PFoo just so you can return it to wayward clients, or returning a derived class of Foo all of whose member functions scream loudly every time they are accessed.

Now suppose that Foo is really an abstract base class like this.

class Foo {
protected:
    Foo() {} // Cannot be directly instantiated
public:
    virtual void MemberOfFoo()=0;
    virtual int AnotherMember()=0;
    // ...
};

The client of the pointer now need have no idea which derived class of Foo is actually contained in the smart pointer. In fact, it is simple to switch the object pointed to at run time. "That's nothing," you may say, "I can change the address contained in a pointer variable, too." True, but can you train your pointers to do this?

class PFoo {
private:
    Foo* fFoo;
public:
    PFoo(Foo* f): fFoo(f) {}
    const Foo* operator->() const { return fFoo; }
    Foo* operator->() { // Warning! Non-const access!
        if (fFoo->IsConst())
            fFoo = fFoo->UpdateCopy(); // A new one I can change
        return fFoo;
    }
};

Presumably there is some optimal representation of Foo when it is being used in a read-only (const) fashion. If someone requests a member in a non-const way, the smart pointer automatically replaces the read-only version with a writeable version. In fact, with an extra level of indirection this example can be reworked so that the object itself need not provide the method support shown here (IsConst() and UpdateCopy()); that logic can be entirely contained in read-only and read-write pointers. That extra effort is rewarded; this architecture can then be slipped into an existing program that uses smart pointers. It would be extraordinarily difficult, however, to make this sort of change late in the game in a program organized around *s. One of the first steps on the road to C++ elegance is to learn to mumble smart pointers in one's sleep, using them routinely out of the vague sense that they might come in handy someday.

I am only hinting at a general strategy here: read-only pointers as distinct from read-write pointers. These wrap the object and largely insulate it from knowledge about how it is being used. For example, in a distributed object system one may use a local copy for read-only purposes but retrieve the "master" copy in order to perform updates.

When a smart pointer is in one-to-one correspondence with the object it points to, the terms "master pointer," "envelope" and "handle" have all been used by various authors. I prefer the term "master pointer" because I have other uses for the other terms. Here is a simple example that illustrates master pointers and the recursive use of operator->.

class PFoo { // This acts as a "master pointer"
private:
    Foo* aFoo;
public:
    PFoo(): aFoo(new Foo) {}
    PFoo(args):aFoo(new Foo(args)) {}
    PFoo(const PFoo& pf):aFoo (new Foo(*pf.aFoo)) {} // Copy contents
    PFoo& operator=(const PFoo& pf); // See below
    ~PFoo() { delete aFoo; }
    Foo* operator->() const { return aFoo; }
};
inline PFoo& PFoo::operator=(const PFoo& pf) {
    if (this == &pf) return *this;
    delete aFoo;
    aFoo = new Foo (*pf.aFoo);
    return *this;
}

class HFoo {
private:
    PFoo& fMasterPtr;
public:
    HFoo(PFoo& pf):fMasterPtr(&pf) {}
    Foo* operator->() const { return fMasterPtr; }
}

PFoo pf; // Creates master pointer plus instance of Foo
HFoo hf(pf); // Handle to the Foo

hf->MemberOfFoo(); // Works! Doubly interpreted: HFoo and PFoo

Here the direct pointer class, PFoo, is in one-to-one correspondence with the object it points to. When the master pointer is deleted, its destructor also deletes the object pointed to. operator-> is recursively interpreted twice so that the user of an HFoo need not do any explicit dereferencing. HFoo is now analogous to a "handle" in the Macintosh or Windows environments; it refers to the object indirectly through a master pointer. Unlike those environments, however, the dereference is done without compromising type safety and without relying on a single model of how objects are created, stored, and destroyed. In fact, those memory managers are really just a special case, a specific implementation of this design paradigm in C++.

Master pointers can be used for a wide variety of simple memory management strategies. operator-> can, for example, create the object on the fly if it doesn't already exist, perhaps reading it from a database or obtaining a copy from another process elsewhere on the network. Handles are the key to most advanced memory management strategies, including reference counting and the many variations of mark-and-sweep and generational garbage collection. It can also be extended to elegantly handle distribution of objects over multiple processes, computers, and storage media. In the seminar, we take this one step further, designing each master pointer to automatically maintain two copies of the target object: one for current access and one for undo.

One of the more intriguing and less obvious uses of indirection arises from the design of collection classes.

Arrays and operator[]

operator[] is amazingly versatile in ways most C++ programmers never consider. Here is a simple application: an array that checks its bounds when its elements are accessed.
class ArrayOfFoo {
private:
    int fSize;
    Foo* fContents;
    static Foo* fgSurrogate; // A dummy for out-of-bounds indices
public:
    ArrayOfFoo():fSize(0),fContents(nil) {}
    ArrayOfFoo(int size):
        fSize(size),fContents((Foo*)(new void*[size]) {}
    ~ArrayOfFoo() { delete fContents; }
    Foo*& operator[] (int index) {
        return (index<0 || index>=fSize)
            ? fgSurrogate : fContents[index];
        }
};

// In a .cp file somewhere
Foo* ArrayOfFoo::fgSurrogate = nil;

The return value from operator[] is a Foo*&, that is, a reference to an address of a Foo. This allows the result of operator[] to be used as the left-hand side of an assignment, among other things.

anArray[10] = aFoo;

If efficiency is more important than robustness, simply surround the fSize data member and the range-checking logic with #ifdefs, and you have an array identical in size and performance to a standard C-style array of pointers. But this only scratches the surface of operator[]. It can be overloaded to take non-integral arguments. Here is the outline of an association class that maintains a set of pairs (String, String), where the first string acts as a key and the second, the unique value associated with that key.

class Association {
public: // Implementation details spared here
    const String& operator[] (const String& key);
};

String value = anAssociation[someString];

This is much more elegant and expressive of the designer's intent than using a purely method-based interface like

String value = anAssociation.Lookup(someString);

Operator[] can be overloaded to accept any argument type, as for String in this example, with the sole restriction that it can only accept one argument. Thus, the following is not legal.

class WontWork {
public:
    Foo& operator[] (int x, int y); // Only one argument allowed
};

This isn't as much of a problem as it would appear, since one can easily create classes or structs that simulate a point in n-dimensional array space.

struct Index {
    int fx;
    int fy;
    Index(int x, int y):fx(x), fy(y) {}
    Boolean operator== (const Index& i) 
        { return fx==i.fx && fy==i.fy; }
};

class WorksFine {
public:
    Foo& operator[] (Index i);
};

anArray[Index(17,29)]; // Uses an anonymous instance of Index

It is also possible to overload operator[] more than once in a single class. Why would you want to do that? Perhaps you have a need to index two ways.

class StringArray {
public:
    const String& operator[] (int index);
    int operator[] (const String&);
};

The first operator[] maps from an integral index to the string at that location. The second does the opposite: given a string, it returns the index of that string in the array. (Presumably some value such as -1 is set aside to return when the string does not occur in the array.) Once one gets used to overloading operator[], it becomes invaluable as a way to encapsulating collections of all sorts, not just arrays. However, collections that do not simply span indices from 0 to N require a little more attention than simply overloading operator[].

A Sparse Array Class

One of the most basic data structures is the sparse array. This is a matrix most of whose cells are empty. To represent this as an NxM (or even higher dimensions) array of cells would be a terrible waste of space, so a variety of lower-level data structures are used to simulate the array: linked lists, hash tables, binary trees, and just about any other exercise from your Introduction to Data Structures course in college. We aren't concerned here with the implementation details but rather with how to best leverage C++ language features to isolate clients from those details. To illustrate the ideas, here is a brute-force implementation using a linked list of cells.
class SparseArray {
private:
    struct Node {
        Index fIndex; // Uses Index structure above
        Foo* fContents;
        Node* fNext;
        };
    Node* fCells;
public:
    SparseArray(): fCells(nil) {}
    Foo* operator[](Index i) {
        Node* n = fCells;
        while (n)
            if (n->fIndex == i) // Why we overloaded == for Index!
                return n->fContents;
            else n = n->fNext;
        return nil; // Not found
        }
};

aFoo = anArray[Index(17,29)]; // Works

This is fine for accessing cells of the array, but how do we add new cells or reassign existing ones? The operator[] we have created won't work as the left-hand side of an assignment because it is a Foo*, not a Foo*&.

anArray[Index(17,29)] = aFoo; // Will not work

We could create some special functional interface, but that would mean breaking the illusion for the client that this is a normal array. Is there a way to use operator[] as the left-hand side of an assignment with this and other pseudo-arrays? It turns out the answer is Yes, but we have to introduce a new design paradigm for the purpose.

Cursors

Let's try again.
class ArrayCursor;
 
class SparseArray {
friend class ArrayCursor;
public: // private: ***The Semantec work-around***
   struct Node {
   Index fIndex; // Uses Index structure above
   Foo* fContents;
   Node* fNext;
   Node(Index i, Foo* content, Node* next)
   : fIndex(i), fContents(content), fNext(next) {}
   };
   Node* fCells;
public:
   SparseArray(): fCells(nil) {}
   ArrayCursor& operator[](Index i);// inline moved below ArrayCursor
};
 
class ArrayCursor {
friend class SparseArray;
private:
   SparseArray& fArray;
   Index fIndex; // Index this cursor represents
   SparseArray::Node* fNode; // Non-nil means the index exists
   // Constructors are private; only SparseArray can create these
   ArrayCursor(SparseArray& array, Index i)
   : fArray(array), fIndex(i), fNode(nil) {}
   ArrayCursor(SparseArray& array, SparseArray::Node* node)
   : fArray(array), fIndex(node->fIndex), fNode(node) {}
public:
   ArrayCursor& operator=(Foo* foo) {
   if (!fNode) {
   fNode = new SparseArray::Node
   (fIndex,foo,fArray.fCells);
   fArray.fCells = fNode;
   return *this;
   }
   fNode->fContents = foo;
   return *this;
   }
};
 
inline ArrayCursor& SparseArray::operator[](Index i) {
   SparseArray::Node* n = fCells;
   while (n)
   if (n->fIndex == i) return ArrayCursor(*this,n);
   else n = n->fNext;
   return ArrayCursor(*this,i); // Not found
   }

Whoa! What's going on here? operator[] returns an ArrayCursor for an index that does not yet exist. This becomes the left-hand side of the assignment, so the ArrayCursor::operator= is invoked. This creates a new Node and adds the right-hand side as the contents of that cell. To the client this appears to be a simple array even though the internal details are anything but simple.

There are a couple of details glossed over here. For example, the following would not work.

anArray[Index(17,29)]->MemberOfFoo();

This and similar problems are simple to handle by overloading operator-> and adding an operator Foo* in the ArrayCursor class.

The concept of a cursor is not specific to the matrix form. A cursor can be used to represent any "position" in a collection, even a collection that is unordered. Cursors can also be used to represent positions in disk files or other processes and computers. C++ operators ->, [] and =, overloaded together, largely insulate clients from where the actual objects and collections are physically stored and what index structures are used to retrieve them.

From Cursors to Iterators

I said earlier that a cursor is a new design idiom, but it is easy to connect it to one you are probably already familiar with: the iterator. The basic concept of an iterator is illustrated by the following code fragment.
class Collection {
public:
    class Iterator {
    public:
        Boolean More();
        Foo* Next();
    };
    Iterator* ProvideIterator(); // Returns an Iterator
};

Collection::Iterator* iter = aCollection->ProvideIterator();

while (iter->More())
DoSomethingToAFoo(iter->Next());

Here we have assumed that the collection contains Foo*s; more generally, one would use a template to genericize these classes, but the treatment here is otherwise general. Note that you ask the collection object to hand you the iterator, rather than directly instantiating it yourself. This allows derived classes of Collection to return derived classes of Iterator without the client having any knowledge that derived classes are involved.5 There are lots of variations on the theme, such as controlling the order or range of the iteration.

How are iterators implemented? One straightforward approach is to simply extend the collection's cursor class to add the More and Next member functions! This combination provides a bonus in the box of candy corn: assignment to the current location of the iterator/cursor is supported automatically. A closely related design strategy is to create an iterator class that contains a cursor as a data member.

Cursors and iterators aren't just oddly shaped widgets in the bag of C++ tricks. If you look carefully, you will see that they recycle the idea of indirection. A cursor with an overloaded operator-> is just a "smarter" pointer of sorts. The same technique of pointing to something that isn't there yet finds wide application in other design problems: persistent objects, distribution, and caching, to name a few. This convergence of design ideas is typical of advanced C++ architectures.

Top Floor

There is a relatively small circle of experts in C++ and object-oriented design that understand and routinely apply these principles, creating what to others often seems black magic. As with all magicians, people hold them in a combination of reverence and distrust. However, the real problem is a lack of literature, for the techniques themselves are accessible to anyone with a solid background in C++ and software design. Hopefully now that C++ is entering its teen years we will see more attention paid to members of that frustrated underclass, the C++ architect. In future articles, I'll pay some attention to homomorphic type hierarchies and memory spaces.

Copyright ©1994 by Jeff Alger. All rights reserved.

  1. That's Bjarne Stroustrup's C++ Annotated Reference Manual, and if you had to ask perhaps you should go find one of those cocktail parties. No offense intended… I'll catch up with you later.
  2. Well, maybe James Coplien's Advanced C++ Programming Styles and Idioms caught your eye for a while, but much of that book is just too weird. I mean, how many people really care about how to use C++ to emulate Lisp programming? Do you really need to change the implementation of a method on the fly without shutting down a running C++ program? I admire the book, but it doesn't fill the void on advanced C++ usage.

    You don't mind footnotes, either.

  3. Oh, you're not a Unix hacker, either? That's the Unix-based "yet another compiler compiler," (cute, huh?) a do-it-yourself kit for implementing programming languages.
  4. Note for MacApp programmers: iterators in MacApp violate these rules and as a result are not quite as flexible as the strategy presented here.
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Air Video Server HD 2.1.0 - Stream video...
Air Video Server HD streams videos instantly from your computer on your iPhone, iPad, iPod touch or Apple TV. No need to worry about converting or transferring files. We took everything that was... Read more
Duplicate Annihilator 5.7.5 - Find and d...
Duplicate Annihilator takes on the time-consuming task of comparing the images in your iPhoto library using effective algorithms to make sure that no duplicate escapes. Duplicate Annihilator... Read more
BusyContacts 1.0.2 - Fast, efficient con...
BusyContacts is a contact manager for OS X that makes creating, finding, and managing contacts faster and more efficient. It brings to contact management the same power, flexibility, and sharing... Read more
Capture One Pro 8.2.0.82 - RAW workflow...
Capture One Pro 8 is a professional RAW converter offering you ultimate image quality with accurate colors and incredible detail from more than 300 high-end cameras -- straight out of the box. It... Read more
Backblaze 4.0.0.872 - Online backup serv...
Backblaze is an online backup service designed from the ground-up for the Mac.With unlimited storage available for $5 per month, as well as a free 15-day trial, peace of mind is within reach with... Read more
Little Snitch 3.5.2 - Alerts you about o...
Little Snitch gives you control over your private outgoing data. Track background activity As soon as your computer connects to the Internet, applications often have permission to send any... Read more
Monolingual 1.6.4 - Remove unwanted OS X...
Monolingual is a program for removing unnecesary language resources from OS X, in order to reclaim several hundred megabytes of disk space. If you use your computer in only one (human) language, you... Read more
CleanApp 5.0 - Application deinstaller a...
CleanApp is an application deinstaller and archiver.... Your hard drive gets fuller day by day, but do you know why? CleanApp 5 provides you with insights how to reclaim disk space. There are... Read more
Fantastical 2.0 - Create calendar events...
Fantastical is the Mac calendar you'll actually enjoy using. Creating an event with Fantastical is quick, easy, and fun: Open Fantastical with a single click or keystroke Type in your event details... Read more
Cocktail 8.2 - General maintenance and o...
Cocktail is a general purpose utility for OS X that lets you clean, repair and optimize your Mac. It is a powerful digital toolset that helps hundreds of thousands of Mac users around the world get... Read more

Pie In The Sky: A Pizza Odyssey (Games)
Pie In The Sky: A Pizza Odyssey 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: A game about delivering pizza. In space. | Read more »
Chosen Gives Hopeful Singers, Songwriter...
If YouTube videos and reality TV shows like The Voice have taught us one thing, it’s that there are a lot of people out there who are anxious to show the world their talents. And if they’ve taught us a second thing, it’s that there’s an almost... | Read more »
Android's Popular OfficeSuite Now A...
Once only available for Android devices, OfficeSuite has finally landed on the app store. The Mobile Systems app lets you view, edit, create, and share Word, Excel, and PowerPoint documents as well as convert them to/from PDFs. It's touted as being... | Read more »
Warhammer: Arcane Magic is Coming Soon,...
Turbo Tape Games has announced that they're joining forces with Games Workshop to bring the turn-based strategy board game, Warhammer: Arcane Magic, to life on the iOS. | Read more »
Fast & Furious: Legacy's Creati...
| Read more »
N-Fusion and 505's Ember is Totally...
| Read more »
These are All the Apple Watch Apps and G...
The Apple Watch is less than a month from hitting store shelves, and once you get your hands on it you're probably going to want some apps and games to install. Fear not! We've compiled a list of all the Apple Watch apps and games we've been able to... | Read more »
Appy to Have Known You - Lee Hamlet Look...
Being at 148Apps these past 2 years has been an awesome experience that has taught me a great deal, and working with such a great team has been a privilege. Thank you to Rob Rich, and to both Rob LeFebvre and Jeff Scott before him, for helping me... | Read more »
Hands-On With Allstar Heroes - A Promisi...
Let’s get this out of the way quickly. Allstar Heroes looks a lot like a certain other recent action RPG release, but it turns out that while it’s not yet available here, Allstar Heroes has been around for much longer than that other title. Now that... | Read more »
Macho Man and Steve Austin Join the Rank...
WWE Immortals, by Warner Bros. Interactive Entertainment and WWE, has gotten a superstar update. You'll now have access to Macho Man Randy Savage and Steve Austin. Both characters have two different versions: Macho Man Randy Savage Renegade or Macho... | Read more »

Price Scanner via MacPrices.net

Apple offering refurbished 27-inch 5K iMacs f...
The Apple Store is offering Apple Certified Refurbished 27″ 3.5GHz 5K iMacs for $2119 including free shipping. Their price is $380 off the price of new models, and it’s the lowest price available for... Read more
16GB iPad mini on sale for $199, save $50
Walmart has 16GB iPad minis (1st generation) available for $199.99 on their online store, including free shipping. Their price is $50 off MSRP. Online orders only. Read more
New 128GB MacBook Airs on sale for $50 off MS...
 B&H Photo has 128GB 11″ and 13″ 2015 MacBook Airs on sale today for $50 off MSRP including free shipping plus NY sales tax only: - 11″ 1.6GHz/128GB MacBook Air (Model #MJVM2LL/A): $849 $50 off... Read more
13-inch 2.6GHz Retina MacBook Pro (refurbishe...
The Apple Store has Apple Certified Refurbished 13″ 2.6GHz/128GB Retina MacBook Pros available for $979 including free shipping. Original MSRP for this model was $1299. Read more
Save up to $600 with Apple refurbished Mac Pr...
The Apple Store is offering Apple Certified Refurbished Mac Pros for up to $600 off the cost of new models. An Apple one-year warranty is included with each Mac Pro, and shipping is free. The... Read more
Samsung Galaxy S 6 and Galaxy S 6 edge U.S. P...
Samsung Electronics America, Inc. has announced the Galaxy S 6 and Galaxy S 6 edge will be available in the U.S. beginning April 10, with pre-orders being accepted now. “We have completely reimagined... Read more
13-inch 2.5GHz MacBook Pro (refurbished) avai...
The Apple Store has Apple Certified Refurbished 13″ 2.5GHz MacBook Pros available for $829, or $270 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free: - 13″ 2.... Read more
Save up to $80 on iPad Air 2s, NY tax only, f...
 B&H Photo has iPad Air 2s on sale for $80 off MSRP including free shipping plus NY sales tax only: - 16GB iPad Air 2 WiFi: $469.99 $30 off - 64GB iPad Air 2 WiFi: $549.99 $50 off - 128GB iPad... Read more
iMacs on sale for up to $205 off MSRP
B&H Photo has 21″ and 27″ iMacs on sale for up to $205 off MSRP including free shipping plus NY sales tax only: - 21″ 1.4GHz iMac: $1019 $80 off - 21″ 2.7GHz iMac: $1189 $110 off - 21″ 2.9GHz... Read more
Färbe Technik Offers iPhone Battery Charge LI...
Färbe Technik, which manufactures and markets of mobile accessories for Apple, Blackberry and Samsung mobile devices, is offering tips on how to keep your iPhone charged while in the field: •... Read more

Jobs Board

DevOps Software Engineer - *Apple* Pay, iOS...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
Sr. Technical Services Consultant, *Apple*...
**Job Summary** Apple Professional Services (APS) has an opening for a senior technical position that contributes to Apple 's efforts for strategic and transactional Read more
Lead *Apple* Solutions Consultant - Retail...
**Job Summary** Job Summary The Lead ASC is an Apple employee who serves as the Apple business manager and influencer in a hyper-business critical Reseller's store Read more
*Apple* Pay - Site Reliability Engineer - Ap...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.