Sourceforge.net - The VCF's Project Host
   The VCF Website Home   |   Online Discussion Forums   |   Sourceforge.net Project Page   

Chapter 3. Core Libraries

Table of Contents

3.1. FoundationKit
3.1.1. Uses
3.1.2. Startup and Shutdown
3.1.3. Command line parameters
3.1.4. VCF::Object - the VCF base class
3.1.5. System Toolkit
3.1.6. Reference Counting
3.1.7. Components
3.1.8. Exceptions
3.1.9. RTTI
3.1.10. Delegates and Callbacks (aka Event Handling)
3.1.11. Multithreaded programming support
3.1.11.1. Synchronization Primitives
3.1.11.1.1. SynchObjects
3.1.11.1.2. Waiting for ...
3.1.11.1.3. Mutexes
3.1.11.1.4. Semaphores
3.1.11.1.5. Conditions
3.1.11.2. Threads
3.1.11.2.1. Thread Manager
3.1.11.2.2. Run Loops
3.1.11.2.3. Threaded Functions
3.1.11.2.4. Thread Pools
3.1.11.2.5. Asynchronous Delegates
3.1.12. Strings
3.1.12.1. STL usage
3.1.12.2. Data Representation
3.1.12.3. Language Encoding
3.1.12.4. Conversion and Text Codecs
3.1.12.5. Formatting
3.1.12.6. Utilty Functions
3.1.12.6.1. Trimming
3.1.12.6.2. Number conversion
3.1.12.6.3. UUID generation
3.1.12.6.4. Type ID
3.1.12.6.5. Formatting
3.1.13. Locales
3.1.13.1. Naming
3.1.13.2. Numeric Formatting
3.1.13.3. Date Time Formatting
3.1.13.4. Collation
3.1.13.5. Translation
3.1.14. Resources
3.1.14.1. Accessing a resource
3.1.14.2. Program Information
3.1.14.3. Platform Specific
3.1.14.4. Platform Neutral
3.1.15. System Functions
3.1.15.1. String functions
3.1.15.2. Filesystem Functions
3.1.15.3. Date/Time Functions
3.1.15.4. Environment Functions
3.1.15.5. Resource functions
3.1.15.6. Locale functions
3.1.15.7. Miscellaneous functions
3.1.16. Dates and Time
3.1.16.1. Data Representation
3.1.16.2. Date Time Spans
3.1.16.3. Iterating
3.1.16.4. String Formatting
3.1.17. Streams
3.1.17.1. Base Functionality
3.1.17.2. Input Streams
3.1.17.3. Output Streams
3.1.17.4. Serializing classes
3.1.17.5. Serializing Variants
3.1.18. Files
3.1.18.1. File Searching
3.1.19. Dynamic Libraries
3.1.19.1. Init and term functions
3.1.20. Processes
3.1.20.1. Process Output
3.1.21. XML Parsing
3.1.21.1. SAX style parsing
3.1.21.2. DOM style parsing
3.2. GraphicsKit
3.2.1. The GraphicsContext
3.2.2. The GraphicsToolkit
3.2.3. Colors
3.2.4. Images
3.2.5. Fonts
3.2.6. Paths
3.2.7. Transforms
3.2.8. Strokes
3.2.8.1. Usage
3.2.8.2. Implementation
3.2.9. Fills
3.2.9.1. Usage
3.2.9.2. Implementation
3.2.10. Image Loading
3.2.11. Exceptions
3.2.12. Peer Classes
3.3. ApplicationKit
3.3.1. Introduction
3.3.2. Applications
3.3.3. UI Toolkit
3.3.3.1. Intro
3.3.3.2. UI Metrics
3.3.3.3. Event Loops
3.3.3.4. Timers
3.3.3.5. Carets
3.3.3.6. Posting Events
3.3.3.7. Default Buttons
3.3.3.8. Accelerators
3.3.3.9. Creating Peers
3.3.3.10. Miscellaneous
3.3.4. Event Loops
3.3.5. Event Types
3.3.6. Drag and Drop
3.3.7. Controls and Containers
3.3.7.1. Control Listing
3.3.8. Borders
3.3.9. Layout
3.3.9.1. Standard Container layout
3.3.9.2. Horizontal layout
3.3.10. Frames, Dialogs, and Windows
3.3.10.1. Dialog usage in VFC
3.3.10.1.1. Standard Dialogs
3.3.10.1.2. Custom Dialogs
3.3.10.2. Frame Sizing
3.3.10.3. Frame usage in the VFC
3.3.10.4. Window usage in the VFC
3.3.11. Model/View/Controller
3.3.11.1. Models
3.3.11.2. Items
3.3.11.3. Views
3.3.11.4. Controls
3.3.11.5. Document/View Architecture
3.3.12. Undo/Redo and Commands
3.3.13. Help
3.3.13.1. Implementing Help for Win32 systems
3.3.13.1.1. Creating the HTML Help project file
3.3.13.1.2. Adding HTML content
3.3.13.1.3. Adding Index/Search support
3.3.13.1.4. Adding a Table of Contents
3.3.13.1.5. Adding Context Sensitive entries
3.3.13.1.6. Adding "What's This" entries
3.3.13.1.7. Compiling the help with the HTML Help Compiler
3.3.14. Control Focus and Activation
3.3.15. Accelerator Keys ("Hot Keys")
3.3.16. Peer Classes
3.4. Component Authoring
3.4.1. Writing a Component
3.4.2. Property Editing
3.4.2.1. Creating a new property editor class
3.4.2.1.1. Attributes
3.4.2.1.2. Storing the property's value
3.4.2.1.3. Retreiving the property's value
3.4.2.1.4. Sub Properties
3.4.2.1.5. Multiple values and sorting
3.4.2.1.6. Painting the display of a property
3.4.2.2. Editing a Property
3.4.2.3. Registering Property Editors
3.4.3. Component Editing
3.4.3.1. Creating a new component editor class
3.4.3.1.1. Attributes
3.4.3.1.2. Accessing the Component Instance
3.4.3.1.3. Providing Commands
3.4.3.2. Editing a Component
3.4.3.3. Registering Component Editors
3.4.4. Saving and Loading Component State
3.4.5. Registering Components
3.4.5.1. Component Info
3.4.5.2. Component Manager
3.4.6. Distributing your Components and Editors
3.4.6.1. VPL Exported Functions
3.4.6.2. VPL Initialization
3.4.6.3. VPL Termination

3.1. FoundationKit

3.1.1. Uses
3.1.2. Startup and Shutdown
3.1.3. Command line parameters
3.1.4. VCF::Object - the VCF base class
3.1.5. System Toolkit
3.1.6. Reference Counting
3.1.7. Components
3.1.8. Exceptions
3.1.9. RTTI
3.1.10. Delegates and Callbacks (aka Event Handling)
3.1.11. Multithreaded programming support
3.1.11.1. Synchronization Primitives
3.1.11.1.1. SynchObjects
3.1.11.1.2. Waiting for ...
3.1.11.1.3. Mutexes
3.1.11.1.4. Semaphores
3.1.11.1.5. Conditions
3.1.11.2. Threads
3.1.11.2.1. Thread Manager
3.1.11.2.2. Run Loops
3.1.11.2.3. Threaded Functions
3.1.11.2.4. Thread Pools
3.1.11.2.5. Asynchronous Delegates
3.1.12. Strings
3.1.12.1. STL usage
3.1.12.2. Data Representation
3.1.12.3. Language Encoding
3.1.12.4. Conversion and Text Codecs
3.1.12.5. Formatting
3.1.12.6. Utilty Functions
3.1.12.6.1. Trimming
3.1.12.6.2. Number conversion
3.1.12.6.3. UUID generation
3.1.12.6.4. Type ID
3.1.12.6.5. Formatting
3.1.13. Locales
3.1.13.1. Naming
3.1.13.2. Numeric Formatting
3.1.13.3. Date Time Formatting
3.1.13.4. Collation
3.1.13.5. Translation
3.1.14. Resources
3.1.14.1. Accessing a resource
3.1.14.2. Program Information
3.1.14.3. Platform Specific
3.1.14.4. Platform Neutral
3.1.15. System Functions
3.1.15.1. String functions
3.1.15.2. Filesystem Functions
3.1.15.3. Date/Time Functions
3.1.15.4. Environment Functions
3.1.15.5. Resource functions
3.1.15.6. Locale functions
3.1.15.7. Miscellaneous functions
3.1.16. Dates and Time
3.1.16.1. Data Representation
3.1.16.2. Date Time Spans
3.1.16.3. Iterating
3.1.16.4. String Formatting
3.1.17. Streams
3.1.17.1. Base Functionality
3.1.17.2. Input Streams
3.1.17.3. Output Streams
3.1.17.4. Serializing classes
3.1.17.5. Serializing Variants
3.1.18. Files
3.1.18.1. File Searching
3.1.19. Dynamic Libraries
3.1.19.1. Init and term functions
3.1.20. Processes
3.1.20.1. Process Output
3.1.21. XML Parsing
3.1.21.1. SAX style parsing
3.1.21.2. DOM style parsing

The FoundationKit is the base library of the entire VCF. Both the GraphicsKit and the ApplicationKit build upon it, especially the ApplicationKit.

3.1.1. Uses

So what is the FoundationKit good for? Just about any application that would run on a command line, will find a set of useful classes within the FoundationKit. This includes support for the VCF's RTTI, input and output classes (with support for file IO too), thread and synchronization primitives like mutexs, semaphores and conditions, locale support, string support, date/time classes, support for string formatting, and even support for run loops for control over code that runs in a loop. This makes many application tasks easy to deal with, and takes care of many of the more error prone activities that you have to deal with as a programmer.

The following sections will deal with each of the core sections of the FoundationKit separately and in more detail.

3.1.2. Startup and Shutdown

The FoundationKit needs to be initialized before you can use it, and likewise needs to terminated just before your program ends. The FoundationKit is initialized by calling the static FoundationKit::init() once in your program's main() function. To shutdown the FoundationKit, and reclaim any memory it's allocated for it's own use, you need to call the FoundationKit::terminate() function. Here's an example:


int main( int argc, char** argv )
{
	VCF::FoundationKit::init( argc, argv );
	
	//rest of your program's code here....
	
	VCF::FoundationKit::terminate();
	
	return 0;
}

		

Every executable that uses the FoundationKit needs to start something like the above example, or else the FoundationKit's runtime instance's will not be properly created.

3.1.3. Command line parameters

One thing you'll want to do with your programs is identify the command line parameters that may have been passed into your program. To do this with the VCF you use the CommandLine class. In hte previous example, you'll note that we initialize the FoundationKit with the initial command line parameters passed into the programs main() entry point function. We can access these at any time by calling the FoundationKit::getCommandLine() function, which returns CommandLine object.


void someFunction()
{
	CommandLine cmdLine = FoundationKit::getCommandLine();
	if ( cmdLine.hasSwitch( "-version" ) ) {
		//display the version message! 
	}
}

		

