Visual Component Framework - Libraries and Projects
Environment: VC 6, Windows NT/2K
Editor's Note
The creator of this toolkit (Jim Crafton) has also sent in a fantastic Scribble tutorial to show how easy it is to get up to speed and create applications quickly using the VCF.Introduction
The Visual Component Framework was inspired by the ease of use of environments like NeXTStep's Interface Builder, Java IDE's like JBuilder, Visual J++, and Borland's Delphi and C++ Builder. I wanted a generic C++ class framework I could use to build app's quickly and visually (when designing UIs), as well as having the core of the framework be as cross platform as possible. This article will discuss some of the issues I ran into, and how I attempted to solve them. The Visual Component Framework is an Open Source project, so feel free to grab and use it if you think it might be useful. If you're really adventuresome you can volunteer to help develop it, making it even better, especially in tying it into the VC++ environment as an add-in. For more information on either the project, helping out, or just browsing the Doc++ generated documentation please go to the VCF project on Source Forge here, or the project website here. The code is available from CVS (follow the how-to here for setting up CVS on windows), or the a zip file here (it's around 1.8 MB - this includes the XML libs for Xerces - an XML parser. Check the site for the latest version).
The Visual Component Framework (VCF) is divided into three DLL's, the
FoundationKit, the GraphicsKit, and the ApplicationKit. This article discusses
the FoundationKit, the heart of the VCF. The FoundationKit provides the basic
core classes and the advanced RTTI capabilities of the framework. Early on I
knew I needed some sort of RTTI, or reflection, similar to what is provided in
Java or ObjectPascal. This was a requirement because visual design environments
need to a way to expose a component's properties and events, and to provide a
way to edit them in a consistent, customizable and extendable manner. With this
in mind the framework allows the developer to query an object for its Class,
which in turn provides access to the class's name, superclass, Properties,
Events and Methods. To achieve this the framework makes heavy use of templates
and STL. Class is an abstract base class that template classes
device from. Classes provide the following information:
- the name of the
Class- this is stored in a member variable rather than relying ontypeid(...).name()to retrieve the class name. Not all compiler's support thetypeid(...).name()function. - the Class ID - this represents a UUID (Universally Unique IDentifier) for the class. This will prove useful when distributed features creep in.
- the ability to discover all the properties of a
Classat runtime. A property is defined as some class attribute that is provided access to via getter and setter methods. Properties can be primitive types (int, long double, etc), Object derived types, or enums. Properties can also be collections of other objects. - retrieving the super class of the class.
- the ability to create a new instance of the class the
Classobject represents. This of course assumes a default constructor is available.
The first step is (obviously) making sure that your class is derived from a Framework object. For example:
class Foo : public VCF::Object { //this is OK
...
};
//this is bad - there is no way to hook the RTTI up without at
//least deriving from VCF::Object
class Foo {
};
Next you should define a class id (as a string) for your class. On Windows I use guidgen.exe to create UUIDs. The define should look something like this:
#define FOO_CLASSID "1E8CBE21-2915-11d4-8E88-00207811CFAB"The next step is to add to macros to your class definition (.h/.hpp file). These are required for the basic RTTI info (class-super class relation ships) and to make sure you'll inherit any properties of your super class. For example:
class Foo : public VCF::Object {
public:
BEGIN_CLASSINFO(Foo, "Foo", "VCF::Object", FOO_CLASSID)
END_CLASSINFO(Foo)
...
};
The macro takes the class type-id, the string to use as the class name, the
string that represents the classes supper class, and a string that represents
the class id (where the class id is a string representation of a UUID). What the macros end up creating is a public nested class used to register your class that you're
writing. The above macros generate the following inline code for the developer
of the Foo class.
class Foo : public VCF::Object {
class FooInfo : public ClassInfo<Foo> {
public:
FooInfo( Foo* source ):
ClassInfo<Foo>( source,
"Foo",
"VCF::Object",
"1E8CBE21-2915-11d4-8E88-00207811CFAB" ){
if ( true == isClassRegistered() ){
}
}
virtual ~FooInfo(){}
};//end of FooInfo
...
};
The isClassRegistered() method checks the ClassRegistry
to see if the class is already registered, if it is not then a new entry in the ClassRegistry
is made. Classes are stored in a singleton ClassRegistry object,
which contains a single Class instance for each registered class
type, thus multiple object instance's share a Class instance. To do
this the framework has an abstract class defined (Class), and two template
classes TypedClass and TypedAbstractClass. The
template parameter specified in the TypedClass and TypedAbstractClass
are used to safely allow class comparisons and to allow the creation of an
object at run time (this feature is only supported by TypedClass).
The two template classes are necessary because it is possible to have abstract
classes in the framework that by definition cannot be instantiated, but need to
in the class hierarchy, since all Class instances have a getSuperClass()
method, allowing the programmer to walk the class hierarchy at runtime.
The ClassRegistry
keeps all the classes in a map, and each time a new Class
instance is registered, the super class is found and all of it's properties are
copied over to the newly registered instance, making sure that derived classes
"inherit" the properties and events of their super class.
To add more detailed RTTI you can add properties, events, and methods. An example of this can be found in the Component class declaration:
class APPKIT_API Component : public Object, public Persistable{
public:
BEGIN_ABSTRACT_CLASSINFO(Component,
"VCF::Component",
"VCF::Object",
COMPONENT_CLASSID)
PROPERTY(double, "
left",
Component::getLeft,
Component::setLeft,
PROP_DOUBLE );
PROPERTY(double, "top",
Component::getTop,
Component::setTop,
PROP_DOUBLE );
PROPERTY(String, "name",
Component::getName,
Component::setName,
PROP_STRING );
EVENT("VCF::ComponentEvent",
"onComponentCreated",
"VCF::ComponentListener",
"VCF::ComponentHandler" );
EVENT("VCF::ComponentEvent",
"onComponentDeleted",
"VCF::ComponentListener",
"VCF::ComponentHandler" );
END_CLASSINFO(Component)
...
This demonstrates exposing three properties and two events. Properties allow
you to dynamically discover the attributes of an object at runtime. A Property
holds a method pointer to an accessor method (or "get" method), and
optionally a method pointer to a mutator or "set" method. In addition
a Property has display name, and a display description that can be
read and modified. Like the Class class, the Property
is abstract, with mostly virtual pure methods, to allow the framework to have an
arbitrary collection of properties without knowing the exact type . The real
work is done by template classes that derive from Property and
properly implement the methods. To allow the generic getting and
setting of a wide variety of types, another class is used in conjunction with Property
called VariantData. This class wraps most of the C++ standard primitive types,
String's, Enum's (more on those later), and Object derived classes. The core of
the class is a union of types, the one exception being a reference to a String
(which is just a typedef around std::basic_string<char>). It
also has a member variable that describes the type of data the instance holds.
union{
int IntVal;
long LongVal;
short ShortVal;
unsigned long ULongVal;
float FloatVal;
char CharVal;
double DblVal;
bool BoolVal;
Object* ObjVal;
Enum* EnumVal;
};
The rest of the class provides conversion and assignment operators allowing you to write code like this:
VariantData v; int i = 12; double d = 123.456; String s = "Foo"; v = s; // v now holds a reference to the String s, // and it's data type is automatically set to PROP_STRING v = i; // v now stores the int value 12, // and data type is PROP_INT v = d;// v now stores the double value 123.456, // and the data type is PROP_DOUBLE
The assignment functions allow for the sort of magic we see above. It also makes sure the data type is set correctly. One of the conversion/assignment functions looks like this:
operator float (){
return FloatVal;
}
operator=( const float& newValue ){
FloatVal = newValue;
type = PROP_FLOAT;
}
Why is this useful ? Because we can have a collection of properties of a
given Object at runtime, we will not necessarily know what the types are. The VariantData
class allows us to ignore this, allowing the compiler to sort out what needs to
happen for us. In other words I might have a collection of properties, one of
which is an int, another some Object*, and a third a bool. The VariantData
lets me write the same style of code for any of the types, and the compiler will
resolve the type for me. This happens because our "get" and
"set" methods have type signatures with them and when the VariantData
instance is used, the compiler invokes the correct conversion operator based on
the method signature.
Ah, but how do we get these method signatures defined ? Remember that the Property
class is an abstract one. The real work is done through several other template
classes that derive from Property, but actually implement the
methods of Property. So lets take a look at the TypedProperty
class. This class uses it's template type to specify the type of data that it is
to represent. It then declares typedefs to get and set member functions for and
Object.
template <class PROPERTY> class TypedProperty : public Property {
public:
typedef PROPERTY (Object::*GetFunction)(void);
typedef void (Object::*SetFunction)(const PROPERTY& );
...
protected:
GetFunction m_getFunction;
SetFunction m_setFunction;
};
Now we have member function pointers in our property class that are type
safe, in other words, if we specified a new Property for an int type (TypedProperty<int>),
then the get and set functions would read as follows:
- Get:
typedef int (Object::*GetFunction)(void); - Set:
typedef void (Object::*SetFunction( const int& );
So when our Property::set(Object* source, VariantData* value )
method is called, the magic in the VariantData mentioned
above occurs, in other words, the set() method in TypedProperty<int>
class binds the source to the set method method pointer (m_setFunction),
and passes in the de-referenced value pointer, which in turn causes
the VariantData's int() conversion operator to be
called by the compiler, and now we have safely passed in an int value to the
function, without ever needing to worry about it. This process works the same
for any of the types mentioned above, though there are specific template classes
that derive from Property to support Object and Enum
pointers.
I've mentioned a the Enum class a couple of times now, and I
imagine you're all just dying with anticipation to discover just who and what
these little fella's are. The Enum class is used, not surprisingly,
to wrap C++ enum types and provide a type safe way of using them without
worrying about the specific type at runtime. This allows for iterating the enum
values (and wrapping back to the beginning), and retrieving the first and last
enum values. It also allows for having string representations of the various
enum values for display purposes. Enum classes can be set either by
the actual enum type, as an int, or from a string.
Well that about wraps it up for now. I will try and write the next two installments as soon as I can (assuming anyone is actually interested in this kind of coding insanity !)
Downloads
Warning: This is a serious development framework and as such it might take a while to download the entire set of source codeDownload source for the VCF Framework (latest zip from CVS tree)- 3.2 Meg
Download source for the VCF Framework (InstallShield Installer) - 5.8 Meg

Comments
Looks good
Posted by holyhhw on 09/20/2005 04:43amSimply rename the *.zip to *.exe
Posted by Legacy on 01/12/2003 12:00amOriginally posted by: QCWorld.net
Don't worry for those found the zip sth wrong...
Replythat's right, it has a MZ signature, not a zip file,
it is an exe excutable...
Simply rename the *.zip to *.exe
then things would be fine...
Installer is not a zip file (having 'MZ' signature)
Posted by Legacy on 11/24/2001 12:00amOriginally posted by: Liviu Catrina
...all whe have to do is to rename to exe.
ReplyDownload vcf-Installer from ftp://vcf.sourceforge.net/pub/vcf/VCFInstaller.exe
Posted by Legacy on 09/07/2001 12:00amOriginally posted by: CeeBee
I have loaded the vcf-Installer from ftp://vcf.sourceforge.net/pub/vcf/VCFInstaller.exe
ReplyAlso can't unpack
Posted by Legacy on 08/31/2001 12:00amOriginally posted by: Friedhelm
Hi,
as Jordan remarked earlier, I do have again same problem with unzipping the installer file. There was no comment to Jordans posting, so I ask for help again.
TIA
ReplyFriedhelm
Great ideea but somehow impractical to use
Posted by Legacy on 03/28/2001 12:00amOriginally posted by: Eugen Paval
First of all this is something very impressive. The possibilities opened by having RTTI and reflection available to you are endless especially when you are concerned with building generic tools, classes, etc. What I really don't like is the fact that I, as a developer, have to hardcode all this extra information into my classes. I can easily make mistakes or leave things out of sync like declaring type information for one event as something and having the actual event handler being a (totally) different thing. This stands true especially in large projects involving multiple team members that are modifying frantically the code every day. Althoug being a huge undertake you might want to consider generating the necessary type information with a precompiler tool. In this way we will have the best of both worlds: reflection and the same way of writing code (no macros, no extra coding). Being an open source project someone might want to pick up this job.
ReplyBuild and run problem!
Posted by Legacy on 03/22/2001 12:00amOriginally posted by: doon.
hi, jim.
i have installed the VCF successfully. Following the steps i have built the DLLs: FoundationKitDll, FoudationKit, GraphicsKitDll, GraphicsKit, ApplicationKitDll, ApplicationKit, then i built VCFBUILDER.exe and INTEGRATOR.exe. when i run VCFBUILDER.exe, i get a crash. but when i run VCFBUILDER_D.exe, I can only see 3 three windows: the mainapp, the object inspector window and project window. but all these window is blank and i can see nothing in them, only menus at mainapp window. and press the menu i can't insert any VCF objects and open any dialogs. some menu can cause a crash.
also i have run the scribble example, i found that it can only run for debug version.
what shall i do to solve these problems?
doon.
ReplyCan't unpack InstallShield Installer
Posted by Legacy on 03/21/2001 12:00amOriginally posted by: jjordan
I've tried to download it a couple of times. It says "The file is damaged or unknown format". Second ZIP file is ok.
Thanks.
ReplyI don�t find RefVectorOf.c file
Posted by Legacy on 03/15/2001 12:00amOriginally posted by: David Dietta
I am very interesting on this subject, but I do not make
ReplyApplicationKitDLL.dll. The next file is missing RefVectorOf.c
I try looking for in www.apache.org and www.ibm.com without
succes. May anybody help me ?