TweetFollow Us on Twitter

Stream for All Seasons

Volume Number: 16 (2000)
Issue Number: 4
Column Tag: Programming

A Stream for All Seasons

by Neil Mayhew, Calgary, AB

A lightweight ostream-compatible class using Pascal strings

Getting Your Message Across

They say a picture is worth a thousand words, but a picture without any words is often hard to figure out. The Macintosh user interface does a wonderful job of being graphical, but it also uses text very effectively in appropriate places.

Can you imagine using a large application without a single word of text on its menus and dialogs? Consumer electronics devices that use cute little icons for everything can be extremely puzzling until you have spent a little time studying the manual-in text form, of course.

The ability to manipulate text is therefore crucial to even the most graphical Macintosh program. Text in the user-interface-rather than text as data-uses Pascal string conventions. This doesn't always fit very well with C, which has it's own string conventions. The problem is worse with C++, which has a much richer standard library of text manipulation functions-none of which understand the Pascal string format.

However, the power and flexibility of C++ can be used to overcome many of these limitations in a way that is both transparent and economical. Yet again, templates, inline functions, and stack-based objects can come to our rescue-if we know how to take advantage of them.

This month's article describes a lightweight class that builds Pascal strings using the << notation used by the standard C++ ostream. Next month, I'll introduce a program that uses STL-compatible iterators to monitor your System Folder for unexpected changes to extensions and control panels by wayward installer programs.

Printf Considered Harmful

Much of the time, text that is being displayed in the user-interface is a fixed string taken from a resource. Sometimes, though, text is built up from a number of pieces, some fixed and some not. We have come to appreciate having the text of the Undo menu item change to reflect the action that is being undone. In fact, there are all kinds of situations in which user-interface text needs to be built up programmatically: prompts, error messages, status messages, progress messages, and so on. In all of these cases, the generated text needs to be inserted into the user-interface as a Pascal string.

There are a number of simple approaches to doing this. For alerts and dialogs, the ParamText mechanism is useful. Or, a sequence of string-concatenation operations can be performed. Even better, one of the printf family of functions can be used to format a string with complex substitutions via % characters.

Although the printf approach is powerful, it has some severe limitations in any setting:

  • substitutions are not type-checked or counted, and are therefore error-prone
  • sprintf's output string is not protected against overflow

In a Mac OS setting, there are some additional limitations:

  • Pascal strings are not understood as arguments
  • the output is a C string rather than a Pascal string
  • printf cannot be used at interrupt time or in a code resource
  • the C i/o library increases code size considerably

The C++ iostream library takes care of the first two problems very effectively, using ostringstream, but the Mac OS-specific problems remain. How could we retain the elegant << syntax and yet be able to handle Pascal strings with very low overheads?

Simple Is Beautiful

Early on in my programming career, I learnt that writing large, complicated programs is relatively easy. What is hard is keeping them simple! A more experienced colleague had a wonderful knack for developing small, sophisticated programs that contained just enough functionality for the task in hand-and no more. The C++ iostreams library quite rightly has a huge range of functionality, much of which we do not need for our limited task of formatting strings. If we select just the functionality that is useful to us-concatenating strings, integers and floating-point numbers-it should not be hard to implement a lightweight class that supports << syntax for those items.

The output needs to be a Pascal string, and various manipulation capabilities for Pascal strings will be needed. It makes sense therefore first to develop a C++ encapsulation of Pascal strings. This will simplify our stream implementation, and will be a useful facility in itself.

As I mentioned in last month's article, the constant template-argument feature of C++ is specifically aimed at the implementation of fixed-length arrays such as Pascal strings. This enables us to store various sizes of strings on the stack, instead of having to allocate variable-length blocks from the heap. Without this, we would not be able to meet our design criterion of usability at interrupt time or in a code resource.

Making It To First Base

Last month's template used only inline functions, and these were sufficiently simple that they more or less disappeared in the generated code. The functions to process strings are not so simple, however. If we make them inline, we face two possible outcomes: either a substantial chunk of code will be inserted into every routine that calls one of our template's methods, or the compiler will opt to make them non-inline anyway.