For more detailed documentation see the source docs for the VCF::CommandLine class.

3.1.4. VCF::Object - the VCF base class

Intro.  The VCF is (mostly, with a few exceptions) designed as a single rooted class hierarchy, with the VCF::Object class as the root or base class for the rest of the framework. VCF::Object support several virtual method that you can override, such as the VCF::Object::hash() method for return a hash value for the object instance. VCF::Object::clone() can be used to "clone" a new instance from an existing one.

Reference Counting.  The VCF::Object class also supports reference counting for instances that have been allocated on the heap. A new instance starts with it's refcount initialized to 0. Calling VCF::Object::addRef() increments the refcount, and calling VCF::Object::release() decrements the refcount. If VCF::Object::release() is called and the refcount is equal to 0, then the object is destroyed by a call to VCF::Object::free().

RTTI.  You can get the VCF::Class instance from any instance of a VCF::Object by calling VCF::Object::getClass() method. If the class has been properly registered in the Class Registry, the VCF::Object::getClass() will return a pointer to the Class instance. If not, then it will return NULL. You can also get the class name of the Object instance by calling VCF::Object::getClassName().

3.1.5. System Toolkit

The System Toolkit is used by VCF developers to create instances of various FoundationKit peer interfaces. For example, the Thread class uses the System Toolkit to create the thread's internal ThreadPeer instance. You generally do not need to call any functions on this class unless you are implementing low level functionality within the FoundationKit itself.

This class is implemented for each platform that the VCF is ported to. The relationship between the OS and the System Toolkit can be seen below.

Figure 3.1. System Toolkit

System Toolkit

3.1.6. Reference Counting

As mentioned above, the VCF::Object class supports reference counting. Reference counting is useful as a form of garbage collection, albeit a manual one. It is most appropriate to use when you are unsure of the lifetime of a particular object, and/or the fact that the object may have multiple "owners" who need to keep track of the object. In this situation it helps to have reference counting.

It should be noted that this is only appropriate to use on heap allocated objects. Objects that allocated on the stack do not, and should not use the reference counting functionality.

One example might be the use of a model. For example, imagine that we have created a model object, and that 3 different objects all need to hold a reference or pointer to the single instance of the model. Yet all 3 "owners" may have different lifetimes, so who then becomes responsible for destroying the model? If one of the three were to delete the model instance before, and then one of the other "owners" were to make use of it's pointer, your program would crash, due to a reference to invalid memory that had been freed. The solution is to use reference counting. When each "owner" object is assigned the pointer to the model, it should increment the model's refcount by calling VCF::Object::addRef(). Likewise, when the owner is destroyed, it should release the object by calling VCF::Object::release(), as opposed to deleting it.

3.1.7. Components

Components represent the smallest nonvisual class that can be manipulated at design time. This manipulation takes place through property editors, event editors, and component editors. Technically the VCF RTTI system and event system will work with any class that is derived (directly or indirectly) from VCF::Object, but the VCF::Component represents the beginning of user interface related classes. In addition, any class derived from Component can be registered with the run time component information system, and will also show up in an IDE's component palette.

Components come in two flavors: non-visual components, such as the TimerComponent, and visual components, that derive from Control (which is itself derived from Component), and are visible to the user. Examples of visual components are the ListBoxControl, Window, and Panel control.

You do not create instances of Component. It is an abstract class that is intended to serve as a base class for other concrete components.

Creating and destroying Components.  Components need to be created on the heap using the typical operator new() function. However, to destroy an instance that is directly or indirectly derived from VCF::Component, you should call the component's VCF::Component::free() method if you own the object. When the VCF::Object::free() is called it first calls the virtual VCF::Object::destroy() method, allowing you customize the clean up of your class, and making it safe to call other virtual methods. After VCF::Component::destroy() returns, it then deletes the instance.

Components can be "owned" by other components, freeing the developer from having to worry about deleting the Components later on. To add a Component you simply call the Component's addComponent() method, passing in the instance of the component you want to "own". Once the component is added, it's owner will take care of destroying it when the owner is destroyed. You can also remove a component. If you remove a component, and then do not add it back to some other owner, the responsibility for destroying the component is up to you.

When components get added or removed they fire an event which can be handled. You can register for this event by adding an callback to the either the Component's ComponentAdded or ComponentRemoved delegate.

Components maintain a special state variable called component state, that indicates the current state of the component. This is useful, for example, in determining if the component is in design mode, or normal mode. The state is an enumeration of type ComponentState. In general the component's state is csNormal indicating that it's been created. When a component is in the process of being read in from a stream, it's state is marked as csLoading, and when the component is being written to a stream it's state is marked as csSaving. As a general rule most UI events are only fired if the component's state is csNormal. The other important state is the csDesigning state. This is set when the component is being edited through some visual editing tool (like the VCF Builder's Form Designer). A component in the csDesigning state can process some event, but ignores most others. This is particularly true if the component is a control. For example, if the component is a TextControl, then any keyboard events are ignored if it's state is set to csDesigning.

Components are intended to be reused by other applications. They can be part of a standalone library, that can be used by other applications, or a an application may create a series of components specifically for it's use. When part of a standalone library, library is referred to as a Visual Package Library (VPL). A VPL can be a static library, or a dynamic library in the form of a DLL or Shared Object. VPL's typically have a file extension of ".vpl". To allow this form of re-use, Components need to provide not only RTTI information, but also language specific information, such as the name of the library they belong to, the header(s) they require for usage, etc. This information is stored in two main classes, ComponentInfo, which deals with the specific details of a particular Component class, and PackageInfo, which deals with the specifics of the package libary (VPL) that the Component belongs to.

The ComponentInfo class gives detailed information about the particular component class. It provides information about the author of the component, the company (if relevant),copyright, and text containing any other relevant information the developer wishes to provide. A developer can set the image to use for the component as well (if left alone a default image will be provided).

The ComponentInfo class also provides information about which include file(s) are needed by the component. The getRequiredHeaders() method returns an Enumerator of Strings that lists the include files in the order they should be included. The headers should not contain absolute paths. If the component is part of a namespace (other than the VCF namespace) it can expose this information via the getRequiredNamespaces() method. This will list any namespaces the component requires for usage.

The PackageInfo class describes the library the component is part of. It specifies the author of the package library, the company, copyright info, and text for other information. It gives out the name of the library excluding the platform specific extension (such as .lib or .a or .so). It indicates the platforms/toolchains it supports (if no source is provided). It indicates whether it allows static linkage, dynamic linkage, or both.

3.1.8. Exceptions

The VCF uses exceptions throughout the framework as a way of indicating error conditions, as opposed to returning error codes. The rationale for this is largely practical: error codes, while useful, frequently (if not always) get ignored in real world code. Because nothing forces you to check the error code, error conditions can go unnoticed until later on, making it difficult to track down bugs. Exceptions, on the other hand, if not caught, tend to do ugly things immediately, making it much more obvious where things went wrong.

The root exception class is VCF::BasicException. All other VCF exceptions derive from this. To get the error message from a VCF exception, call VCF::BasicException::getMessage().

To throw a VCF exception, you create the exception on the stack, and pass in a VCF::String to the exceptions constructor. Some of the specific exception classes has a default constructor that fills in a default message for you. If you would like to include line and file information with your excepton message you can use the MAKE_ERROR_MSG_2 macro, for example:


String fileName = getMyFileName();
if ( fileName.empty() ) {
	throw BasicException( MAKE_ERROR_MSG_2("My File Name is Empty - please fix this ASAP!"));
}

		

This will take you error string and append the source file and line number that the exception was thrown in, which can be useful in tracking errors down.

3.1.9. RTTI

Introduction.  RTTI, or Run Time Type Information, is the process of identifying the class information about a particular object instance at run time. If you used Java at all, this is often referred to as "Reflection", and if you compare the VCF RTTI classes with Java's you'll see a great deal of similarity between the two. Other languages like Java, C#, ObjectPascal, Smalltalk, Objective C (to name a few), as well as other C++ frameworks, such as Qt, wxWindows, MFC, COM (well this isn't really a C++ only framework but we'll let that go), VRS3D and many others, also implement some sort of RTTI system to varying degrees complexity. Most of the languages mentioned previously offer full support for things like dynamic object creation from a class name, dynamic property enumeration, and dynamic method invocation. Some also have support for discovering the fields of a class (like Java) and for discovering the events the class supports. Unfortunately C++ itself offers only the ability to determine the super class of a given class, i.e. using the dynamic_cast operator, you can tell if class B inherits in some way from class A. In addition the typeid() method will return a type_info class which can tell you the class name of a particular instance at runtime. Obviously this is not enough, so many of the C++ frameworks tend to implement some form of their own RTTI. What makes the VCF unusual is the depth to which it offers RTTI features, namely:

  • object creation from a class name

  • discovery of super class

  • discovery and dynamic invocation of properties at runtime

  • discovery and dynamic invocation of methods at runtime

  • discovery of events exposed via a Delegate at runtime

  • discovery of interfaces at runtime

  • discovery and dynamic invocation of interface methods at runtime

The Basics.  RTTI in the VCF is described through a series of abstract classes, and implemented through heavy use of templates. This makes it type safe, and does not require any weird void pointer type casting. To simplify matters, a number of macros exist that enable you to specify the RTTI elements you would like to expose in your class declaration, so there are no separate build steps like IDL compiling or other pre-processor tools. These macros basically "collect" all the stuff that the C++ compiler knows about during compilation, but throws away.

Each RTTI element is registered in a central repository called the VCF::ClassRegistry - a singleton instance that is created when the FoundationKit is initialized and destroyed when the FoundationKit is terminated. This registering of a class happens only once per class type, if another attempt is made to register an existing class, the registration will fail.

The basic RTTI element is the VCF::Class class - this holds all the necessary information to describe a C++ class at runtime. At a glance, a VCF::Class has the following methods:

  • getSuperClass() - returns a VCF::Class pointer to the next super class of the class in question. Using this method, you can walk up the class hierarchy.

  • getClassName() - returns the class name.

  • getID - returns a string that should be a unique ID. While the generation of this is left to the developer, all the VCF classes that are registered use UUID's created with uuidgen, or some similar tool. It is highly recommended to use the same approach for your own classes when adding RTTI support.

  • createInstance() - this allows you to create a new default instance of the class.

  • getInterfaces() - this allows you to enumerate all the interfaces that a class may implement.

  • getMethods() - allows you to enumerate all the methods the class has

  • getProperties() - allows you to enumerate all the properties of a class

  • getDelegates() - allows you to enumerate all the delegates a class has

Registering RTTI for a class.  In order to take advantage of these features, you have to register your class with the VCF's ClassRegistry. To make this simple, there are a set of macros you can use. For example, lets declare a class called "Foo" and register it:


class Foo : public VCF::Object {
public:
	Foo(){}
};

