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
$111.78
Apple Inc.
-0.87
MSFT
$47.66
Microsoft Corpora
+0.14
GOOG
$516.35
Google Inc.
+5.25

MacTech Search:
Community Search:

Software Updates via MacUpdate

NeoOffice 2014.6 - Mac-tailored, OpenOff...
NeoOffice is a complete office suite for OS X. With NeoOffice, users can view, edit, and save OpenOffice documents, PDF files, and most Microsoft Word, Excel, and PowerPoint documents. NeoOffice 3.x... Read more
LibreOffice 4.3.5.2 - Free Open Source o...
LibreOffice is an office suite (word processor, spreadsheet, presentations, drawing tool) compatible with other major office suites. The Document Foundation is coordinating development and... Read more
CleanApp 5.0.0 Beta 5 - Application dein...
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
Monolingual 1.6.2 - 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. It requires a 64-bit capable Intel-based Mac and at least... Read more
NetShade 6.1 - Browse privately using an...
NetShade is an Internet security tool that conceals your IP address on the web. NetShade routes your Web connection through either a public anonymous proxy server, or one of NetShade's own dedicated... Read more
calibre 2.13 - Complete e-library manage...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital librarian... Read more
Mellel 3.3.7 - Powerful word processor w...
Mellel is the leading word processor for OS X and has been widely considered the industry standard since its inception. Mellel focuses on writers and scholars for technical writing and multilingual... Read more
ScreenFlow 5.0.1 - Create screen recordi...
Save 10% with the exclusive MacUpdate coupon code: AFMacUpdate10 Buy now! ScreenFlow is powerful, easy-to-use screencasting software for the Mac. With ScreenFlow you can record the contents of your... Read more
Simon 4.0 - Monitor changes and crashes...
Simon monitors websites and alerts you of crashes and changes. Select pages to monitor, choose your alert options, and customize your settings. Simon does the rest. Keep a watchful eye on your... Read more
BBEdit 11.0.2 - Powerful text and HTML e...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more

Latest Forum Discussions

See All

Make your own Tribez Figures (and More)...
Make your own Tribez Figures (and More) with Toyze Posted by Jessica Fisher on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
So Many Holiday iOS Sales Oh My Goodness...
The holiday season is in full-swing, which means a whole lot of iOS apps and games are going on sale. A bunch already have, in fact. Naturally this means we’re putting together a hand-picked list of the best discounts and sales we can find in order... | Read more »
It’s Bird vs. Bird in the New PvP Mode f...
It’s Bird vs. Bird in the New PvP Mode for Angry Birds Epic Posted by Jessica Fisher on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Telltale Games and Mojang Announce Minec...
Telltale Games and Mojang Announce Minecraft: Story Mode – A Telltale Games Series Posted by Jessica Fisher on December 19th, 2014 [ permalink ] | Read more »
WarChest and Splash Damage Annouce Their...
WarChest and Splash Damage Annouce Their New Game: Tempo Posted by Jessica Fisher on December 19th, 2014 [ permalink ] WarChest Ltd and Splash Damage Ltd are teaming up again to work | Read more »
BulkyPix Celebrates its 6th Anniversary...
BulkyPix Celebrates its 6th Anniversary with a Bunch of Free Games Posted by Jessica Fisher on December 19th, 2014 [ permalink ] BulkyPix has | Read more »
Indulge in Japanese cuisine in Cooking F...
Indulge in Japanese cuisine in Cooking Fever’s new sushi-themed update Posted by Simon Reed on December 19th, 2014 [ permalink ] Lithuanian developer Nordcurrent has yet again updated its restaurant simulat | Read more »
Badland Daydream Level Pack Arrives to C...
Badland Daydream Level Pack Arrives to Celebrate 20 Million Downloads Posted by Ellis Spice on December 19th, 2014 [ permalink ] | Read more »
Far Cry 4, Assassin’s Creed Unity, Desti...
Far Cry 4, Assassin’s Creed Unity, Destiny, and Beyond – AppSpy Takes a Look at AAA Companion Apps Posted by Rob Rich on December 19th, 2014 [ permalink ] These day | Read more »
A Bunch of Halfbrick Games Are Going Fre...
A Bunch of Halfbrick Games Are Going Free for the Holidays Posted by Ellis Spice on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »

Price Scanner via MacPrices.net

13-inch 2.6GHz Retina MacBook Pro on sale for...
Best Buy has lowered their price on the 2014 13″ 2.6GHz/128GB Retina MacBook Pro to $1149.99 on their online store for a limited time. That’s $150 off MSRP and the lowest price available for this... Read more
Kodak Returns to CES With New Consumer Produ...
Former photography colossus Kodak is returning to CES for the first time in three years where the Kodak booth (#21818 South Hall 1) will showcase a wide range of innovative, imaging-related products... Read more
Invaluable Launches New Eponymously -Named A...
Invaluable, the world’s largest online live auction marketplace, hhas announced the official launch of the Invaluable app for iPad, now available for download in the iTunes App Store. Invaluable... Read more
IDC Reveals Worldwide Mobile Enterprise Appli...
International Data Corporation (IDC) last week hosted the IDC FutureScape: Worldwide Mobile Enterprise Applications and Solutions 2015 Predictions Web conference. The session provided organizations... Read more
Hello Vino Wine App Launches “Safe Ride Home”...
Hello Vino has announced addition of a new “Get a Safe Ride Home” feature in its Food & Drink app with a direct connection to Uber, the technology platform that connects users with rides. The... Read more
DEVON-technologies Releases DEVONthink To Go...
Coeur d’Alene, Idaho based DEVON-technologies, LLC has updated DEVONthink To Go, its mobile companion to DEVONthink, to version 1.5. The update includes an iOS 8 extension, compatibility with the... Read more
The Apple Store offering free next-day shippi...
The Apple Store is now offering free next-day shipping on all in stock items if ordered before 12/23/14 at 10:00am PT. Local store pickup is also available within an hour of ordering for any in stock... Read more
It’s 1992 Again At Sony Pictures, Except For...
Techcrunch’s John Biggs interviewed a Sony Pictures Entertainment (SPE) employee, who quite understandably wished to remain anonymous, regarding post-hack conditions in SPE’s L.A office, explaining “... Read more
OtterBox Defender Series Case For iPad mini 3...
With their innovative Touch ID technology and ultrathin profile, the latest tranche of Apple iPads are more desirable than ever, and OtterBox has just announced the Defender Series custom-engineered... Read more
Holiday sales this weekend: MacBook Pros for...
 B&H Photo has new MacBook Pros on sale for up to $300 off MSRP as part of their Holiday pricing. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $1699... Read more

Jobs Board

*Apple* Store Leader Program (US) - Apple, I...
…Summary Learn and grow as you explore the art of leadership at the Apple Store. You'll master our retail business inside and out through training, hands-on experience, Read more
Project Manager, *Apple* Financial Services...
**Job Summary** Apple Financial Services (AFS) offers consumers, businesses and educational institutions ways to finance Apple purchases. We work with national and 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
*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
*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.