What happens if we have non-inline methods in a template class? Ordinarily, we would put the implementation of non-inline methods into a .cp file that is compiled and linked. However, most development environments (CodeWarrior included) are not able to handle template methods this way. A separate copy of the method needs to be generated for each set of template arguments that are used with its class, and the compiler can't know which copies to generate until it has compiled the whole project. This restriction is normally overcome by putting the implementation of the method into the header file, but not declaring it inline. The compiler generates 'real' code for each combination of template arguments, and the linker takes care of eliminating redundant copies.

In a large project, this can slow build times by an order of magnitude. This is especially unfortunate when the majority of the copies generate almost identical machine code. For example, in the case of our Pascal string class, this would mean a separate copy of the code for each different string size (31, 63, 255, etc.) even though all the copies are performing virtually the same operation. This effect is known as code bloat.

If the foregoing paragraphs haven't made much sense to you, Don't Panic! Just remember that code bloat is something you definitely want to avoid. However, this doesn't mean that you have to stay clear of templates altogether. There is a very simple solution.

If you find that the majority of your template's methods are going to generate virtually identical code, then they are not really part of the template at all. Why not put them into a non-template class and have your template call them using very simple inline methods? The easiest way to do this is by having your template inherit from a non-template class that contains most of the actual code. This technique is known as a template class with a non-template base, and is sufficient to cure most kinds of code bloat. There are other solutions for the more difficult cases, but we won't go into those here. If you are interested, try looking up explicit instantiation and partial specialization in any good C++ book.

A Solid Foundation

The algorithms to copy, append and compare Pascal strings are the same regardless of the buffer length. The first step in building our C++ encapsulation is therefore to define a non-template base class that implements this functionality. We could of course simply use global functions, but by including them in the class, we can give them shorter, simpler names. We also don't have to worry whether they provide behavior that would be needed when used from outside the class, such as returning an error on overflow.

The class declaration looks like this:

class PascalStringBase {
protected:
	typedef unsigned char uchar;
	static void assign(int max, uchar* dst, const uchar*);
	static void assign(int max, uchar* dst, const char*);
	static void append(int max, uchar* dst, const uchar*);
	static void append(int max, uchar* dst, const char*);
	static void append(int max, uchar* dst, uchar);
	static void append(int max, uchar* dst, char);
	static int  compare(const uchar*, const uchar*);
	static int  compare(const uchar*, const char*);
	static bool equal(const uchar*, const uchar*);
	static bool equal(const uchar*, const char*);
};

Note that it is possible to pass in both C and Pascal strings for assignment, concatenation and comparison. This is done by overloading the function names to handle both signed and unsigned string pointers. We also haven't arranged to return any error codes, as we are not interested in checking for error codes whilst building strings. Therefore the assign and append functions silently truncate the destination string if overflow occurs.

There is a good reason why all these functions are declared static. The string buffer, referred to in these functions by the dst pointer, has to be a part of the template, since the length will be a template argument. The buffer address must therefore be passed as one of the arguments, along with its length. Since these methods do not refer to any data members (instance variables), they are independent of any instance of the class and they should properly be made static. In the same way, no constructors or destructor are necessary since there is nothing to initialize or release.

The implementation of PascalStringBase is not very interesting, so we won't include it here. It defines a local version of strlen to help with the C strings, and it uses BlockMoveData for copying data around. I opted to use RelString and CompareString for the Pascal comparisons, but this would need to be changed if the code was ever going to be used from an interrupt routine. The other methods could also have used standard Toolbox or glue functions, but overflow management would still have been necessary.

Dressing It Up

Armed with this low-level functionality, we can define a clean, efficient C++ encapsulation of Pascal strings:

template<unsigned char max>
class PascalString : public PascalStringBase {

protected:
	uchar data[max + 1];

public:
	PascalString()
			{ clear(); }
	PascalString(const PascalString& p)
			{ *this = p; } // Calls operator =
	PascalString(const uchar* p)
			{ *this = p; } // Calls operator =
	PascalString(const char* p)
			{ *this = p; } // Calls operator =

	PascalString& clear()
			{ data[0] = 0; return *this; }
	
	// Assignment
	PascalString& operator = (const PascalString& p)
			{ assign(max, data, p); return *this; }
	PascalString& operator = (const uchar* p)	
			{ assign(max, data, p); return *this; }
	PascalString& operator = (const char* p)
			{ assign(max, data, p); return *this; }