#define FOO_CLASSID		"99c0754b-c18f-49b0-999f-81384c65fd18"
_class_rtti_( Foo, "VCF::Object", FOO_CLASSID )
_class_rtti_end_

int main( int argc, char** argv ) 
{
	FoundationKit::init( argc, argv );

	//register the class 
	REGISTER_CLASSINFO_EXTERNAL( Foo );

	FoundationKit::terminate();
	return 0;
}

As you can see it's relatively easy to register the class - the only catch is that the FoundationKit library must be initialized prior to registering the class.

Once registered with the VCF you can now work with the RTTI features. For example we could create and instance of the class like so:


//class definition omitted for clarity

int main( int argc, char** argv ) 
{
	FoundationKit::init( argc, argv );

	//register the class 
	REGISTER_CLASSINFO_EXTERNAL( Foo );


	Object* object = ClassRegistry::createNewInstance( "Foo" );

	FoundationKit::terminate();
	return 0;
}

Once the object is created we can confirm the class type by querying it's name:


Object* object = ClassRegistry::createNewInstance( "Foo" );

Class* clazz = object->getClass();
System::println( clazz->getClassName() );

At this point, you now have a Class object, that you can call the methods mentioned above.

Properties.  You can query for a class's proeprties easily by just calling the Class::getProperties() method. This will return an enumeration of Property instances. Each property can be queried for it's name, description, it's type (an enumeration of PropertyDescriptorType which indicates either the primitive type, or the fact that it's an instance of a VCF::Object), and probably most importantly, the ability to get and set the values of the property. Let's look a little closer:


Class* clazz = object->getClass();
Enumerator<Property*>* properties = clazz->getProperties();
while ( properties->hasMoreElements() ) {
	Property* property = properties->nextElement();
	String name = property->getName();
	String description = property->getDescription();
	String typeName = property->getTypeClassName();

	VariantData* value = property->get();
}

Note the call to Property::get() to retreive the current value of the Property instance. It returns a VariantData object which is acts like VB's Variant type, or CORBA's Any object. A VariantData instance can store any primitive type or Object type.

3.1.10. Delegates and Callbacks (aka Event Handling)

Many frameworks have some sort of mechanism to notify others when something else changes, for example, the user clicks on a form, or an object is deleted. Usually this involves 1 or more callback functions and some way of invoking them with a series of parameters. C++ introduces some special challenges here due to the way the language differentiates static function pointers from class member function pointers. The VCF's previous technique for dealing with this had a number of limitations that have now been fixed, as well as adding the ability to work asynchronously.

Previously the VCF was limited to callback functions (sometimes referred to as "event handlers") with a very specific type of function signature. This is no longer the case. You can now define a notification to use any sort of function signature you'd like, with a limit of up to 8 function parameters. The function signature also no longer has to just be "void", you are now allowed to have arbitrary return values. The idea of a Delegate is still in use, however the idea of an "event handler" has been replaced with the more generic term of callback.

What is a callback? A callback is simply a function pointer. Specifically it is represented in the VCF by the CallBack class. The callback, like an event handler, has a name that can be used to identify it. A callback may have a "source" object that it belongs to if it represents a C++ member function. A "source" in this case is the class instance that is bound with the member function pointer so that the function is triggered on the correct class instance. If the source instance is derived from ObjectWithCallbacks, then the source will clean up the callback for you, and you can retreive the callback by the name that you gave it.

The delegate is kind of a super function pointer, essentially the same, in principal, as .Net's multicast delegate. A delegate is represented by the Delegate base class. A Delegate maintains a collection of 0 or more CallBack instances. When you "invoke" a delegate, the delegate manages the job of invoking all of it's callbacks.

An object that wishes to fire "event", so to speak, uses a Delagate as the way to allow others to be notified (through callbacks). When the object needs this notification to take place it uses the specific delegate and calls the invoke method of the delegate. For example, let say we had a simple class called Person, that had a method called talk(). When talk() is called, we want to allow others to know that the Person object is "talking", so we define a Delegate member variable called Talking. This allows other objects to register with the Talking delegate, and then be nortified through their callback methods. The pseudo code might look like this:

		
class Person {
public:
	Delegate Talking;
	
	void talk() {
	  Talking.invoke();
	}
};


void myTalkingCallback()
{
  //do something interesting
}

Person p;
p.Talking += myTalkingCallback;

p.talk();

		

Obviously the exact C++ is a little more complex than that, but that's the basic principle.

Both the CallBack and Delegate classes use templates to handle the generic function parameters and for dealing with generic return types.

The CallBack class is the base class for all other callbacks, but you'll need ot use a specific type and declare with the appropriate template parameters. Callbacks are broke into two main groups - functions that return a value, and functions that do not. Within these two groups we have a further distinction for functions that are class member functions.

Functions that do not return anything are called a Procedure. If the Procedure is a class member function, then it's called a ClassProcedure. Following the name are the number of function paramters/arguments that it takes forming something like Procedure for a function with nor arguments, or Procedure3 for a function with three arguments. You will need to declare the exact argument types, and possibly the type of the class if it is a class member function. Some examples:


//for a simple static function
void myCallBack()
{

}

//here's your callback wrapper
CallBack* myCB = new Procedure(myCallBack);


//for a static function with 2 arguments
void myCallBack2( int i, const String& s )
{

}

//here's your callback wrapper
CallBack* myCB = new Procedure2<int,const String&>(myCallBack2);

//for a class member function that takes 1 parameter

class MyClass : public Object {
public:
  void myCallBack( Event* e ) {
	
	}
};

//here's your callback wrapper
MyClass cbSrc;

CallBack* myCB = new ClassProcedure1<Event*,MyClass>(&MyClass::myCallBack,&cbSrc);

		

Functions that do return something are called Function and for class member functions are called ClassFunction. Like a Procedure, they are followed by the number of arguments that the function takes and you must declare the function return type and argument types in the template declaration. A function that returns an int, but takes no arguments is called Function<int>, a function that returns a String and takes a const bool reference is Function1<String,const bool&> and so on. Some examples:


//for a simple static function
int myCallBack()
{
	return 100;
}

//here's your callback wrapper
CallBack* myCB = new Function<int>(myCallBack);


//for a static function with 2 arguments
String myCallBack2( int i, const String& s )
{
	return s + i;
}

//here's your callback wrapper
CallBack* myCB = new Function2<String,int,const String&>(myCallBack2);

//for a class member function that takes 1 parameter

class MyClass : public Object {
public:
  bool myCallBack( Event* e ) {
		return true;
	}
};

//here's your callback wrapper
MyClass cbSrc;

CallBack* myCB = new ClassFunction1<bool,Event*,MyClass>(&MyClass::myCallBack,&cbSrc);

		

Delegates are differentiated in a similar manner as callbacks. They are broken into 2 groups, delegates that return values, and delegates that do not. Also, the exact delegate class name includes the number of arguments it takes. So a delegate that has no return value and no arguments is named Delegate0, a delegate with 3 function arguments and no return type is Delegate3, and a delegate with a return type and 1 function argument is Delegate1R, where the "R" means it returns a value. Note that the delegate does not need to distinguish between static functions or class member functions - these are handled by the concrete CallBack classes and can therefor be safely ignored by the delegate. Which means that a delegate may have callback functions of either type. Some examples:


//a simple delegate
void myCallBack()
{
	
}

//returns nothing, takes no parameters
Delegate0 d; 


void myCallBack2( int i)
{
	
}

//returns nothing, takes on parameter, an int
Delegate1 d<int>; 


//for a delegate with 2 arguments and a return type of String
String myCallBack2( int i, const String& s )
{
	return s + i;
}


//returns a String, takes 2 parameters, an int and a const String ref 
Delegate2R<String,int,const String&> d2;

		

Once you've declared a delegate you can add a callback to it:

		

void doit( int j ) 
{

}

Delegate1 d<int>; 
CallBack* myCB = new Procedure1<int>(doit);
d += myCB;

//for static functions you can also do:
d += doit;


		

At this point you have a delegate with callbacks hooked up to it. Now when you "invoke" the delegate, the delegate will pass this along to all it's callbacks.

The next step is to actually use the Delegate class within your source object, so that the source can fire events. The VCF does this by declaring a public member variable of type Delegate1<Person&> and giving it the name of the event that will be fired. For example:


class Person;

typedef Delegate1<Person&> TalkDelegate;

class Person : public VCF::Object {
public :
	TalkDelegate Talking;
  
	void talk() {
		Talking.invoke(*this);
	}
};

		

We have exposed the talking event by declaring a Delegate named Talking. Now any interested observer can register their callback with this and be notified when a talking event is fired. For example:


class PersonObserver : public VCF::ObjectWithCallbacks {
public:
	void onTalking( Person& thePerson ) {
		System.println( "PersonObserver got it's onTalking() method called!" );
	}
};

int main( int argc, char** argv ) 
{
	FoundationKit::init(argc,argv);
	
	Person person;
	
	//our observer
	PersonObserver observer;
	
	//the observer now registers with the Foo object's Talking delegate
	person.Talking += 
		new ClassProcedure1<Person&,PersonObserver>(&observer, &PersonObserver::onTalking, "onTalking" );
	
	//the person talks, and lo, and behold the PersonObserver's onTalking() method will
	//get called!
	person.talk();
	
	FoundationKit::terminate();
	return 0;
}

		

The Talking delegate it triggered if the Person::talk() method is called, and we can see how we have connected the two objects (source and observer) through the use of Delegates and callbacks.

A Delegate may have a return value, and because it can have more than one callback it needs to store an equivalent number of return values. You can access these return values in two different ways. The easiest is to use the value returned by the invoke() function. This will give the last return value in the return value collection. So if you had a delegate with three callbacks, you'd have a collection of 3 return values, and after calling invoke() you'd be returned the result of the 3rd callback. For example:


Delegate2R<bool,const String&,double> myDelegate;
myDelegate += //some callback #1
myDelegate += //some callback #2
myDelegate += //some callback #3
bool result = myDelegate.invoke( "Hello", 0.000812 );

		

The above example would return the result from callback #3.

You can access all the return values by examing the delegate's results member variable. The return values are simply stored in a vector, and they are in the order that the callbacks are invoked in.


Delegate2R<bool,const String&,double> myDelegate;
myDelegate += //some callback #1
myDelegate += //some callback #2
myDelegate += //some callback #3
for ( size_t i=0;i<myDelegate.results.size();i++ ) {
	printf( "myDelegate results[%d]: %s\n", i, myDelegate.results[i] ? "true" : "false" );
}

		

The results member variable is cleared when the invoke() method is called, and is not considered usable until after the invoke() method has returned.

3.1.11. Multithreaded programming support

3.1.11.1. Synchronization Primitives

The VCF has several classes that can be used to synchronize access to resources (for example a collection of integers) in a multi-threaded program.

3.1.11.1.1. SynchObjects

