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. Exceptions
3.1.8. RTTI
3.1.9. Events
3.1.10. Synchronization Primitives
3.1.10.1. SynchObjects
3.1.10.2. Waiting for ...
3.1.10.3. Mutexes
3.1.10.4. Semaphores
3.1.10.5. Conditions
3.1.11. Threads
3.1.12. Run Loops
3.1.12.1. Relationship to Threads
3.1.12.2. Run Loop Source
3.1.12.3. Timers
3.1.12.4. Posting Events
3.1.13. Strings
3.1.13.1. STL usage
3.1.13.2. Data Representation
3.1.13.3. Conversion and Text Codecs
3.1.13.4. Formatting
3.1.13.5. Utilty Functions
3.1.13.5.1. Trimming
3.1.13.5.2. Number conversion
3.1.13.5.3. UUID generation
3.1.13.5.4. Type ID
3.1.13.5.5. Formatting
3.1.14. Locales
3.1.14.1. Naming
3.1.14.2. Numeric Formatting
3.1.14.3. Date Time Formatting
3.1.14.4. Collation
3.1.14.5. Translation
3.1.15. Resources
3.1.15.1. Accessing a resource
3.1.15.2. Program Information
3.1.15.3. Platform Specific
3.1.15.4. Platform Neutral
3.1.16. System Functions
3.1.16.1. String functions
3.1.16.2. Filesystem Functions
3.1.16.3. Date/Time Functions
3.1.16.4. Environment Functions
3.1.16.5. Resource functions
3.1.16.6. Locale functions
3.1.16.7. Miscellaneous functions
3.1.17. Dates and Time
3.1.17.1. Data Representation
3.1.17.2. Date Time Spans
3.1.17.3. Iterating
3.1.17.4. String Formatting
3.1.18. Streams
3.1.18.1. Base Functionality
3.1.18.2. Input Streams
3.1.18.3. Output Streams
3.1.18.4. Serializing classes
3.1.18.5. Serializing Variants
3.1.19. Files
3.1.19.1. File Searching
3.1.20. Dynamic Libraries
3.1.20.1. Init and term functions
3.1.21. Processes
3.1.21.1. Process Output
3.1.22. XML Parsing
3.1.22.1. SAX style parsing
3.1.22.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. Components
3.3.8. Controls and Containers
3.3.8.1. Control Listing
3.3.9. Borders
3.3.10. Layout
3.3.10.1. Standard Container layout
3.3.10.2. Horizontal layout
3.3.11. Frames, Dialogs, and Windows
3.3.11.1. Dialog usage in VFC
3.3.11.1.1. Standard Dialogs
3.3.11.1.2. Custom Dialogs
3.3.11.2. Frame Sizing
3.3.11.3. Frame usage in the VFC
3.3.11.4. Window usage in the VFC
3.3.12. Model/View/Controller
3.3.12.1. Models
3.3.12.2. Items
3.3.12.3. Views
3.3.12.4. Controls
3.3.12.5. Document/View Architecture
3.3.13. Undo/Redo and Commands
3.3.14. Help
3.3.14.1. Implementing Help for Win32 systems
3.3.14.1.1. Creating the HTML Help project file
3.3.14.1.2. Adding HTML content
3.3.14.1.3. Adding Index/Search support
3.3.14.1.4. Adding a Table of Contents
3.3.14.1.5. Adding Context Sensitive entries
3.3.14.1.6. Adding "What's This" entries
3.3.14.1.7. Compiling the help with the HTML Help Compiler
3.3.15. Control Focus and Activation
3.3.16. Accelerator Keys ("Hot Keys")
3.3.17. 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.5. HTML Kit
3.6. Internet Kit
3.7. OpenGL Kit
3.8. NetworkKit
3.9. Database Kit
3.10. JavaScript Kit
3.11. RegEx Kit
3.12. RemoteObjectKit

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. Exceptions
3.1.8. RTTI
3.1.9. Events
3.1.10. Synchronization Primitives
3.1.10.1. SynchObjects
3.1.10.2. Waiting for ...
3.1.10.3. Mutexes
3.1.10.4. Semaphores
3.1.10.5. Conditions
3.1.11. Threads
3.1.12. Run Loops
3.1.12.1. Relationship to Threads
3.1.12.2. Run Loop Source
3.1.12.3. Timers
3.1.12.4. Posting Events
3.1.13. Strings
3.1.13.1. STL usage
3.1.13.2. Data Representation
3.1.13.3. Conversion and Text Codecs
3.1.13.4. Formatting
3.1.13.5. Utilty Functions
3.1.13.5.1. Trimming
3.1.13.5.2. Number conversion
3.1.13.5.3. UUID generation
3.1.13.5.4. Type ID
3.1.13.5.5. Formatting
3.1.14. Locales
3.1.14.1. Naming
3.1.14.2. Numeric Formatting
3.1.14.3. Date Time Formatting
3.1.14.4. Collation
3.1.14.5. Translation
3.1.15. Resources
3.1.15.1. Accessing a resource
3.1.15.2. Program Information
3.1.15.3. Platform Specific
3.1.15.4. Platform Neutral
3.1.16. System Functions
3.1.16.1. String functions
3.1.16.2. Filesystem Functions
3.1.16.3. Date/Time Functions
3.1.16.4. Environment Functions
3.1.16.5. Resource functions
3.1.16.6. Locale functions
3.1.16.7. Miscellaneous functions
3.1.17. Dates and Time
3.1.17.1. Data Representation
3.1.17.2. Date Time Spans
3.1.17.3. Iterating
3.1.17.4. String Formatting
3.1.18. Streams
3.1.18.1. Base Functionality
3.1.18.2. Input Streams
3.1.18.3. Output Streams
3.1.18.4. Serializing classes
3.1.18.5. Serializing Variants
3.1.19. Files
3.1.19.1. File Searching
3.1.20. Dynamic Libraries
3.1.20.1. Init and term functions
3.1.21. Processes
3.1.21.1. Process Output
3.1.22. XML Parsing
3.1.22.1. SAX style parsing
3.1.22.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().