	// Concatenation
	PascalString& operator += (const uchar* p)
			{ append(max, data, p); return *this; }
	PascalString& operator += (const char* p)
			{ append(max, data, p); return *this; }

	PascalString& operator += (uchar c)
			{ append(max, data, c); return *this; }
	PascalString& operator += (char c)
			{ append(max, data, c); return *this; }
	
	// Comparison
	bool operator <  (const uchar* p) const
			{ return compare(data, p) < 0; }
	bool operator <  (const char* p) const
			{ return compare(data, p) < 0; }
	bool operator == (const uchar* p) const
			{ return equal(data, p); }
	bool operator == (const char* p) const
			{ return equal(data, p); }

	// Conversion operator
	operator const uchar * () const { return data; }
	
	// Information
	uchar length()  const { return data[0]; }
	uchar maximum() const { return max; }
};

All of the methods are trivial wrappers for the 'worker' functions from the base class. Yet, the constant template argument max takes care of the ugly details of buffer allocation and length checking. objects of any size can be defined as stack variables, and take up no more space than the data itself. In fact, because the memory layout of the object is the same as the equivalent raw Pascal string, an unsigned char* can be cast to a PascalString<nnn>* that is used without restriction. A static inline method that performs such a cast would be a useful addition.

Note that because we have declared max as an unsigned char, the compiler will ensure that its value is within the correct range.

PascalString explicitly defines three of the fundamental four methods that always need to be considered when designing a class.

  • The default constructor (no arguments) is necessary to ensure that the default value of a PascalString is a zero-length string.
  • If the copy constructor (const PascalString&) is not explicitly defined, the compiler will supply an automatic version which copies every byte of the entire object. For larger values of max, and short strings, this is undesirable. Like the others, this constructor delegates the work to the equivalent assignment operator. No prior initialization is needed, since any previous state is ignored by PascalString's assignment operators.
  • The compiler will also supply a default assignment operator if one is not explicitly defined. Again, this would copy the entire object in every case. We already have an assign function that takes a const unsigned char*, so we make implicit use of the conversion operator and call this function to perform the copy.
  • The destructor is the only one of the four that is not defined, since there is no cleanup needed when a PascalString is destroyed.

The conversion operator allows a PascalString to be used in any context that requires a raw Pascal string. It implicitly converts a PascalString to a const unsigned char*. However, since this gives up any control of overflow checking, the pointer has to be const to prevent modification of the internal data. If max is 255, it is always safe to pass a writable pointer to the internal data, but there is no simple way to arrange for this to happen automatically for just this value of max. A compromise would be to define a method that takes a size as an argument, returns a writable pointer to the data if the size is less than or equal to max, and returns nil otherwise. This would allow a PascalString to be passed as a receive-buffer to Toolbox routines without the need for casting.

A more radical solution would be to fool around with MacTypes.h to redefine Str31 as PascalString<31>* and so on, so that the Mac OS APIs appear to be expecting instances of our template class instead of raw pointers. Since the pointer value that is passed is the same, the APIs will work as expected, but the API arguments will automatically be checked for correctness of length. It would be more correct, but more work, to write inline wrapper functions for all the APIs that take writable Pascal strings as arguments. These would simply perform a suitable cast on their PascalString& argument and pass the result on to the OS. The API name could be retained if the wrappers were placed in a different namespace.

Heading For The Goal

Our main goal is the creation of a stream class. This is not hard now that we have the needed PascalString class.

It doesn't make much sense to build a stream on anything less than a PascalString<255>, so our stream class doesn't need to be a template since we don't need to specify a size. C++ does allow us to specify default values for template arguments, so we could make it a template with a default size of 255. However, this would start another round in the fight against code bloat, and there is no need for this additional complication. Remember, simple is beautiful!

We now have the interesting phenomenon of a class inheriting from a template inheriting from a class:

class PascalStringStream : public PascalString<255>
{
public:
	// Concatenation
	PascalStringStream& operator << (const unsigned char* s)
			{ return *this += s; }
	PascalStringStream& operator << (const char* s)
			{ return *this += s; }
	PascalStringStream& operator << (unsigned char c)
			{ return *this += c; }
	PascalStringStream& operator << (char c)
			{ return *this += c; }

	// Numbers
	PascalStringStream& operator << (long);
	PascalStringStream& operator << (double);