In the VCF an object is considered syncronizeable if it implements the SynchObject interface. This means it must provide methods for locking the object (either for an infinite amout of time, or some shorter amount of time specified in milliseconds), and for unlocking the object. The SynchObject object interfaces specifies both of these in it's SynchObject::lock() and SynchObject::unlock() methods. When a SynchObject's lock method is called, the first thread that completes the call will have the object "locked", and all other threads that attempt to lock the object will block until the thread that has the object locked releases it with a call to SynchObject::unlock().

In connection with the SynchObject class, there's the Lock class which is intended to be used on the stack, making it easy to use a SynchObject, without forgetting to unlock the SynchObject when finished. For example:

			
void MTDoIt( Mutex* m )
{
  Lock l(*m);
  //access some critical data
  .
  .
  .  
}

			

When the Lock instance's constructor is called, it calls the SynchObject's lock() method. When the Lock instance falls off the stack, it's destructor is called and the SynchObject's unlock() method is called. Using the Lock class with SynchObjects makes it impossible to forget to unlock a SynchObject.

3.1.11.1.2. Waiting for ...

In addition to synchronized objects, it's also possible to "wait" on certain objects. An object is said to be "waitable" if it implements the VCF::Waitable interface. The Waitable interface has two wait functions, one that blocks forever, and the other that only block for a specified number of milliseconds. Waiting on something means that the caller of the wait() function is blocked until the waitable object completes what it's doing, or is set or a "signaled" state. For example, waiting on a thread means that caller will be blocked till the thread exits. In the VCF both threads and conditions implement the Waitable interface. Let's look at a simple example:


class MyThread : public Thread {
public:
	virtual bool run() {
		int count = 0;
		while (  count < 1000 ) {
			sleep(100);
			count ++;
		}
	}
};

Thread* thread = new MyThread();
thread->start();
switch ( thread->wait() ) {
	case Waitable::wrTimedOut : {
		System::println( "Wait timed out!" );
	}
	break;
	
	case Waitable::wrWaitFinished : {
		System::println( "Wait completed succesfully" );
	}
	break;
	
	case Waitable::wrWaitFailed : {
		System::println( "Wait FAILED!" );
	}
	break;
}

			

The above example will block until the MyThread instance's run() method is completed.

The Waitable interface is used to wait on a single instance. If you have more than 1 waitable object, and you'd like to wait on the group, then you can use the ThreadManager's wait() functions which allow you to pass in a vector of Waitable objects in.

3.1.11.1.3. Mutexes

A mutex is used to synchronize access to a resource amongst more than one thread. It's the simplest of the 3 synchronization primitives. The first thread to obtain a lock forces all others to block until it's lock is released (see Mutex::unlock).

3.1.11.1.4. Semaphores

A semaphore is used to synchronize access to a resource amongst more than one thread. A semaphore is created with an intial count. Each unlock of the semaphore decrements the semaphores count by one, and when it reaches 0, it is allows one of the blocked threads to return and carry on.

3.1.11.1.5. Conditions

A condition is modeled after the Unix pthread condition primitive. The primary difference between a condition and a mutex or semaphore is that a mutex/semaphore will only allow one thread to "wake up" from a blocked state. A condition, on the other hand, can have multiple threads waiting on it, and wake them all up.

A condition is used in conjunction with a valid mutex instance like this: Mutex m; Condition c(m); Now the condition instance ("c") is ready to be used. At this point the conidition can be used in one of two ways: you can notify it that some "condition" has occured, for example, some data is ready to processed, or you can wait on it for some amount of time.

If you're waiting on a condition, then you will use the wait methods, either waiting for a specific period of time (measured in milliseconds), or waiting indefinitely. Either way, the wait is ended when the condition is notified, or "signaled", in some way.

The condition is considered signaled when either it's signal() or broadcast() method is called. Once the condition is signaled it then wakes up one or more threads that are waiting on it. Depending on whether signal() or broadcast() is called, the condition may wake up only one thread, or all the threads, as seen below.

Figure 3.2. Conditions and Threads - Condition::signal()

Conditions and Threads - Condition::signal()

Figure 3.3. Conditions and Threads - Condition::broadcast()

Conditions and Threads - Condition::broadcast()

In the first figure we see that the condition's signal() method has been called, and only one thread is "woken up", e.g. it will return from it's wait() call. In the second firgure we see that all the threads have been "woken up" and will all return from their respective wait() calls.

3.1.11.2. Threads

Threads in the VCF are quite simple to use and work with. A thread represents a piece of code that can be run, or executed. Since the code is "runnable", we have a special class in the VCF called VCF::Runnable which is used to indicate executable code. Since a thread is executable the VCF::Thread class implements the Runnable interface. Any class that implements the Runnable interface can be run in a separate thread.

To help manage threads the VCF has a ThreadManager class. The ThreadManager allows you to wait on multiple threads, and to obtain the current running thread. Note that all programs have at least one thread of execution, even if you don't create a VCF::Thread instance explicitly. The "main" thread instance is created for you autmotically by the ThreadManager when it is initialized. For example:


int main( int argc, char** argv )
{
	FoundationKit::init( argc, argv );
	
	Thread* mainThread = ThreadManager::getCurrentThread();
	
	System::println( Format("Main thread ID: %u") % mainThread->getThreadID() );
	
	FoundationKit::terminate();
	return 0;
}

		

Even though we have not created a thread instance explicitly, we are stil able to obtain the main thread and get information from it, as in the example above.

Implementing Threads.  The Runnable interface provides two methods - one called run(), which is where your thread specific code goes, and another called stop(), which is where you should put code to cleanly stop your executing code in your run() method.

A Thread implements both these methods, and you can derive your own thread classes directly from the Thread class. If you do this you should check the status of the canContinue() method, and if it returns false, you should immediately stop what you're doing and exit your run() implementation cleanly. For example:


class MyThread : public Thread {
public:
	bool run() {
		int count = 0;
		while ( (count < 10000) && (canContinue()) ) {
			System::println( "count: %d", count );
			count ++;
		}
		return true;
	}
};
		
		

In this simple example, we loop till count equals 10,000 or canContinue() returns false. Doing this ensures that if the program requests the thread to stop, it will do so cleanly.

To actually run you must call the Thread::start() method. All threads are inactive when first created, and will not run till the Thread::start() method is called.

To stop the thread gracefully, you can call the Thread::stop() method. This will block till the thread's peer actually stops and destroys the underlying thread handle.

A Thread can automatically delete itself when it stops running, and this is the default behaviour. By passing in a boolean flag set to false in the Threads constructor, you can prevent this behaviour. In this case, it is up to the caller who created the thread to delete it. Lets look at a short example:



class MyThread : public Thread {
public:
	MyThread( bool autoDelete ):Thread(autoDelete)  {
	
	}
	
	bool run() {
		//..do something interesting
		return true;
	}
};


int main( int argc, char** argv ) 
{
	FoundationKit::init(argc,argv);
	
	Thread* autoDeleteThread = new MyThread(true);
	
	Thread* manualDeleteThread = new MyThread(false);
	
	
	autoDeleteThread->start();
	manualDeleteThread->start();
	
	//wait and/or loop for the threads to finish
	
	manualDeleteThread->free();
	
	FoundationKit::terminate();
	return 0;
}
		
		

An alternate way to work with a thread is to create a class that simply derives from Runnable, and implementing the run() and stop methods. Then you can simply create a Thread and pass in the Runnable instance to the Thread's constructor. For example:


class MyRunnable : public Runnable {
public:
	MyRunnable() : conContinue_(true){
		
	}
	
	virtual bool run() {
		int count = 0;
		while ( (count < 10000) && conContinue_ ) {
			count ++;
			System::println( "count : %d", count );			
		}
		
		return true;
	}
	
	virtual void stop() {
		conContinue_ = false;
	}
	
	bool conContinue_;
};

int main( int argc, char** argv ) 
{
	FoundationKit::init(argc,argv);
	
	Thread* thread = new Thread(new MyRunnable());
	
	thread->start();
	
	//wait and/or loop for the thread to finish
	System::sleep(2000); //wait 2 seconds
	thread->stop(); //stop the thread
		
	FoundationKit::terminate();
	return 0;
}
		
		

This creates a thread, and a new instance of our custom runnable class (MyRunnable) starts the thread. By default, both the thread and the runnable instance are automatically deleted. We then wait for 2 seconds, and then stop the thread, which in turn stops the runnable instance. Strictly speaking, this is not the best idea to stop the thread like this when it's set to auto delete, as it is possible (depending on how you implemented your run() method) for the thread to have already finished by the time the code gets here. To be truly safe you would want to create the thread with auto deletion turned of. For more on this, please see the Thread.h and the Thread constructors.

3.1.11.2.1. Thread Manager

The ThreadManager is used to manage threads and provide access to both the current thread and current run loop.

You can also use it to wait on multiple "waitable" objects, and then get a list of what objects became signaled. This is basically the same idea behind the Win32 API function WaitForMultipleObjects.

You can get the current thread object by calling the thread manager's ThreadManager::getCurrentThread() function. This will return the thread instance that's associated with the calling thread. For example:


int main( int argc, char** argv ) 
{
	FoundationKit::init(argc,argv);
	
	Thread* thread = ThreadManager::getCurrentThread();
		
	FoundationKit::terminate();
	return 0;
}
		
			

This returns the main thread's thread object. If you had made this call within the context of a different running thread, then you would get that thread's thread object.

Likewise, there's a method to the retreive the current runloop for the calling thread. For that you just call ThreadManager::getCurrentRunLoop(), for example:


int main( int argc, char** argv ) 
{
	FoundationKit::init(argc,argv);
	
	RunLoopPtr::Shared runLoop = ThreadManager::getCurrentRunLoop();
		
	FoundationKit::terminate();
	return 0;
}
						
			

Note that this returns you a smart pointer, not a raw RunLoop pointer.

3.1.11.2.2. Run Loops

A RunLoop is used to represent a repeating loop that runs within the context of a particular thread. You might say that a RunLoop is a sort of OO wrapper around a while {} loop. Runloops are never created by the programmer - instead they are maintained by the framework.

Run loops work by having sources that feed them events, timers that get fired, and observers that get notified when these kind of events take place. The run loop is activated by calling one of the run loop's run() methods. These can cause the run loop to run indefinitely or for a specified period of time.

3.1.11.2.2.1. Relationship to Threads

Each thread instance has one and only one RunLoop instance associated with it. The runloop is created on demand, so if the thread in question never uses the runloop, then it will not get created. When the thread is destroyed, the runloop (if it exists) will also be destroyed.

You can get access to a RunLoop through the ThreadManager's getCurrentRunLoop() function.

3.1.11.2.2.2. Run Loop Source

A run loop Source is used to "feed" events and/or notifications to the run loop. Each run loop can have 0 or more sources attached to it. A run loop source is meant to be customized by overriding it's perform() method. The perform() method is what will get called by the run loop, and where you're expected to do your work.

Sources can be attached at any time to a run loop. Once attached to the run loop, the source needs to notify the run loop that it's ready to perform. It does this by calling it's signal() method, which notifies the run loop and in turn ultimately ends up with the source's perform() method being called.

