Continue with last article, we will try to write an identical application to use OO features including: encapsulation, inheritance, polymorphism, properties, meta info and event-driven mechanism. Java supports the 3 basic features in language level. It uses interfaces to implements event-driven. To implements properties and meta info, we have to write our own code. We want to implements API like someObject.setProperty(prop-name, prop-value). I write my own NewObject class:

To use our setProperty()/getProperty() method, all classes should derive from the NewObject class. To be consistent with the JavaBean convention, we assume that the getter/setter function to be “get”/”set” + capitalize_first_letter_of(member-variable-name).

Property annotation and PropertyAccess enum are defined to indicate properties:

ClassInfo and ClassInfoList annotation are defined to indicate class meta info:

Let’s see how to use them, our Base is defined as:

Since our implementation of properties are simply methods, they can be inherited by subclasses. But the class meta info cannot be retrieved in subclasses. They just get their own.

I do not want to demo events/listeners code here, just find them in source code in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestJavaObject-{date}.zip file.

From the last series of GObject library, we know the approach of OOP using C. Now, I just want to have a comparison of OO implementation in all leading programming languages: C, C++, Java and C#. I will use C++/Qt in this article. Apart from basic features like encapsulation, inheritance, polymorphism, I will demonstrate how to use advanced features including properties, meta info and event-driven programming.

Now, let’s start. Since C++ supports inheritance and polymorphism in language level. They are not the problem. When encounter encapsulation, it does not do well. We CAN declare member variables as private to prohibit their directly access. But the internal implementation is still exposed. When adding/removing private member variables, the class structure is changed. This can cause binary compatible issues. According to the guide of Qt, we define a private class to hold all private member variables, and add the pointer of it to our public class. The size of pointer is constant in all platforms, so this will not break the binary compatibility. Here’s the code:

NOTE: To use Qt’s object mechanism, your class should inherit QObject class and include the Q_OBJECT macro.

Just note the forward declaration of QBasePrivate private class. It is define in *.c file, and cannot be used by client applications. We defined a d_ptr protected member variable of this type to hold all private data values. Qt library provideds a series of easy-to-use macros to support this scheme to implementation:

Qt library supports properties and meta info. properties are defined with Q_PROPERTYmacro, while class meta info are defined with Q_CLASSINFO. Both of them can be inherited by derived classes. Last is Qt’s event-driven mechanism: signals/slots. Since they are also based on QObject, we had to define a test class to include all slots:

Test code:

All source code is available in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestQObject-{date}.zip file.

Recall there are 3 types in GObject type system: Fundamental, static and dynamic. A fundamental type is a top-most type which has no parent type. Most of them are pre-defined. Static types never load/unload its class type (say, their class struct) at runtime, since they are static. On the contrary, dynamic types can be dynamically loaded/unloaded at runtime. They are normally used within a module.

We can call g_type_register_dynamic() to register a dynamic type. When used in a module of GObject library (may be a GTypeModule type), We can also call g_type_module_register_type() to create your dynamic types. g_type_register_dynamic() is invoked for you in that function. Let’s go through the code:

NOTE: PLEASE READ ALL COMMENT CAREFULLY.

The implementation structure may be a little different with the stuff when creating a static type. An additional parameter GTypeModule is passed in. It represents the module your dynamic type belongs to. So, when the module is unloaded, all dynamic types in it are unaccessible.

Also note the bar_type_class_finalize() function. We use it to override the finalize() virtual function in GObjectClass. Now you can do un-initialiation in this function. It is like the destructor in a C++ class.

Let’s move on to the module type. This type inherits GTypeModule:

GTypeModule is an abstract type. We should implements its load() and unload() virtual function.

Our test code:

Another dynamic type BarType is defined in addition to FooType to demo the usage. The output maybe:

See the init/finalize process?

At the end of my note, Let me summarize to compare GObject library with C++ implementation:

1. Member Variables:

GObject C++
in class struct class meta info
in object struct class instance member
global variable class static member

2. Function Callbacks:

GObject C++
base_init init class dynamic meta info
base_finalize finalize dynamic class meta info, only dynamic types use it
class_init init class static meta info
class_finalize finalize class static meta info, only dynamic types use it
instance_init init instace, like C++ constructor
override finalize in GObjectClass finalize instance, like C++ destructor

All source code is available in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestGObject-{date}.zip/TestGObject7 folder.

Interfaces usage in library is like class usage. We need to define a interface struct, but no object struct is needed:

NOTE: PLEASE READ ALL COMMENT CAREFULLY.

Then we register the interface using g_type_register_static() with G_TYPE_INTERFACE as first parameter. For interfaces, we only need to assign base_init() and base_finalize()callbacks.

As described in the official document, we should allocate dynamic memebers of class struct in base_init(). Otherwise, all copies of the class struct share only one copy of dynamic members. This leads to problems.

Let’s define the type which implements the interface:

Note the naming convention I used here. Our FakeDesktop class will implement the FakeIServer interface and another FakeIClient interface. This time do not use corresponding interface struct as the first members of FakeDesktop and FakeDesktopClass. Interface info will be added dynamically when initialize a real instance of FakeDesktop. Let’s move to the *.c code:

Note the g_type_add_interface_static() function call to add interface info. The interface info is defined in a GInterfaceInfo struct. We just make use of the interface_init() callback. In it, we assign function pointers of corresponding interface to our implementation function. We can add multiple interface infos to implement them.

Finally, the test code:

In runtime, if your classed type implements an interface, it will be considered as the interface type (is-a).

All source code is available in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestGObject-{date}.zip/TestGObject6 folder.

Signals in GObject are used to support a event-driven programming. Signals can be connected to callback handlers. When they are emitted, these handlers are invoked. To add signals to a type, notice following lines of code:

NOTE: PLEASE READ ALL COMMENT CAREFULLY.

Like properties, signals can be inherited too. Use signals like:

The callback functions are defined simply like:

All source code is available in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestGObject-{date}.zip/TestGObject5 folder.