	// Convenience
	const unsigned char* contents() const { return *this; }
	PascalStringStream&  reset() { clear(); return *this; }
};

It makes sense to use inheritance rather than aggregation (embedding PascalString as a data member), since we want our stream to be more or less interchangeable with a PascalString. It also saves us the effort of redeclaring several methods. However, using aggregation also works quite well.

We have chosen not to implement advanced features such as field-widths and justification. This is very rarely appropriate in a GUI context with proportional-width fonts, and we want to keep the size and complexity to a minimum.

Why create an extra class?

The concatenation methods are just re-presentations of the ones from PascalString, using a different operator. Why bother with this? Because the associativity of << is different from +=, so that it becomes possible to 'cascade' a series of concatenations in typical ostream style:

s << "\pOverwrite " << oldf << "\p with " << newf << "\p?"

In which case, why not just put all this functionality into PascalString? Because accidental use of some of PascalStringStream's operators may bring surprises. For example, if we accidentally pass an int instead of a char to a concatenation operation on a string, do we want a decimal representation appended to the end of a string? A warning from the compiler would be preferable. By requiring us to include stream functionality explicitly, through our choice of class, these ambiguities are avoided.

There is another advantage to using the << operator. Every expression that uses a PascalStringStream is upwardly compatible with the standard C++ ostream. If, for example, you write some utility code that dumps out parameters from OpenTransport, that code will work equally well with either type of stream. If you later decide to send the output to a file rather than to a dialog, you only need to change the stream declaration and everything will work as expected. If the formatting capabilities of PascalStringStream turn out to be too simple, you can move up to using an ostringstream without a problem.

It is even possible to place your text-generating code inside a template function (not a template class), so that it will work with any type of stream you choose to pass to it. Just prefix the function or method definition with template<class Stream> and use Stream wherever you need to declare a stream as a variable or an argument. The compiler can infer the template argument from the function arguments, so you can still call the function as if it were a non-template one.

Internal details

There are only two methods that are not trivial and inline. These are implemented in a .cp file. The details are not very interesting, but for completeness, we'll include them here. They each create a text representation of the number in a buffer on the stack, and append it to the main string.

#include <fp.h>		// Mac OS APIs for manipulating floating-point numbers

// Decimal integer output

PascalStringStream&
		PascalStringStream::operator << (long n)
{
	Str31 num;
	NumToString(n, num);
	return *this << num;
}

// Quick-and-easy floating-point output: X.XXXXXe±Y

const int precision	=  6;

PascalStringStream&
		PascalStringStream::operator << (double d)
{
	decform form;
	decimal result;
	char output[DECSTROUTLEN];

	form.style = FLOATDECIMAL;
	form.digits = precision;
	num2dec(&form, d, &result);
	
	// Use fixed-point notation if it fits within a small enough space
	if (3 >= result.exp && result.exp >= -precision - 3)
	{
		form.style = FIXEDDECIMAL;
		form.digits = -result.exp;
		num2dec(&form, d, &result);
	}

	dec2str(&form, &result, output);

	return *this << output;
}

Again, this code should not be called at interrupt time, due to the use of NumToString. However, it would be very easy to replace the call to NumToString with something homegrown that doesn't move memory.

Note that it would not be hard to make PascalStringStream fully compatible with Mac OS international number formatting, which would not be possible with a standard ostream.

Putting it all to use

The reset method is provided as a convenience. It enables a single stream to be reused multiple times in the same function, and because it returns a reference to the stream, it can be used as the first operation in a cascade:

	gMessage.reset() << "\pReading data from " << filename;

An entire cascade expression can also be passed as the argument to a function:

	SetDialogItemText(item, message << "Hello, " << name);

In this case, a C string literal has been used instead of a Pascal one; it now makes very little difference which kind is used.

Rather than defining a single stream at the start and reusing it repeatedly, it is also possible to use unnamed temporaries for creating and passing stream-generated text:

	SetDialogItemText(item,
		PascalStringStream() << "Hello, " << name);

This allocates and constructs an unnamed temporary variable on the stack, performs all the operations in the cascade, passes a const unsigned char * to SetDialogItemText, and destroys the variable when it is no longer needed. Quite a lot of work in one line! And not too cryptic either.