Creating and destroying Objects.  Creating objects on the heap is done using the typical operator new(). However, to destroy an instance that is directly or indirectly derived from VCF::Object, you should call VCF::Object::free() if you own the object, or VCF::Object::release() to decrement the refcount on 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 otehr virtual methods. After VCF::Object::destroy() returns, it then deletes the instance. So, if you have a class that is intended to be used on the heap, it's a good idea to override the destroy() method (keeping it in protected scope), and put your clean code in there, instead of the destructor.

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. 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.8. 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

  • getEvents() - allows you to enumerate all the events 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.9. Events

Events happen to us every day. When we open a door, when we go to work, when we fall asleep. Objects in a framework, particularly objects that represent a user interface, are no different. Events are triggered when a user closes a window, or clicks on a button. Events can happen when the state of an object changes, such as reading data from a stream, or upon the object's creation. Not only do events happen, but quite often one object wants to be notified when a particular event happens to another object, and frequently the two objects may have no direct knowledge of each other beyond a basic base class. To further complicate matters, it is entirely possible that more than one object wants to notified when the event happens!

One of the goals of the VCF is to provide a uniform architecture for this and provide a solution that allows events to be used every where in the framework, but that in no way ties their usage just to user interface classes. So this is an overview and explanation of how the event mechanism is used in the VCF.

In most frameworks event handling is done through callbacks, and their are many different ways to approach this. In C++ this gets a bit tricky due to the nature of C++ member functions, so we need a way to wrap the callback function, which itself needs to be a member function. In addition, we need a consistent way to let objects know that we are interested in being notified when a particular event happens, as well as telling the same object when we are no longer interested in the notifications.

To accomplish this in the VCF we have three main players: the source, or object that fires the event, the event, which is itself represented by an object, and the observer ( also called listener ), or the object that wishes to receive some notification when the event occurs and have that notification call a method on the observer object. Each of these objects has to have certain responsibilities as described in the table below.

