Object Manager concepts

Changes in BazisLib 2.01

A new template class has been added to BazisLib 2.01: ConstManagedPointer. References to it should be used as function/template parameters to optimize performance. See its description for details.

General considerations and definitions

An object that supports reference counting is called a managed object. Every class that represents a managed object is a direct or inderect descendant of the ManagedObjectBase class. A managed object cannot be allocated in stack or statically. The only correct way to allocate such objects is to use the new() operator. The debug build of the framework checks the newly created objects whether they were allocated from heap, however, the release build does not. All managed objects are organized in a tree-like structure. Practically, an object that wants to store the pointer to another object, should become a client for that object. An object, that may have clients is called a service and should be a direct or indirect descendant of the ServiceObject class. A root service that is not a client of anyone, should be a child (directly) of the ServiceObject class. The object manager ensures that a service is not deleted until all its clients are freed. A client may be a service at the same time. In the simpliest case, such object can inherit the IntermediateServiceObject class and use the IntermediateServiceObject::GetParentService() method to get its service. However, if an object (no matter, whether it is a service or not) wants to be a client of a multiple services, it should use managed field pointers. Mind that you should avoid circular references when designing your classes. This means, that a service cannot become a client for any of its clients. The debug build of the framework detects such problems.

Managed field pointers

If you want to store a pointer to a service inside a client, you should never do it directly. Instead, use the following scheme: To illustrate the rules by an example, let's assume that we have two service classes: a Disk and a File. A File is a client for a Disk. Also, a Disk object is deleted after all file objects are freed. Let's see how this example is implemented:
class Disk;

class File
{
private:
	DECLARE_REFERENCE(Disk, m_pDisk);
	int m_SomeVariable;

public:
	File(Disk *pDisk) :
	INIT_REFERENCE(m_pDisk, pDisk),
	m_SomeVariable(SomeValue)
	{
	}

	File(const File &file) :
	COPY_REFERENCE(m_pDisk, file)
	m_SomeVariable(SomeValue)
	{
	}
};
			

Note that we declared a copy constructor. It is required only if you want to enable copying of your objects. If not, it is not required and an attempt to perform such copying will result in a compilation error. In case you really need it, simply use the COPY_REFERENCE() macro to ensure that all pointers are updated correctly.

Remarks:
The implementation of such managed pointers is very simple. On creation, it registers its owner as a client (by calling ServiceObject::RegisterClient()) and on deletion (or when its value is changed), it calls ServiceObject::DeregisterClient(). This is smarter than incrementing/decrementing the reference counter, as it provides additional features, such as broadcasting.

Generic managed pointers

If you want to declare a managed pointer that will not be a field of a managed object, you should use the ObjectManager::ManagedPointer class. It ensures that the reference counter is decreased automatically after the pointer is deleted. It also supports copying.

Using unmanaged pointers

An unmanaged pointer is a native C++ pointer (like SomeObject *pObj). Note that if some method returns you an unmanaged pointer, you are responsible for releasing it by calling ManagedObjectBase::Release() method. However, when you initialize any type of managed pointer using an unmanaged one, it takes the ownership of the object (it does not retain the object another time, however, it releases it on deletion). Note that the only way to get an unmanaged pointer to an object is to call the ManagedPointer::RetainAndGet() method that returns a previously retained unmanaged pointer. The described scheme implies a set of simple rules to follow: A sample illustrating object manager usage is located under SAMPLES\COMMON\OBJMGR, while an autotest program is in TESTS\OBJMGR.

Executing commands

There are some cases that can require a service to tell its clients some information. For example, a disk object that detected device removal may want to notify the file objects about this event. Moreover, this is the only way for a service to interact with its clients. To send a message to all the clients of a service, you should call the ServiceObject::CallClients() method. It notifies every client of the service by calling ManagedObjectBase::ExecuteCommand() method. If one of the clients returns an error code, the method does not call other clients and return that code. On successful termination, it returns a value of CommonErrorType::Success.
Remarks:
Note that recursive child calls are not supported to prevent command code collisions. Normally, a set of command codes that can be sent by a service to its clients is defined by the type of this service. That way, a client that connects to a service should be aware of handling the codes provided by that service. If the IgnoreNotSupported parameter is set, the clients returning NotSupported value will not cause the call to fail, if not - they will.

Auto interfaces

An auto interface is an interface for a managed object. Actually, it is an interface that is virtually inherited from the ServiceObject class. Virtual inheritance ensures that an object implementing multiple managed interfaces contains a single instance of the ServiceObject class. See AUTO_INTERFACE and AUTO_OBJECT documentation for usage examples. Note that if you want to declare two equivalent interfaces, one managed and one unmanaged, use the MAKE_AUTO_INTERFACE macro. To declare a pair of classes (one for managed and one for unmanaged interface), use the following scheme:
template <class _Parent> class _Cxxx : public _Parent
{
	...
};

typedef _Cxxx<Ixxx> UnmanagedXxx;
typedef _Cxxx<AIxxx> ManagedXxx;
		

Key points to remember

SourceForge.net Logo