Since the perform() method may get called repeatedly, you need to take this into account when designing your run loop source class. This kind of behaviour is ideal for writing a state machine like class, where you maintain a state variable that changes as the perform method is called.

3.1.11.2.2.3. Timers

In addition to run loop sources, a run loop may have any number of timers associated with it. Adding the timer is simply a matter of creating the timer object - a RunLoopTimer instance, and adding it to the runloop. When you create a timer and specify when it will fire and whether or not it will repeat.


RunLoopPtr::Shared runLoop = ThreadManager::getCurrentRunLoop();
	
RunLoopTimerPtr::Shared timer1( new RunLoopTimer( DateTimeSpan( 1000 ) ) );
runLoop->addTimer( timer1 );

			

This code creates a time that fires every 1000 milliseconds.


RunLoopPtr::Shared runLoop = ThreadManager::getCurrentRunLoop();
	
RunLoopTimerPtr::Shared timer1( new RunLoopTimer( DateTime( 2008,5,12,22,30,0 ) ) );
runLoop->addTimer( timer1 );

			

This timer will fire once on May 12, 2008 at 10:30 PM local time.

You can get notified that a timer has fired by adding a callback to the TimerFired delegate. The function signature for this callback looks like:


void TimerFiredCallback( RunLoopTimer& );

			

3.1.11.2.2.4. Posting Events

You post an event to a run loop. The "event" must be an object instance that derives from the Event class. You can post the event to a specific run loop from any calling thread. When the event is processed, it will be executed in the thread that the run loop belongs to.

To post an event to a run loop, you need an event instance that's been created on the heap, and a CallBack instance of type Procedure1<Event*> or ClassProcedure1<Event*>. Pass both of these to the run loop's postEvent() method.

3.1.11.2.3. Threaded Functions

A threaded function is like a regular function, only it get's asynchronously in a new thread.

3.1.11.2.4. Thread Pools

A ThreadPool is used to manage a group, or "pool", of threads that can then be assigned work to do by the thread pool. The ThreadPool constructor takes a integer that specifies how many threads to create in the pool.

Once you've created the thread pool you need to start the pool by a call to start(). You can stop the pool by calling stop(). You can assign work to the thread pool where "work" item is a valid Runnable instance. To add the item to be run, just call the ThreadPool's postWork() method. This will push the runnable instance on the thread pool's work queue and then return immediately.

You can wait on the thread pool. Waiting means that the wait() methods will return when all the work assigned to the thread pool's threads have been completed.

3.1.11.2.5. Asynchronous Delegates

Delegates can be fired synchonously or asynchronously. If you fire them asynchronously, then the delegates callbacks will be fired in some other thread, one managed by a thread pool used by all delegates. The thread pool is created and managed by the framework, so you don't have to worry about it.

To invoke the delegate asynchronously, all you need to do is call the delegate's beginInvoke() method (as opposed to the synchonous invoke() method). This will return an AsyncResult instance. The caller needs to delete this instance when they are done with it. For example:


Delegate1<int> myDelegate;
AsyncResult* ar = myDelegate.beginInvoke( 1001, NULL );
delete ar;

			

When you invoke the delegate, you need to supply the function arguments, and an optional callback that will let you know when the delegate has completed executing all of it's callbacks. This optional completion callback takes the form of:


void AsyncCompletionCallback( AsyncResult* ) 

			

This may be a Procedure1<AsyncResult*> or ClassProcedure1<AsyncResult*> instance. The AsyncResult that is passed in to your callback is the same instance that is returned by the delegate's beginInvoke() method.

[Note]Note

When your async completion callback is called, don't assume it's in the same calling thread as the one that called the delegate's beginInvoke(). Keep this in mind if you need to access resources or make calls to UI objects like controls or windows.

You can also wait on the AsyncResult instance. The AsyncResult will get notified by the internal thread pool when all of the delegate's callbacks have been completed. After the wait is over it's safe to delete the AsyncResult instance.


Delegate1<int> myDelegate;
AsyncResult* ar = myDelegate.beginInvoke( 1001, NULL );
ar->wait();
delete ar;

			

This will block until all the callbacks have completed.

If the delegate has a return value you can extract this value by calling the delegate's endInvoke() method, passing in the AsyncResult instance that was returned in the beginInvoke() call. It's important to remember that these two calls, beginInvoke() and endInvoke(), need to be paired up if you're interested in the return value. An example of this might look like:


Delegate1R<bool,int> myDelegate;
AsyncResult* ar = myDelegate.beginInvoke( 300, NULL );
ar->wait();
bool res = myDelegate.endInvoke(ar);
delete ar;

			

Since a delegate can have multiple callbacks, it's also possible to have multiple return values. When you call endInvoke() it will return you the last result, or return value, in it's collection of return values. If you want to get all the return values then you need to call endInvokeWithResults() and, just like with the endInvoke() call, pass in AsyncResult instance. When you call endInvokeWithResults() you'll get the result collection specific to the AsyncResult. This result collection is simply a vector of return values, just like the delegate's results member variable.

3.1.12. Strings

Strings in the VCF are represented by the VCF::UnicodeString class, which is typedeffed as "String" to make it more convenient to use. Almost all functions that require a string use the String class as opposed to a "char*" or "const char*" parameters. All string data is stored as unicode, but it's trivial to convert to ansi if neccessary. Conversion can either be automatic, or you can explicitly control it via the use of text codecs. Automatic conversion relys on the underlying platform for unicode-to-ansi or ansi-to-unicode conversion routines.

3.1.12.1. STL usage

The VCF's String can't derive from std::basic_string<> due to the design of std::basic_string<>. Instead it wraps an instance of std::basic_string and has the exact same interface as std::basic_string. It also has a conversion operators for std::basic_string<>, which should make it easy to get at the underlying std::basic_string<> instance if it's needed.

[Note]Note
The reason the VCF::UnicodeString class can't derive from std::basic_string is due to the fact that it has a non virtual destructor. The C++ standard says that deriving from a class with a non virtual destructor leads to undefined behaviour.

3.1.12.2. Data Representation

The UnicodeString uses a 16 bit unsigned short as it's data type. This means that, in general, each "character" is 16 bits - double the size of what you'd use in an ansi string. Thus the std::basic_string<> template is instantiated as std::basic_string<unsigned short>. This makes the underlying buffer storage the same on major platforms like Mac OS X and Win32, both of which use Unicode and both of which store the unicode data as 16 bit unsigned shorts.

The one problematic side affect is that not all compilers treat wchar_t as a 16 bit value. The C++ standard has no definition of the size of wchar_t and naturally each compiler vendor does something different. Some compilers treat it as a 16 bit value (Microsoft Visual C++ and Borland), and some default to a 32 bit value (GCC, Code Warrior). The problem occurs with string literals. When the following:


String str = L"Hello World";

			

is compiled by a compiler like Visual C++, the wchar_t string literal is treated as a 16 bit unicode string, and no conversion needs to take place. However, when compiled by a compiler like GCC, the wchar_t string literal is treated as a 32 bit array of characters, and does require a conversion to take place. It may be that for consistencies sake, when using string literals, it's better to use ansi string literals ("Hello World" as opposed to L"Hello World") than their wchar_t counterparts.

3.1.12.3. Language Encoding

When loading an ansi string you can specify a specific language encoding to use when converting the string from ansi to unicode. The complete list is compatible with Windows Code Page values. Other systems may not support as complete a list of encodings.

To convert the ansi string, you just specify an encoding to use:


FileInputStream fs("cedict.b5");
	
unsigned char* data = new unsigned char[ fs.getSize() ];

fs.read( data, fs.getSize() );

String chStr((const char*)data,fs.getSize(),UnicodeString::leChineseTraditionalBig5);

delete [] data;

			

chStr now contains a unicode string that's been converted using Chinese Traditional Big5 language encoding (also known as code page 950 on Win32).

3.1.12.4. Conversion and Text Codecs

Conversion happens either automatically, or through the use of the UnicodeString::encode() or UnicodeString::decode() functions. Using UnicodeString::encode() or UnicodeString::decode() alows the developer to explicitly specify the codec to use and control how the transformation from unicode to ansi (or vice-versa) is performed.

Automatic conversion applies whenever you assign a string literal to a UnicodeString or pass it to a constructor. For example:


String uniStr = "Hello World";

			

This uses the default conversion routines of the native platform, with the default character encoding, to convert the ansi string literal to a UTF16 unicode string. If this is insufficent for you then you need to use the UnicodeString::encode() or UnicodeString::decode() methods.

Custom Text Codecs.  You can create a custom text code by creating a new class and deriving from VCF::TextCodec. You need to implement the following methods, like so:


class MyTextCodec : public VCF::TextCodec {
public:
	/**
	These are the 4 methods you have to implement
	*/
	virtual ulong32 convertToAnsiString( const UnicodeString& str, UnicodeString::AnsiChar* ansiStrBuffer,
										const ulong32& ansiStrBufferLength );

	virtual UnicodeString::AnsiChar convertToAnsiChar( const UnicodeString::UniChar& c );

	virtual UnicodeString convertToUnicodeString( const UnicodeString& str );

	virtual UnicodeString convertToUnicodeString( const UnicodeString::AnsiChar* str, UnicodeString::size_type stringLength );

	
	//You need to return a name, which needs to be unique, for your codec
	virtual String getName() {
		return "MyTextCodec";
	}
};
			
			

The first method, convertToAnsiString(), converts a UnicodeString to an ansi string, and writes the resuls to the buffer supplied in ansiStrBuffer, writing no more than ansiStrBufferLength bytes to the buffer.

The next method, convertToAnsiChar() converts a single unicode character to an ansi character.

The third method, convertToUnicodeString() converts one unicode string to another unicode string, using different encoding rules.

The fourth method, convertToUnicodeString() converts an ansi string buffer into a unicode string, reading only stringLength bytes from the ansi buffer.

The last method, getName(), returns a string name that should uniquely identify the text codec. This is used by the TextCodec's registerCodec() method to register the codec with the FoundationKit's list of registered codecs.

You can use your codec like so:


UnicodeString str = "A longer string to convert!";
char convertedAnsiStr[256];
MyTextCodec codec;
str.decode_ansi( &codec, convertedAnsiStr, sizeof(convertedAnsiStr) );

			

Registering Text Codecs.  It's possible to register a single instance of a text codec with the FoundatKit runtime. You can do this by calling the static TextCodec::registerCodec() function. You can access the codec at a later time by calling the TextCodec::getCodec() function and passing in the correct codec name. For example:


//initilization code...
TextCodec::registerCodec( new MyTextCodec() );

.
.
.
.
.
void someOtherFunction() 
{
	UnicodeString str = "A longer string to convert!";
	char convertedAnsiStr[256];

	str.decode_ansi( TextCodec::getCodec("MyTextCodec"), convertedAnsiStr, sizeof(convertedAnsiStr) );
}

			