Table 3.1. 

ObjectResponsibilities
Source objectThe source must provide a way to register, and un-register one or more observer objects with a particular event that will be fired by the source object.
Event objectThe Event object must provide data to be used by the observer objects. For example, mouse event object would contain data about the position of the mouse, the buttons that were held down, etc. In addition, the event must be able to expose who actually fired the event, in other words provide a pointer back to the source of the event.
Observer objectThe observer must define one or more callback methods that will be invoked during the firing of a specific event by the source object that the observer has registered with.

To make all of this work in the VCF there are three main classes that are used. The first is the EventHandler class. This provides an interface that wraps a C++ callback function, be it a static function, or a class member function. The EventHandler class itself is an abstract class, the developer creates concrete template classes when using event handlers. For example:


class MyClass : public VCF::Object {
public:
	void onButtonClick( ButtonEvent* e ) {

	}
};

MyClass* myClassInstance = ..... //get a MyClass instance
VCF::EventHandler* handler = 
	new ButtonEventHandler<MyClass>( myClassInstance, &MyClass::onButtonClick, "onButtonClick" );

		

The above example creates an event handler and attaches the calss member function MyClass::onButtonClick to the event handler.

The next part of this is the Delegate class. The Delegate represents a single event that would be fired by the source object, and holds a collection of EventHandlers. The Delegate allows you to add or remove event handlers at will. For example:


EventHandler* handler = ...//get handler from somewhere

Delegate d;
//add a handler
d.addHandler( handler );

//remove handler
d.removeHandler( handler );

//or if you prefer the syntactical "sugar"
d += handler;

//to remove the handler
d -= handler;

		

The Delegate also handles routing a single Event instance, as the result of the source object firing an event, to all of the handlers that have registered with it. This is accomplished by calling the Delegate::fireEvent() method, and passing in an event.


Delegate d;
Event event( this, 123 );
//fire the event
d.fireEvent( &event );

		

That's all there is to it! Delegate::fireEvent() will take the event, and loop through it's list of event handler's. For each handler, it will check to verify the event has not been marked as being consumed (Event::isConsumed() returns false), and then call the EventHandler::invoke() method on the handler, which in turn invokes the callback function.

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 Delegate and giving it the name of the event that will be fired. For example:


class Foo : public VCF::Object {
public :
	enum {
		TalkingEvent = 0x111111
	};
	Delegate Talking;
  
	void speak() {
		Event talkingEvent( this, TalkingEvent );
		
		Talking.fireEvent( &talkingEvent );		
	}
};

		

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


class FooObserver : public VCF::ObjectWithEvents {
public:
	void onTalking( Event* e ) {
		System.println( "FooObserver got it's onTalking() method called!" );
	}
};

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

		

Talking events are fired if the Foo::speak() method is called, and we can see how we have connected the two object (source and observer) through the use of Delegates and EventHandlers.

3.1.10. 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.10.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.10.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.10.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.10.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.10.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.

3.1.11. 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.12. Run Loops

3.1.12.1. Relationship to Threads

3.1.12.2. Run Loop Source

3.1.12.3. Timers

3.1.12.4. Posting Events

3.1.13. 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.13.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.13.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.13.3. 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.13.4. 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.13.5. Utilty Functions

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

3.1.13.5.1. Trimming

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

3.1.13.5.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.13.5.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.13.5.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.13.5.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.14. 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.14.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.14.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.14.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.14.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.14.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.15. 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.15.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.15.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.15.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.15.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.16. 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.16.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.16.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.16.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.16.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.16.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.16.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.16.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.17. Dates and Time

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

3.1.17.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.17.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.17.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.17.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.18. 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.18.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.18.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.18.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.18.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.18.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.19. 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.19.1. File Searching

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

3.1.20. 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.20.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.21. 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.21.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.22. 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.22.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.22.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