The only drawback is that the compiler's idea of 'no longer needed' may be the end of the current block, and not just the statement containing the call to SetDialogItemText. If you use many calls like this in one function, you can end up needing a lot of stack space. This isn't usually much of a problem, but you do need to be aware of it. The best solution is to have a reusable named variable instead. Another is to limit the scope using additional sets of braces. It is a pity that one of the final additions to the official C++ standard was to increase the longevity of unnamed temporaries.

A note on localization

All of our examples have used embedded string literals and string variables. In a production program, we would need to allow for localization by storing strings in resources. It is quite possible to do this in combination with PascalStringStream, and a class that can assist with this is mentioned in a later section.

However, there is a well-known localization issue that PascalStringStream does not help to address. This concerns the order of substitutions in a generated message. Different natural languages may need to make the data insertions in different orders, so the localization process needs to be able to specify the relative order of the different parts of the message. This is handled in a primitive way by the ParamText mechanism, but a general solution is more of a challenge.

A solution is more or less impossible when using PascalStringStream, because the substitution pattern is hard-coded within the program. On the other hand, allowing substitutions to be specified apart from the code makes it impossible to check for consistency with the data, which was one of our key design criteria. Real life is full of conflicts!

PascalStringStream is therefore not a universal solution to all text-handling needs within the UI. It is however both powerful and easy to use in a wide variety of situations. It is especially valuable for debugging, and for the development of applications that will not be heavily localized.

Bombs Away!

Before I developed PascalStringStream, I thought I was very smart in defining an error-reporting function that used ParamText to concatenate its arguments, and used C++ overloading to allow this to be called with a variety of argument numbers and types. This worked quite well in some ways, although I sometimes found it frustrating not being able to handle more than four substitutions.

I definitely found it a problem, however, that my library function was dependent on resources. This meant that the appropriate resource file had to be included in the build alongside the library code, and resource IDs for the ALRT and DITL had to be reserved for the library. It also meant that my reporting functions were not completely bombproof. If the resource map got scribbled on and was no longer functional, or the resources didn't get included in the build, there was nothing I could do except 'beep in despair'.

Another frustration was that the Alert always had a fixed size, regardless of the amount of text in it. It needed to be big enough to hold the largest amount of text that the function was capable of displaying (4 x 255 bytes), and yet this looked clumsy in the common case when only a few bytes were used.

I must confess I was rather envious of our fellow-programmers 'on the other side'. That system has an API that takes a string and puts up a dialog box that is sized to fit the text. There is virtually no way that the call can fail since it is handled by the system.

That was until I thought of using the Mac OS Notification Manager. This system-provided facility does not depend on resources, is almost incapable of failing, can be used from an interrupt routine or extension, and (on Mac OS 8) sizes itself to fit the text! The only restriction is that it doesn't support the ParamText mechanism. That is what finally spurred me into designing PascalStringStream.

A better mousetrap

I replaced my once-great set of error functions with very economical equivalents layered on top of PascalStringStream and the Notification Manager. To assist with this, I wrote a simple function that wrapped the NM interface, called Notify:

static pascal void callbackProc(NMRecPtr record) {
	record->nmRefCon = 1;	// Signal completion
}

void Notify(const unsigned char* msg, bool beep) {

	// Code Fragment Manager stuff
#if TARGET_RT_MAC_CFM
	RoutineDescriptor callbackRoutine =
			BUILD_ROUTINE_DESCRIPTOR(uppNMProcInfo,
					 callbackProc);
#else
	#define callbackRoutine callbackProc
#endif

	if (msg == 0)
		msg = "\p<null message>";

	// Prepare notification request
	NMRec request;
	request.qType = nmType;
	request.nmMark = 1;
	request.nmIcon = 0;
	request.nmSound = beep ? Handle(-1) : 0; // -1 means system beep
	request.nmStr = const_cast<StringPtr>(msg);
	request.nmResp = &callbackRoutine;
	request.nmRefCon = 0;
	
	// This can only fail if qType != nmType, which is impossible here
	if (NMInstall(&request) != noErr) {
		SysBeep(60);
		return;
	}
	
	// Await completion before destroying msg
	while (request.nmRefCon == 0) {
		EventRecord event;
		// Wait 10 ticks, and don't fetch any events
		WaitNextEvent(0, &event, 10, 0);
	}

	NMRemove(&request);
}