3.1.12.5. Formatting

The VCF does not make use of variable argument functions ( i.e. functions that use the "..." feature of C/C++) for formatting a string. Instead the VCF::Format class is used. The Format class takes a string argument that is the message string, and then the developer adds "arguments" by using the overloaded operator "%" of the Format class. A simple example:


String formattedStr = Format( "Hello %d times!" ) % 100;

			

3.1.12.6. Utilty Functions

There are a number of utility functions for strings, contained as static functions in the StringUtils class.

3.1.12.6.1. Trimming

The StringUtils class has several functions for "trimming" a string, particularly useful for removing whitespace.

3.1.12.6.2. Number conversion

There are a number of functions, most named StringUtils::toString(), that allow you to convert various number types, like int, long, short, double, and float to a string representation. Note that if you want real, pretty, locale correct string values, please use the Locale::toString() methods.

3.1.12.6.3. UUID generation

The StringUtils::newUUID() function returns a string that represents the value of a newly generated UUID. UUIDs are generated by the OS and are guaranteed to be unique (they are 128 bit numbers).

3.1.12.6.4. Type ID

The StringUtils::getClassNameFromTypeInfo() is used to extract a useable class name from a std::type_info instance as returned by using the typeid() function in C++. Each compiler returns different values for this, even for something that's the same type. For example, Microsoft compilers prefix the type name with a "class " or "struct " prefix. GCC typenames are name mangled and need to be decoded to be "user friendly".

3.1.12.6.5. Formatting

There's support for formatting a DateTime instance and transforming it's value to a string. For example:


String dtStr = StringUtils::format( DateTime::now(), "%a %B %#d, %Y" );

				

The value in dtStr should read something like "Mon June 2, 2005" after StringUtils::format() returns. Using the "#" causes leading 0's to be stripped from the string conversion. For a complete list of the various format specifiers, see the source documentation of StringUtils::format().

3.1.13. Locales

To support internationalization (i18n) and localization (i10n) the VCF has a Locale class. Each locale instance represents a way to identify a language/country/region and to indication to the underlying OS that it should use this identifier for certain operations. For example, on platforms that support this, you can tell the OS to set the locale of a specific thread, so that all operations are made with that locale in mind. The Locale class also provides specific methods to convert date/time, numeric, and currency values into string values appropriate to the locale.

3.1.13.1. Naming

Each locale is identified by a string name that is composed of (currently) several elements. The first is a language identifier as specified by ISO-639-1 (e.g. English is "en", Polish is "pl", and Italian is "it"). The second is the 2 character country identifier as specified by ISO-3166 (e.g. the United States is "US", Poland is "PL", and Italy is "IT").

These two codes are then joined together, separated by an "_" character to form a complete identifier for the locale, such as "en_US", or "en_GB".

This identifier is then used in the construction of a locale:


