-
Notifications
You must be signed in to change notification settings - Fork 6
Object System
Zaf defines an object system to provide limited dynamic and reflection abilities to classes. This article introduces its basic concepts and usage.
Like other object-oriented programming languages, zaf defines a root base class zaf::Object
, which is derived by all classes who want to join the object system and make use of its abilities. zaf::Window
and zaf::Control
are base classes to window and control separately, they are both derived from zaf::Object
, therefore all windows and controls in zaf join the object system.
zaf::Object
has a static constant pointer Type
points to a zaf::ObjectType
object, which represents static type information of the class. It can be used to access class meta information such as class name and properties. zaf::Object
also has a virtual GetType()
method to get a same type pointer that represents dynamic type information of the object. Here is an exmple of zaf::ImageBox
class, it is derived from zaf::Control
which is derived from zaf::Objecct
:
//Output type information of classes.
std::wcout << zaf::Object::Type->GetName() << std::endl; //Output "Object"
std::wcout << zaf::Control::Type->GetName() << std::endl; //Output "Control"
std::wcout << zaf::ImageBox::Type->GetName() << std::endl; //Output "ImageBox"
//Output type information of a zaf::ImageBox object.
std::shared_ptr<zaf::ImageBox> image_box = zaf::Create<zaf::ImageBox>();
std::wcout << image_box->Type->GetName() << std::endl; //Output "ImageBox"
std::wcout << image_box->GetType()->GetName() << std::endl; //Output "ImageBox"
std::shared_ptr<zaf::Control> control = image_box;
std::wcout << control->Type->GetName() << std::endl; //Output "Control"
std::wcout << control->GetType()->GetName() << std::endl; //Output "ImageBox"
std::shared_ptr<zaf::Object> object = image_box;
std::wcout << object->Type->GetName() << std::endl; //Output "Object"
std::wcout << object->GetType()->GetName() << std::endl; //Output "ImageBox"
zaf::ObjectType
has GetBase()
mehtod to get base class’ type information. The relationship of these classes is shown in the picture below:
Note that the three classes derived from zaf::ObjectType
are internal and are invisible to users, they can be accessed only with zaf::ObjectType
interfaces.
In zaf, objects are typically dynamic allocated and are manged by std::shared_ptr
. Besides constructors, objects have further more initialization steps, therefore they must be created using zaf::Create()
function, otherwise they might not be initialized properly.
During initialization, two virtual methods of zaf::Object
is called in order:
-
Initialize()
, called right after consturctor. -
AfterParse()
, called after parsing associated XAML (this is another topic which will be introduced in the future).
Derived classes may override these two methods to do extra initialization they needed. For example, a window class may create and add controls into window in Initialize()
.
A few objects are not necessarily be dynamic allocated and thus no need to use zaf::Create()
to create them. For example, zaf::Rect
, zaf::Size
, zaf::Point
are typically stack allocated, and they have nothing to do in Initialize()
or AfterParse()
.
To make a class join the object system, you need to follow the following steps:
- Make the class be derived from
zaf::Object
. - Add macro
ZAF_DECLARE_TYPE
in the class’ public section, to declare that this class is joined the object system. This macro declares necessarily interfaces to the class. - In .cpp, use macros
ZAF_DEFINE_TYPE()
andZAF_DEFINE_TYPE_END
to define implementation of declared interfaces.
Here is an example:
//MyObject.h
#include <zaf/object/object.h>
class MyObject : public zaf::Object {
public:
ZAF_DECLARE_TYPE;
};
//MyObject.cpp
#include "MyObject.h"
#include <zaf/object/type_definition.h>
ZAF_DEFINE_TYPE(MyObject)
ZAF_DEFINE_TYPE_END;
There are two virtual methods IsEqual()
and Hash()
in zaf::Object
to provide object equality ability.
The default implementation of IsEqual()
checks if two objects are equal by their memory address. Derived classes can override this method to implement their own equality strategy.
The default implementation of Hash()
uses std::hash<>
to calculate the hash value of object’s memory address. Derived classes can override this method to implement their own hash strategy.
If a class has already defined operator==
and specialized std::hash<>
, it can use ZAF_DECLARE_EQUALITY
and ZAF_DEFINE_EQUALITY()
macros to simplify the definition of IsEqual()
and Hash()
. For example:
//MyObject.h
#include <zaf/object/equality.h>
#include <zaf/object/object.h>
class MyObject : public zaf::Object {
public:
ZAF_DECLARE_TYPE;
//Declare IsEqual() and Hash().
ZAF_DECLARE_EQUALITY;
};
//Already defined operator==.
inline bool operator==(const MyObject&, const MyObject&) {
//...
}
//Already specialized std::hash<>.
namespace std {
template<>
struct hash<MyObject> {
std::size_t operator()(const MyObject&) {
//...
}
};
}
//MyObject.cpp
#include "MyObject.h"
#include <zaf/object/type_definition.h>
ZAF_DEFINE_TYPE(MyObject)
ZAF_DEFINE_TYPE_END;
//Define implementation of IsEqual() and Hash().
ZAF_DEFINE_EQUALITY(MyObject);