Of course, the call to WaitNextEvent would need to be changed if this code was used in an extension or an interrupt routine. The details of the replacement would be determined by the context. The most likely strategy would be to require that msg was global or static, and to enable auto-removal of the NMRec by the Notification Manager instead of the callback routine. A more sophisticated solution would be to make Notify a mix-in class instead of a function, with a virtual method for the callback.

Wrapping it up

The error-reporting functions now look like this:

// Use a global to avoid double-evaluation in macros
static OSErr					gLastError;
// Save space by defining repeated text once
static unsigned char	gErrorIntro[] = "\p\r\rError ";

// Display a stream message, with and without a beep
#define Message(X)	Notify(PascalStringStream() << X, false)
#define Error(X)		Notify(PascalStringStream() << X, true)

// Display a stream message and an error number
#define Failure(E, X)	Error(X << gErrorIntro << (E))

// Conditionally display an OS error message
#define OSFailure(E, X) \
	((gLastError = (E)) != noErr && \
	 (Failure(gLastError, X), true))

// Conditionally display a Resource error message
#define ResFailure(H, X) \
	((H) == nil && \
	 (gLastError = ResError(), Failure(gLastError, X), true))

These functions are used like this:

Message("\pHello, " << name);

Failure(theErr, "\pCould not write " << filename);