Locale loc("en_US); //american english locale
Locale loc2("ar_AE); //UAE arabic locale

			

Depending on your platform, it's possible that a valid locale identifier may not be supported which will cause an exception to be thrown by the locale. For example your OS may not support arabic, thus the "ar_AE", while valid, would cause an exception.

Once created a locale instance maintains a peer that maps directly to the OS's support for locale operations.

3.1.13.2. Numeric Formatting

The locale class supports formatting numeric values to strings appropriate to the locale in question. Any integer value may be converted to a string using the Locale::toString() methods. For example:


Locale loc("en_US");
String numStr = loc.toString( 203456 ); 

			

The numStr variable will now contain the string value "203,456". Note that this is different than what StringUtils::toString() does, which is to just do a plain conversion of the 203456 value into "203456". Floating point conversion is also supported in the form of converting float and double types.

Currency values may be converted using the Locale::toStringFromCurrency() function which takes a single double value.


Locale loc("en_US");
String numStr = loc.toStringFromCurrency( 120034.231 ); 

			

3.1.13.3. Date Time Formatting

There are two separate functions for transformating a DateTime instance into a string. The first, Locale::toStringFromDate(), only uses the date portion of a DateTime instance. The second, Locale::toStringFromTime(), only uses the time portion of a DateTime instance.

[Note]Note

The format codes used by Locale::toStringFromTime() and Locale::toStringFromDate() are not the same as the format codes used by StringUtils::format(DateTime).

An empty format string (the default value for the argument) means the implementation should use whatever default values are most appropriate. An example of using this:


Locale loc("it_IT");
String dateStr = loc.toStringFromDate( DateTime::now() );
String timeStr = loc.toStringFromTime( DateTime::now() );

			

3.1.13.4. Collation

Collation means comparing to string values for equivalency or order. The Locale class has two functions for this, one for case sensitive compares, Locale::collate(), and another for case in-sensitive compares, Locale::collateCaseInsensitive(). Both return an integer value that is one of the following:

  • 0 - Indicates that both strings should be considered equal.
  • -1 - Indicates that the first string is "less than", or should come before, the second string.
  • 1 - Indicates that the first string is "greater than", or should come after, the second string.

3.1.13.5. Translation

The Locale class also supports translating a string in one language, into the language represented by the Locale instance. Typically the "source" string is in english. For instance, here's an example of using the string "Hello", and translating it with a French locale:


Locale loc("fr_FR");
String frHelloStr = loc.translate( "Hello" );

			

After loc.translate() returns the frHelloStr variable should contain "Salute", assuming a translation could be found. If no translation is found, then the return value is the same as the source.

Mechanics.  The translation works by using some sort of localized key/value file. A special class called the MessageLoader is used to look up and extract data from the file. The default translation file is a ".strings" file, but because the MessageLoader class allows a developer to register a specific MessageLoader instance with a mimetype (matched to a file name extension), it's possible to add support for other localization files, such as .po/.mo files and the gettext API.

The default file format used to store translation files is the "strings" format. This is pretty much identical to the format specifed for Mac OSX systems and you can read more about it here: Strings Files (Apple Documentation). Basically a .strings file is a very simple that that contains 1 or more key/value pairs. You can also have comments using C style "/*" and "*/" comment brackets. For example, here's a sample:


/*
Some comments!
*/
"Hello" = "Salute" /*this is Hello in french*/

"I understand" = "Je comprends"

			

When Locale::translate("Hello") is called, if this file is loaded by the appropriate MessageLoader, the string "Hello" will be matched to "Salute" and the return value of Locale::translate() will be "Salute". Since a .strings file is considered a resource, they are stored under the application's resource folder. See Resources for more information about how resource folders work.

Custom Message Parsing.  It's possible to create a custom message parser to handle other translation file formats, such as the .po/.mo file format used in many linux applications. You do this by creating a new class that derives from VCF::MessageLoader and override the MessageLoader::loadMessageFile() and MessageLoader::getMessageFromID() functions.

Registering Your MessageLoader.  You need to register an instance of your MessageLoader class with the FoundationKit. You can do this by calling MessageLoader::registerMessageLoader(). In your initialization code you might do the following:


MessageLoader::registerMessageLoader( "text/cool", new MyCustomMessageLoader() );

			

Files whose mime type corresponds to "text/cool" will be opened with the MyCustomMessageLoader instance.

3.1.14. Resources

A resource is considered a chunk of data used by the program at runtime. For example, a look up table of string translations, or an xml file contaning program settings. The VCF allows you access these resources in a variety of ways, all exposed through the use of a ResourceBundle. The ResourceBundle holds 0 or more resources, each accessed by a string name. The resource may be stored in some platform dependant way (such as embedded resources created vi .rc scripts and the Win32 resource compiler), or in a generic platform independant method that closely mirrors how resources are handled on Mac OSX.

3.1.14.1. Accessing a resource

Access to a resource is provided by the ResourceBundle. For a given program there is one and only one ResourceBundle, and you gain access to it by calling the System::getResourceBundle() function. Once you have a ResourceBundle instance, you can get access to a particular resource by calling one of it's functions. The lowest level way to do this is calling the ResourceBundle::getResource() function:


ResourceBundle* resBundle = System::getResourceBundle();
Resource* res = resBundle->getResource( "stuff" );
BasicInputStream bis( (const char*)res->getData(), res->getDataSize () );
//extract data from input stream...

		

Each resource is named, and then the Resource class allows acces to the buffer of data for the resource.

If you don't want to deal with a Resource instance directly, there's a function for extracting text resources (there are also other resource types like images but those are covered in the GraphicsKit).


ResourceBundle* resBundle = System::getResourceBundle();
String text = resBundle->getString( "Text1" );

		

This will return a string resource that corresponds to the name "Text1".

3.1.14.2. Program Information

Another common resource is program information. You can think of this as metadata about the running executable. For example, the name of the program, it's copyright information, the author(s) of the program, and so on. Like regular resources, this can be stored either in a platform dependant way (in Win32 as an embedded VS_VERSION_INFO struct), or in a platform neutral way that is extremely similar to the way this is stored on Mac OS X. Either way, you can retreive this information by calling the ResourceBundle's getProgramInfo() function, which returns a ProgramInfo instance.

Once a ProgramInfo instance is obtained, you can access the following information:

  • Program Name - see ProgramInfo::getProgramName()
  • Program Version - see ProgramInfo::getProgramVersion()
  • Program File name - see ProgramInfo::getProgramFileName()
  • File Version - see ProgramInfo::getFileVersion()
  • Description - see ProgramInfo::getDescription()
  • Copyright - see ProgramInfo::getCopyright()
  • Company name (and/or description) - see ProgramInfo::getCompany()
  • Author (or authors) - see ProgramInfo::getAuthor()

A quick example:


ResourceBundle* resBundle = System::getResourceBundle();
ProgramInfo* info = resBundle->getProgramInfo();
//retrieve data
delete info;

		

3.1.14.3. Platform Specific

The VCF resource system supports embedded resources on Win32 when created using .rc scripts. On OS X if you store your resources in an app bundle, the VCF supports that as well. Program information can be stored using the VS_VERSION_INFO on Win32 (see MSDN for how to add this resource type to your .rc script). Mac OS X developers can use their Info.plist files to store program information.

3.1.14.4. Platform Neutral

An alternate option is to store resources in a platform neutral way. The VCF does this by largely following the bundle layout that is used on Mac OSX. Instead of directly enbedding the resource in the executable image, a specific directory structure is followed, and resources are placed inside as a file. The name of the resource must be the same as the name of resource file. If you want to add localized string trnaslations using the default .strings format, then the name of the file must be the same as the name of you executable (e.g. a program called "bleep" would store it's localized strings in a file called "bleep.strings").

The basic idea is that there's a resource directory. Each resource is then stored in this directory as a file. Localized resources are supported by creating a sub directory named for the locale (like "en_US"), and then putting localized resource files in each locale specific sub directory. For example (assuming this is for a program called "bleep"):

Resources\
	SomeData.dat
	bleep.strings
		

If we add locale support for Italian, German, and Polish then we'd have:

Resources\
	SomeData.dat
	bleep.strings
	it_IT\
		bleep.strings
	de_DE\
		bleep.strings
	pl_PL\
		bleep.strings
		

The precise location of the "Resources" directory can be somewhat varied. There are four main "styles" that the developer can follow and that the VCF runtime understands in order to look up resource files. Some styles may not be compatible with Mac OSX's resource look up patterns. The examples below will all assume a program called "bleep".

Style A.  This is the simplest style (but it's not OSX compatible).

		
<application directory>/
	bleep.exe
	Resources/
		SomeData.dat
		bleep.strings
		it_IT/
			bleep.strings
		de_DE/
			bleep.strings
		pl_PL/
			bleep.strings
				
		

Style B.  Using the Contents directory - this is OSX compatible.

		
<application directory>/
	Contents/
		bleep.exe
		Resources/
			SomeData.dat
			bleep.strings
			it_IT/
				bleep.strings
			de_DE/
				bleep.strings
			pl_PL/
				bleep.strings
				
		

Style C.  With contents and platform directory - OSX compatible

		
<application directory>/
	Contents/
		<OS Name>/
			bleep.exe
		Resources/
			SomeData.dat
			bleep.strings
			it_IT/
				bleep.strings
			de_DE/
				bleep.strings
			pl_PL/
				bleep.strings
				
		

Style D.  With contents, platform, and compiler - OSX compatible

		
<application directory>/
	Contents/
		<OS Name>/
			<Compiler Name>/
				bleep.exe
		Resources/
			SomeData.dat
			bleep.strings
			it_IT/
				bleep.strings
			de_DE/
				bleep.strings
			pl_PL/
				bleep.strings
				
		

3.1.15. System Functions

The System is a singleton class that has a variety of useful functions that all deal with low level OS functionality.

3.1.15.1. String functions

There are several functions that return string information about the OS the VCF is running on. These include the operating system name, the OS version, the compiler the VCF was built with, the name of the computer (the hostname). I simple example of how to use these:


String os = System::getOSName();
String version = System::getOSVersion();
String compiler = System::getCompiler();
String hostName = System::getComputerName();
String user = System::getUserName();
System::println( "OS: " + os + " version: " + version + 
				"\nBuilt with " + compiler + " compiler\nComputer: " + 
				hostName + " has user " + user + " logged in." );	

			

3.1.15.2. Filesystem Functions

The System class also exposes several file system functions. It allows you to get or set the current working directory with System::getCurrentWorkingDirectory() and System::setCurrentWorkingDirectory() respectively. It also allows you to determine if, given a valid file name, a file exists on the file system. This is done using the System::doesFileExist() function.

Finally the System class also lets you get standard directory locations for common directories like the users home directory, where user favorites might be located, where the users temp directory is, where the systems temp directory is, and others. For example:


String homeDir = System::getCommonDirectory( System::cdUserHome );
String systemTemp = System::getCommonDirectory( System::cdSystemTemp );

			

3.1.15.3. Date/Time Functions

The System class has functions for both getting and setting various date/time values. You can query the current "tick" count, meaning the number of milliseconds the system has been up and running. This value may wrap depending on the OS, so it's not useful for any long range time calculations.


ulong32 tick1 = System::getTickCount();

doSomeLengthyCalculation();

ulong32 tick2 = System::getTickCount();

System::println( Format("It took %d millsecs for calc.") % (tick2-tick1) );

			

You can ask the system to "sleep" for a set number of milliseconds by calling the System::sleep() function. This is effective for the current thread that the call is made in.

It also possible to get the current system time, in either UTC (GMT) or local time, and store this in a DateTime instance.


DateTime localSysTime;
System::setDateToLocalTime( &localSysTime );

DateTime utcSysTime;
System::setDateToSystemTime( &utcSysTime );

			

In addition to this, you can translate a date/time value from local to GMT or from GMT to local by using the System::convertLocalTimeToUTCTime() and System::convertUTCTimeToLocalTime() functions respectively.


DateTime localTime(2004,2,24,10,32,12); //February 24, 2004 10:32:12
DateTime utcTime = System::convertLocalTimeToUTCTime( localTime );

System::println( utcTime );

DateTime utcTime2(2004,2,24,10,32,12); //February 24, 2004 10:32:12;

localTime = System::convertUTCTimeToLocalTime( &utcTime );
System::println( localTime );

			

3.1.15.4. Environment Functions

You can query and set environment variable easily by using the System::getEnvironmentVariable() and System::setEnvironmentVariable() functions. System::getEnvironmentVariable() returns a string that represents the environment variable name passed into the function. System::setEnvironmentVariable() lets you set the value of a specific environment variable. Setting these values does so only for the lifetime of the program that makes these calls. You don't need to include the "%" or "$" prefixs to variable names.

You can add a new directory to the system path variable. This is the variable that the OS uses in order to find an executable. In Windows this is the %PATH% variable, in unix systems this is the $PATH variable. The System::addPathDirectory() allows you to pass in a directory to add to this environment variable.

3.1.15.5. Resource functions

The System class has several functions that deal exclusively with resources. Several of these deal with either gaining access to the Resource bundle, or determining the directory where it exists. Use System::findResourceDirectoryForExecutable() to determine what the resource bundle directory for a program would be. Note that this just gives the directory name, there may not be anything there, or the directory may not even exist if the program is not using resources. System::findResourceDirectoryForExecutable() takes the path to an executable as it's only argument. So if you have a path to another program (that's VCF based) you can use this function to determine it's resource bundle location. As a general rule, you'll have little need for this as a developer.


String resDir = System::findResourceDirectoryForExecutable( "/usr/local/bin/foo" );

			

System::findResourceDirectory() is identical to System::findResourceDirectoryForExecutable() but it uses the current program executable path as the path to System::findResourceDirectoryForExecutable().


String resDir = System::findResourceDirectory();

			

You can obtain the resource bundle of the running program by calling System::getResourceBundle() which will return a pointer to the program ResourceBundle. See Resources for more information about Resource Bundles.

The next functions deal with program "meta data". Calling System::getProgramInfoFromFileName() allows you to access the programs information, such as the program copyright (if it exists), the author, and so forth. The functions returns a pointer to a new instance of a ProgramInfo object. You're responsible for deleting it, and if no information can be found the function may return a NULL value. You can determine the "bundle" directory of full program path by calling System::getBundlePathFromExecutableName(). For example (assuming we are running on Mac OS X):


String programBundleDir = 
	System::getBundlePathFromExecutableName("/Applications/Bob/Contents/MacOS/BobsYourUncle.exe");

			

This would result in programBundleDir being assigned the string value "/Applications/Bob". Again, as a general rule, these functions are more useful to the internals of the VCF than to an application developer. To do the opposite, to retrieve the full program path from just a "bundle" path, you can use the System::getExecutableNameFromBundlePath() function. For example:


String bobExePath = 
	System::getBundlePathFromExecutableName("C:\Program Files\Bob.app");

			

Now bobExePath would equal "C:\Program Files\Bob.app\Contents\WinNT\Bob.exe".

3.1.15.6. Locale functions

The System allows you to get and set the locale instance of the current thread. You can retrieve the current thread's locale by calling System::getCurrentThreadLocale(). You can set the current thread's locale by calling System::setCurrentThreadLocale(). Setting the thread's locale means that any VCF locale sensitive functions will default to this new locale value for the thread. Depending on the platform, there may be other effects when setting the thread locale. On Win32 calling System::setCurrentThreadLocale() also affects any Win32 API calls since, under the hood, System::setCurrentThreadLocale() ends up calling the API SetThreadLocale() function. On Mac OS X, there's no concept of per thread locales so this is not an issue.

3.1.15.7. Miscellaneous functions

Since some platform may or may not support Unicode you can query this by calling the System::isUnicodeEnabled() function. If the platform supports Unicode then System::isUnicodeEnabled() will return true, otherwise it will return false. So on Windows 98, a VCF program calling System::isUnicodeEnabled() would be returned a false value, while the same program running on Windows NT would get a true value.

3.1.16. Dates and Time

The DateTime class is used to represent all date-time values in the VCF.

3.1.16.1. Data Representation

The data of the DateTime class is stored as a 64 bit value. It is based on the concept of a Julian Day (see Claus Tøndering's Calendar FAQfor more information on this) and has a resolution of 1 millisecond.

Use of custom 64 bit integer class.  The 64 bit data value is a class that in turns wraps some 64 bit number. The class was used to make access to the value uniform and less of a hassle to deal with. See the VCF::ulong64 and VCF::long64 class documentation for more details. YOu can use it just like you would any other primitive integer type.

3.1.16.2. Date Time Spans

Given two dates there exists a "span", or delta value between the two date-time values. This delta or span value is represented by the DateTimeSpan class. You can subtract two DateTime values from each other and get a DateTimeSpan as a the result, as well as adding (or subtracting) a DateTimeSpan to (or from) a DateTime instance.


DateTime dt1(2003,2,12);
DateTime dt2(2005,6,3);
DateTimeSpan span = dt2 - dt1;

DateTime dt3 = dt1 + span;
		
			

Given a DateTimeSpan instance, you can get the total minutes, seconds, hours, and so forth from the span. See the VCF::DateTimeSpan class documentation for more information.

3.1.16.3. Iterating

You can iterate through a DateTime instance. You can either move forward or backward, and you can do so at different levels of granularity. For example, if you wish to move forward in time by 2 years, you can use the DateTime::incrYear() and pass in a 2 for the amount to increment by. You can also iterate through time using the DateTime::Iterator. This allows you to completely customize the logic of incrementing or decrementing by some value. The DateTime::Iterator supports iterating through operator ++ or operator-- functions.


DateTime dt1(2003,2,12);
DateTime::Iterator<ByHour> byHour = dt1;
for (int i=0;i<5;i++ ) {
	byHour ++;
	DateTime& current = *byHour;
	System::println( StringUtils::format( "%H:%M:%s", current ) );
}

			

The above example iterates across 4 hours, incrementing value by 1 hour each pass.

3.1.16.4. String Formatting

A DateTime instance can be transformed into a string by calling the StringUtils::format() function and passing in certain formatting strings. For example:


DateTime dt1(2003,2,12);
String s = StringUtils::format( "%a %B %#d, %Y", dt );

			

This would result in the string value "Monday January 2, 2003". The precise formatting codes are specified ing the documentation for the StringUtils::format() function.

3.1.17. Streams

Streams are used to store data, either input or output. A stream object allows you to either read or write an arbitrary number of bytes to (or from) the stream. A stream may be used to store data in memory, or it may be used to access the file system (such as a FileInputStream). Streams may be "chained" together to form a more complex interaction. For example a text output stream, that converts data to a format suitable for text storage, might be chained to a file output stream, that takes this data from the text outptu stream and then writes it to disk.


FileOutputStream fs("out.txt");
TextOutputStream tos(&fs);
tos << 12345;

		

This will output the integer value 12345, convert it to a string of "12345", and then write that data to disk.

3.1.17.1. Base Functionality

The base functionality of any stream is to access the data buffer of the stream, retreive the stream's size (in bytes), get the current seek position (in bytes) from the beginning of the stream, and to move, or seek, to a specific position (again, specified in bytes) within the stream. Note that some stream implements may not implement these functions due to constraints of the specific stream type being implemented.

The base class for all streams is the VCF::Stream class which declares the above mentioned functionality in four functions: VCF::Stream::seek(), VCF::Stream::getCurrentSeekPos(), VCF::Stream::getSize(), and VCF::Stream::getBuffer().

3.1.17.2. Input Streams

Input streams are used to read data from the stream. The base class for all input streams is the VCF::InputStream class. All other input stream implementations derive from this.

The basic method for reading data from a stream is the InputStream::read() method, which takes a pointer to a buffer of bytes, and length argument, indicating the size (in bytes) of the buffer passed in. In addition to this basic read() method, the InputStream class also has a number of "helper" methods to make it easy to read common primitive types like an int, a double, a String, etc.

Here's a simple example that reads data from a file, and reads out 3 values, an int, a double, and a bool.


FileInputStream fis("test.dat");
int intVal = 0;
fis.read( intVal );

double dblVal = 0.0;
fis.read( dblVal );

bool boolVal = false;
fis.read( boolVal );

			

Input streams also support the ">>" extraction operator, so you could rewrite the previous example like so:


FileInputStream fis("test.dat");
int intVal = 0;
fis >> intVal;

double dblVal = 0.0;
fis >> dblVal;

bool boolVal = false;
fis >> boolVal;

			

3.1.17.3. Output Streams

Output streams are used to write data to the stream. The base class for all output streams is the VCF::OutputStream class. All other output stream implementations derive from this.

The basic method for writing data to a stream is the OutputStream::write() method, which takes a const pointer to a buffer of bytes, and length argument, indicating the size (in bytes) of the buffer passed in. In addition to this basic write() method, the OutputStream class also has a number of "helper" methods to make it easy to write common primitive types like an int, a double, a String, etc.

Here's a simple example that writes data to a file, and writes out 3 values, an int, a double, and a bool.


FileOutputStream fos("test.dat");
fos.write( 23403 );

fos.write( 0.040543298 );

fos.write( false );

			

Output streams also support the "<<" insertion operator, so you could rewrite the previous example like so:


FileInputStream fis("test.dat");

fis << 23403;

fis << 0.040543298;

fis << false;

			

3.1.17.4. Serializing classes

One way to serialize a data from a class is to use the Persistable interface. Any class that implement the Persistable is said to be "persistable" (deep huh?). The Persistable defines two methods, one for saving to an output stream and another for reading from an input stream. You can pass an instance of a perstable object to an InputStream's read() method or an OutputStream's write() method. Here's a quick example of how you might use this:


class Stuff : public Persistable {
public:
  virtual void 	saveToStream (OutputStream *stream) {
	stream->write( cookies );
	stream->write( veggies );
	stream->write( seeds );
  }

  virtual void 	loadFromStream (InputStream *stream) {
    stream->read( cookies );
	stream->read( veggies );
	stream->read( seeds );
  }
  
  int cookies;
  int veggies;
  int seeds;
};

Stuff stuff1;
stuff1.cookies = 12;
stuff1.veggies = 233;
stuff1.seeds = 9232;


FileOutputStream fos("test.dat");
fos.write( &stuff1 );


Stuff stuff2;

FileInputStream fis("test.dat");
fis.read( &stuff2 );

			

We create a simple class (Stuff) with three integer member variables, and then implement the Persistable interface's loadFromStream() and saveToStream() methods by simply reading and writing the member variables from the stream. The example initializes the stuff instance and then writes it to disk using the FileOutputStream. The reverse is then performed to read the data from the disk using the FileInputStream.

3.1.17.5. Serializing Variants

A VariantData object is a special holder for any sort of data, be it a primitive type, like an int, double, or bool, or some object instance. The simplest, though not neccessarily the most efficient, way to serialize a VariantData object is to call it's VariantData::toString() or VariantData::setFromString() methods. VariantData::toString() is used to store the data in a text format, while VariantData::setFromString() takes a string and converts it to an appropriate data type.

If you need to be more efficient about how the data encapsulated in a VariantData is serialized, then you should examine the VariantData's type member variable and read or write to the stream accordingly. If the VariantData::type member is set to pdObject, then you should use the dynamic_cast<Persistable*> to determine if the object instance supports this form opf streaming.

3.1.18. Files

Files are manipulated by using the VCF::File class. There is one static function used to determine if a given file name exists on the system. In addition to actual files, you can also manipulate file names using the FilePath class.

The VCF::File class allows you to open and query various traits about a given file or directory. You can use it to determine if the file name is a directory, or if you have read or write permissions to the file. You can determine the file size (e.g. the number of bytes the file takes on disk as reported by the filesystem) to an accuracy of 64 bits. For example:


File file( "C:\Foo\Bar\Baz.txt" );	
ulong64 sizeInBytes =  file.getSize();

if ( file.isReadable() ) {
	System::prinln("Readable!");
}

if ( file.isWriteable() ) {
	System::prinln("Writeable!");
}

		

Assuming you have read and/or write access to the file, you can get access to the file input/output streams by calling File::getInputStream() or File::getOutputStream(). As the caller of this function, you're responsible for deleting the new instance of the input or output stream returned (if any). This is functionally equivalent to using the FileIntputStream or FileOutputStream classes.

In addition to querying for information about a file, you can also perform certain common file operations on File instance. You can create a new file, remove (delete) an existing file, copy, or move (rename) an existing file. For example:


File newFile;
newFile.create( "HelloWorld.txt", File::ofReadWrite );
OutputStream* os = newFile.getOutputStream();
if ( NULL != os ) {
	os->write( "Hello" );
	os->free();
}

try {
	newFile.copyTo( "HelloWorld-redux.txt" );
	newFile.move( "Stuff.txt" );
	newFile.remove();
}
catch (	BasicException& e ) {
	System::println( String("Error!:\n\t") + e );
}

		

3.1.18.1. File Searching

You can also easily search for files using the Directory and Directory::Finder classes.

3.1.19. Dynamic Libraries

Dynamic Link Libraries (DLLs) or Shared Objects (SOs),on some Unix systems, are libraries that are linked to dynamically, at runtime, so that a single library's functionality can be easily shared amongst multiple applications. A DLL is loaded, and then the exported functions of the library at "linked" with at runtime for the executable that uses them. The VCF supports this through the Library class.

You can easily load the DLL up using the Library class, and then access the DLL's function pointers by calling the Library::getFunction() function. This will return a function pointer to a named function that you pass in when you make the call. If the function pointer does not exist, then the return value is NULL.

3.1.19.1. Init and term functions

When a DLL is loaded or unloaded you may want to have certain initialization or termination routines called. Different platforms support this in different ways, so to simplify this, the VCF enforces the following policy that you can take advantage of.

When the DLL is first loaded by the VCF's Library class, it attempt to find an exported function named "_vpl_init" ("vpl" stands for "Visual Package Library"). If this function is exported it should be function protottype should look like this:

typedef void (*initFunc)(OSHandleID);			
			

If this function exists, then the VCF will call it, ensuring that you have an initilization routine. The OSHandleID parameter is the Library peer's library handle, which may be NULL, depending on your platform. On Win32 platforms this is the HINSTANCE of the DLL.

The function you export for initialization must be named "_vpl_init", so somewhere in your code you'll have something like this:

void _vpl_init( OSHandleID handle )
{
  //init code here...
}
			

To ensure a termination routine gets called, a similar process takes place when the Library instance unloads the DLL. Just prior to actually unloading the DLL, the Library instance will look for a function exported by the name "_vpl_terminate". If it finds one, it will then invoke it, passing in the same peer handle that was passed into the initialization routine. The funtion prototype loks like this:

typedef void (*terminateFunc)(OSHandleID);			
			

Like the initialization routine, you must export with a specific function name for this to work, so you need to have an exported function like this:

void _vpl_terminate( OSHandleID handle )
{
  //cleanup code here...
}			
			

If the libraries do not support initialization and/or termination routines, then nothing will happen.

[Note]Note
In order for this to work, you must load the DLL with the Library class, otherwise these specific routines will not get called (unless you write explicit code to check for their presence and invoke them yourself).

3.1.20. Processes

The VCF supports executing external processes through the Process class. You can start any process using this class and pass any number of arbirtrary command line arguments to it. The only limits over what processes you can run are those imposed by the host OS.

Starting a process is easy:

Process p1;
p1.createProcess( "XCOPY", "/?" );
		

The createProcess() call will not block, so if you need to wait for the process to complete, you can call the Process::wait() function, like this:

Process p1;
p1.createProcess( "XCOPY", "/?" );
p1.wait(); //blocks indefinitely till the "XCOPY" process ends.
		

3.1.20.1. Process Output

If you need to run a process that outputs content to stdout, but you want to capture this output for your own use, such as displaying the progress of a compiler process, you can use the ProcessWithRedirectedIO class to help with this.

The ProcessWithRedirectedIO class hooks into the stdout output stream and captures output data as it becomes available. Once a chunk of data is made available, the framework will send an event to the class's OutputReady delegate. The current implementation assumes that the data in the stream is text data. The OutputReadyEvent holds this data in the form of String, which does allow arbitrary bytes to be assigned to it.

An important note: when your event handler is invoked, it will be done in a different thread than the thread that created the ProcessWithRedirectedIO instance and started the process running. You should plan for this accordingly and make sure that if you need to make changes to UI classes that you post an event to the UIToolkit, instead of modifying the class directly. This is especially true for making modfications to Model based classes.

3.1.21. XML Parsing

The VCF supports basic XML parseing through the XMLParser, XMLNode, and XMLAttr classes. These handle all the basic XML tags including CDATA, attributes, the use of "<![CDATA[" entity for arbitrary text data, and basic character tags like "<" (&lt;). Fancier features like XML Namespaces, and XSLT are not supported, nor is validating the XML to check that it's correct XML.

The parser is quite easy to use, and you can work with in either a SAX fashion, where you are notified via an event as the XML is parsed, or DOM style, where you traverse the whole tree of xml nodes once the xml data has been completely parsed. Of the two, the second, DOM styles approach is easier to use.

3.1.21.1. SAX style parsing

The XMLParser will notify event handlers whenever a new tags is encountered, or whenever a new chunk of character data is eoncountered.

To make use of this, simply add an event handler to either the XMLParser's NodeFound delegate or the NodeCDATAFound delegate. The NodeFound is called when a new entity or node tag is encountered, and the NodeCDATAFound is called whever text (or character data) is encountered between the start and end tag of a node.

3.1.21.2. DOM style parsing

While the XMPParser class is parsing the XML data, it builds a tree of XML nodes, represented by the XMLNode class. Once the parsing is done, you can get an Enumerator of all the nodes that the parser created and you can enumerate through them.

   Comments or Suggestions?    License Information