if (OSFailure(FSpOpenDF(&spec, fsWrPerm, &refNum),
			"Could not open "<<name<<" for writing")
	return;

The reason these are defined as macros and not functions is twofold. First, a macro can supply the repetitive PascalStringStream() << part. There is no way to avoid including this somewhere in the caller. Second, with the conditional versions, the message-building code does not need to be evaluated when there is not an error. If these were real functions, the message would have to be passed as an argument and therefore would always be evaluated.

Bells And Whistles

Although we chose not to include field-width and justification features in our stream class, a few more features would be extremely useful when displaying debug messages, with DebugStr or with Notify. In particular, we do not have any way to display hexadecimal values. It is also very helpful to have a convenient way of displaying four-character codes such as file and resource types.

It would be possible to add more machinery to PascalStringStream to handle these cases, but there is a cleaner way to achieve the same result. Recall how our number-formatting routines created a text representation of the number in a buffer on the stack, and then appended that to the main string. We can use exactly the same technique for other types of data, without adding more methods to our stream. Instead, we create a subclass of PascalString for each type that we wish to format. This will have a constructor that converts the datatype into its text representation. The newly constructed object can then be passed to the stream for inclusion in the output. An unnamed temporary is ideal for this purpose.

Applying this to the additional features already mentioned, we can define classes hex and ostype that are used like this:

	message << "Mask: " << hex(mask, 8);
	message << "File type: " << ostype(type);

The first line will zero-fill the value of mask to 8 digits and append it to message. The second line appends the four characters of type, without quotes.

These classes are declared in a header file like this:

class hex : public PascalString<sizeof(unsigned long) * 2> {
	public:
		hex(unsigned long x, int width = 0);
};

class ostype : public PascalString<sizeof(OSType)> {
	public:
		ostype(OSType t);
};

The default value of the width parameter of the hex constructor suppresses zero-filling. An additional constructor taking a void * argument could be defined if it is necessary to display the values of pointers.

The implementation of these constructors can be placed in a .cp file, since these are not template classes. The hex constructor chops its argument into nibbles and appends the corresponding characters to the buffer. The ostype constructor performs a four-byte copy from its argument to its buffer and sets the length to four. In both cases, the underlying string buffer is just big enough to contain the corresponding data, and no more. As you can see, any constant expressions can be used as the size.

A similar class could be defined that initializes itself with a string stored in a resource. Such helper classes are easy to implement and easy to use, largely because of the design of the PascalString class that they are based on.

The Thoroughly Modern Macintosh

There is no longer a need to use printf in a modern C++ program. The iostream classes are superior in almost every way. In some situations standard iostreams come at too high a cost, or don't work with our data, but we still don't have to sacrifice the entire ostream paradigm. The amazing flexibility of C++ allows us to develop extensions to the language that are compatible with the standard library, and yet are well adapted to our specific context.

The solution involves templates, inheritance, inline and non-inline functions, and the use of unnamed temporaries, yet nothing is obscure or complicated. An average programmer with suitable experience should be able to produce this kind of tool on a regular basis. The effort involved in developing the PascalString and PascalStringStream classes was not high, but the results have wide-ranging potential for reusability.

So throw out those out-of-date techniques, and spend a little time each week polishing your tools-the results are definitely worth it.


Neil Mayhew works for Wycliffe Bible Translators, a non-profit organization dedicated to translating the Bible for the world's 400 million people that do not have it in their own language. Neil started programming in C in 1983, and graduated to the Mac in 1989. When he's not at his Mac or trying to beat his kids at video games you might find him flying a stunt kite if it's windy, or throwing a boomerang if it's not.

 
AAPL
$97.67
Apple Inc.
+0.64
MSFT
$44.50
Microsoft Corpora
+0.10
GOOG
$589.02
Google Inc.
-4.33

MacTech Search:
Community Search:

Software Updates via MacUpdate

TinkerTool 5.3 - Expanded preference set...
TinkerTool is an application that gives you access to additional preference settings Apple has built into Mac OS X. This allows to activate hidden features in the operating system and in some of the... Read more
Audio Hijack Pro 2.11.0 - Record and enh...
Audio Hijack Pro drastically changes the way you use audio on your computer, giving you the freedom to listen to audio when you want and how you want. Record and enhance any audio with Audio Hijack... Read more
Intermission 1.1.1 - Pause and rewind li...
Intermission allows you to pause and rewind live audio from any application on your Mac. Intermission will buffer up to 3 hours of audio, allowing users to skip through any assortment of audio... Read more
Autopano Giga 3.6 - Stitch multiple imag...
Autopano Giga allows you to stitch 2, 20, or 2,000 images. Version 3.0 integrates impressive new features that will definitely make you adopt Autopano Pro or Autopano Giga: Choose between 9... Read more
Airfoil 4.8.7 - Send audio from any app...
Airfoil allows you to send any audio to AirPort Express units, Apple TVs, and even other Macs and PCs, all in sync! It's your audio - everywhere. With Airfoil you can take audio from any... Read more
Microsoft Remote Desktop 8.0.8 - Connect...
With Microsoft Remote Desktop, you can connect to a remote PC and your work resources from almost anywhere. Experience the power of Windows with RemoteFX in a Remote Desktop client designed to help... Read more
xACT 2.30 - Audio compression toolkit. (...
xACT stands for X Aaudio Compression Toolkit, an application that encodes and decodes FLAC, SHN, Monkey’s Audio, TTA, Wavpack, and Apple Lossless files. It also can encode these formats to MP3, AAC... Read more
Firefox 31.0 - Fast, safe Web browser. (...
Firefox for Mac offers a fast, safe Web browsing experience. Browse quickly, securely, and effortlessly. With its industry-leading features, Firefox is the choice of Web development professionals... Read more
Little Snitch 3.3.3 - Alerts you to outg...
Little Snitch gives you control over your private outgoing data. Track background activityAs soon as your computer connects to the Internet, applications often have permission to send any... Read more
Thunderbird 31.0 - Email client from Moz...
As of July 2012, Thunderbird has transitioned to a new governance model, with new features being developed by the broader free software and open source community, and security fixes and improvements... Read more

Latest Forum Discussions

See All

Reddme for iPhone - The Reddit Client (...
Reddme for iPhone - The Reddit Client 1.0 Device: iOS iPhone Category: News Price: $.99, Version: 1.0 (iTunes) Description: Reddme for iPhone is an iOS 7-optimized Reddit client that offers a refreshing new way to experience Reddit... | Read more »
Jacob Jones and the Bigfoot Mystery : Ep...
Jacob Jones and the Bigfoot Mystery : Episode 2 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Jacob Jones is back in Episode 2 of one of Apples 'Best of 2013' games and an App Store... | Read more »
New Trailer For Outcast Odyssey, A New K...
New Trailer For Outcast Odyssey, A New Kind of Card Battler Posted by Jennifer Allen on July 25th, 2014 [ permalink ] Out this Fall is a new kind of card battle game: Outcast Odyssey. | Read more »
Garfield: Survival of the Fattest Coming...
Garfield: Survival of the Fattest Coming to iOS this Fall Posted by Jennifer Allen on July 25th, 2014 [ permalink ] Who loves lasagna? Me. Also everyone’s favorite grumpy fat cat, Garfield. | Read more »
Happy Flock Review
Happy Flock Review By Andrew Fisher on July 25th, 2014 Our Rating: :: HERD IT ALL BEFOREUniversal App - Designed for iPhone and iPad Underneath the gloss of Happy Flock’s visuals is a game of very little substance. It’s cute, but... | Read more »
Square Register Updates Adds Offline Pay...
Square Register Updates Adds Offline Payments Posted by Ellis Spice on July 25th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Looking For Group – Hearthstone’s Curse...
For the first time since its release (which has thankfully been a much shorter window for iPad players than their PC counterparts), Blizzard’s wildly successful Hearthstone: Heroes of Warcraft CCG is sporting some brand new content: the single... | Read more »
Poptile Review
Poptile Review By Jennifer Allen on July 25th, 2014 Our Rating: :: SIMPLY FUNUniversal App - Designed for iPhone and iPad Simple yet a little bit glorious, Poptile is a satisfying entertaining puzzle game with oodles of the ‘one... | Read more »
Modern Combat 5: Blackout Review
Modern Combat 5: Blackout Review By Brittany Vincent on July 25th, 2014 Our Rating: :: LESS QQ, MORE PEW PEWUniversal App - Designed for iPhone and iPad The fifth entry into the blockbuster Modern Combat series is what mobile... | Read more »
Watch and Share Mobile Gameplay Videos W...
Watch and Share Mobile Gameplay Videos With Kamcord Posted by Jennifer Allen on July 25th, 2014 [ permalink ] iPhone App - Designed for the iPhone, compatible with the iPad | Read more »

Price Scanner via MacPrices.net

Apple’s 2014 Back to School promotion: $100 g...
 Apple’s 2014 Back to School promotion includes a free $100 App Store Gift Card with the purchase of any new Mac (Mac mini excluded), or a $50 Gift Card with the purchase of an iPad or iPhone,... Read more
iMacs on sale for $150 off MSRP, $250 off for...
Best Buy has iMacs on sale for up to $160 off MSRP for a limited time. Choose free home shipping or free instant local store pickup (if available). Prices are valid for online orders only, in-store... Read more
Mac minis on sale for $100 off MSRP, starting...
Best Buy has Mac minis on sale for $100 off MSRP. Choose free shipping or free instant local store pickup. Prices are for online orders only, in-store prices may vary: 2.5GHz Mac mini: $499.99 2.3GHz... Read more
Global Tablet Market Grows 11% in Q2/14 Notwi...
Worldwide tablet sales grew 11.0 percent year over year in the second quarter of 2014, with shipments reaching 49.3 million units according to preliminary data from the International Data Corporation... Read more
New iPhone 6 Models to Have Staggered Release...
Digitimes’ Cage Chao and Steve Shen report that according to unnamed sources in Apple’s upstream iPhone supply chain, the new 5.5-inch iPhone will be released several months later than the new 4.7-... Read more
New iOS App Helps People Feel Good About thei...
Mobile shoppers looking for big savings at their favorite stores can turn to the Goodshop app, a new iOS app with the latest coupons and deals at more than 5,000 online stores. In addition to being a... Read more
Save on 5th generation refurbished iPod touch...
The Apple Store has Apple Certified Refurbished 5th generation iPod touches available starting at $149. Apple’s one-year warranty is included with each model, and shipping is free. Many, but not all... Read more
What Should Apple’s Next MacBook Priority Be;...
Stabley Times’ Phil Moore says that after expanding its iMac lineup with a new low end model, Apple’s next Mac hardware decision will be how it wants to approach expanding its MacBook lineup as well... Read more
ArtRage For iPhone Painting App Free During C...
ArtRage for iPhone is currently being offered for free (regularly $1.99) during Comic-Con San Diego #SDCC, July 24-27, in celebration of the upcoming ArtRage 4.5 and other 64-bit versions of the... Read more
With The Apple/IBM Alliance, Is The iPad Now...
Almost since the iPad was rolled out in 2010, and especially after Apple made a 128 GB storage configuration available in 2012, there’s been debate over whether the iPad is a serious tool for... Read more

Jobs Board

WW Sales Program Manager, *Apple* Online St...
**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* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.