Kernel SDK Developer's Guide > Run-Time Type Identification > Belonging to an Instance of a Class
Belonging to an Instance of a Class

The OdRxObject class declares the queryX(), isKindOf(), and x() methods that allow a developer to check whether the instance belongs to the specified class or a subclass of the specified class. These methods require a raw pointer to the class describing instance as an argument; they differ by behavior and returned result. Developers can use these methods for verifying the admissibility of converting pointers to the specified instance, applying methods and functions, or selecting data processing subject to the class type of the specified instance.

The queryX() method is the virtual method that returns an address of the instance when the instance is the object derived from or belonging to the specified class, or returns NULL otherwise. The ODRX_DEFINE_RTTI_MEMBERS(ClassName,ParentClass) macro automatically creates the implementation of the queryX() method for each class that uses it. This macro requires the class name and parent class name, and creates the following implementation of the queryX() method:

OdRxObject* ClassName::queryX(const OdRxClass* pClass) const
{ 
  return ::odQueryXImpl<ClassName,ParentClass>(this, pClass); 
}

The odQueryXImpl() global function is the template that automatically creates the implementation of verifying a specified class and parent class. The template of the odQueryXImpl() function has the following implementation:

template <class Class, class Parent>
OdRxObject* odQueryXImpl(const Class* pThis, const OdRxClass* pClass)
{
  ODA_ASSERT(pClass!=0);
  OdRxObject* pObj = 0;

  if(pClass == Class::desc())
  {
    pObj = (OdRxObject*) pThis;
    pObj->addRef();
  }
  else
  {
    pObj = Class::desc()->getX(pClass).detach();
    if(!pObj)
      pObj = pThis->Parent::queryX(pClass);
  }
  return pObj;
}

The odQueryXImpl() function recursively defines the class to which the current class belongs through the queryX() method of the parent class. A developer does not need to redefine the queryX() method. A developer can use the result as a pointer for converting to the specified class or verifying the class.

The isKindOf() method is the non-virtual method that returns True when an instance is the object derived from or belonging to the specified class, or returns False otherwise. Classes derived from the OdRxObject classes inherit this method; a developer can not redefine it. The isKindOf() method uses the queryX() method to check whether the instance belongs to the specified class and returns False when the queryX() method returns NULL. The method creates the non-typified smart pointer and tries to attach to it the instance returned by the queryX() method. If the queryX() method returns an address of the instance, attachment is successfully and the isKindOf() method returns True. The isKindOf() method has the following implementation:

bool isKindOf(const OdRxClass* pClass) const
{
  OdRxObjectPtr pRes;
  pRes.attach(queryX(pClass));
  return (!pRes.isNull());
}

The x() method is the virtual method that returns an address of the instance when the instance is the object derived from or belonging to the specified class, or the method generates the eNotThatKindOfClass exception otherwise. A developer can catch this exception in the program using the try – catch statement when it calls this method. The x() method uses the queryX() method to check whether it belongs and generates the exception when the queryX() method returns NULL. The x() method has the following implementation:

OdRxObject* OdRxObject::x(const OdRxClass* pClass) const
{
  OdRxObject* pRes = 0;
  if(pClass)
  {
    pRes = queryX(pClass);
    if(!pRes) throw OdError_NotThatKindOfClass(isA(), pClass);
  }
  else throw OdError(eInvalidInput);
  return pRes;
}

The OdRxClass class declares the isDerivedFrom() method that also allows developers to check whether a class belongs to a specified class or its subclass. This method also requires a raw pointer to the class describing instance as an argument. The method returns True when the class is derived from or belonging to the specified class, or it returns False otherwise. The isDerivedFrom() method analyzes the hierarchical structure of the class describing instances using pointers to the parent classes. This method works with class describing instances and can be used after registering classes. The isDerivedFrom() method has the following implementation:

bool OdRxClass::isDerivedFrom(const OdRxClass* pClass) const
{
  const OdRxClass* pCurr = this;
  while(pCurr)
  {
    if(pCurr == pClass) return true;
    pCurr = pCurr->myParent();
  }
  return false;
}

Note: To use the queryX(), isKindOf(), x(), isDerivedFrom() methods, you must perform the register procedure first. Only registered classes can be checked using these methods.

For example, consider three classes: ClsX, ClsY, ClsZ. The ClsX class is derived from the OdRxObject class. The ClsY class is derived from the ClsX class. The ClsZ class is derived from the ClsX class. The example has the following code:

#include "OdaCommon.h"
#include "OdToolKit.h"
#include "..\..\Teigha.dwg\Extensions\ExServices\ExHostAppServices.h"
#include "..\..\Teigha.dwg\Extensions\ExServices\ExSystemServices.h"

class MyApp : public ExSystemServices 
{
 protected:
   ODRX_USING_HEAP_OPERATORS(ExSystemServices);
 public:
   MyApp() {};
};

class ClsX : public OdRxObjectImpl<OdRxObject>
{
 public:
   ODRX_DECLARE_MEMBERS(ClsX);
   ClsX() {};
};

ODRX_CONS_DEFINE_MEMBERS(ClsX, OdRxObject, NEWOBJ_CONSTR);

class ClsY : public ClsX
{
 public:
   ODRX_DECLARE_MEMBERS(ClsY);
   ClsY() {};
};

ODRX_CONS_DEFINE_MEMBERS(ClsY, ClsX, NEWOBJ_CONSTR);

class ClsZ : public ClsX
{
 public:
   ODRX_DECLARE_MEMBERS(ClsZ);
   ClsZ() {};
};

ODRX_CONS_DEFINE_MEMBERS(ClsZ, ClsX, NEWOBJ_CONSTR);

int main()
{
  OdStaticRxObject<MyApp> svcs;
  odInitialize(&svcs);
  ClsX::rxInit();
  ClsY::rxInit();
  ClsZ::rxInit();

  odPrintConsoleString(L"\nIs ClsY derived from ClsX => %s", ((ClsY::desc()->isDerivedFrom(ClsX::desc())) ? L"true" : L"false") );
  odPrintConsoleString(L"\nIs ClsZ derived from ClsX => %s", ((ClsZ::desc()->isDerivedFrom(ClsX::desc())) ? L"true" : L"false") );
  odPrintConsoleString(L"\nIs ClsZ derived from ClsY => %s", ((ClsZ::desc()->isDerivedFrom(ClsY::desc())) ? L"true" : L"false") );
  odPrintConsoleString(L"\nIs ClsX derived from ClsY => %s", ((ClsX::desc()->isDerivedFrom(ClsY::desc())) ? L"true" : L"false") );

  OdRxObjectPtr pClsX = ClsX::createObject();
  OdRxObjectPtr pClsY = ClsY::createObject();
  OdRxObjectPtr pClsZ = ClsZ::createObject();

  odPrintConsoleString(L"\nQuery (ClsY => ClsX) = %x", pClsY->queryX(ClsX::desc()) );
  odPrintConsoleString(L"\nQuery (ClsZ => ClsX) = %x", pClsZ->queryX(ClsX::desc()) );
  odPrintConsoleString(L"\nQuery (ClsZ => ClsY) = %x", pClsZ->queryX(ClsY::desc()) );
  odPrintConsoleString(L"\nQuery (ClsX => ClsY) = %x", pClsX->queryX(ClsY::desc()) );

  odPrintConsoleString(L"\nIs ClsY %s to ClsX", ((pClsY->isKindOf(ClsX::desc())) ? L"belongs" : L"does not belong") );
  odPrintConsoleString(L"\nIs ClsZ %s to ClsX", ((pClsZ->isKindOf(ClsX::desc())) ? L"belongs" : L"does not belong") );
  odPrintConsoleString(L"\nIs ClsZ %s to ClsY", ((pClsZ->isKindOf(ClsY::desc())) ? L"belongs" : L"does not belong") );
  odPrintConsoleString(L"\nIs ClsX %s to ClsY", ((pClsX->isKindOf(ClsY::desc())) ? L"belongs" : L"does not belong") );

  try {
    odPrintConsoleString(L"\nIs ClsY kind of ClsX => %s", ((pClsY->x(ClsX::desc()) != NULL) ? L"true" : L"false") );
  }
  catch(OdError_NotThatKindOfClass err) 
  {
    odPrintConsoleString(L"\nException %d - %s", err.code(), err.description().c_str());
  }
  try {
    odPrintConsoleString(L"\nIs ClsZ kind of ClsY => %s", ((pClsZ->x(ClsY::desc()) != NULL) ? L"true" : L"false") );
  }
  catch(OdError_NotThatKindOfClass err)
  {
    odPrintConsoleString(L"\nException %d - %s", err.code(), err.description().c_str());
  }

  ClsZ::rxUninit();
  ClsY::rxUninit();
  ClsX::rxUninit();
  odUninitialize();
  return 0;
}

Checking the ClsX, ClsY, ClsZ classes gives the following result:

Is ClsY derived from ClsX => true
Is ClsZ derived from ClsX => true
Is ClsZ derived from ClsY => false
Is ClsX derived from ClsY => false

Query (ClsY => ClsX) = 135a3d0
Query (ClsZ => ClsX) = 135a3e0
Query (ClsZ => ClsY) = 0
Query (ClsX => ClsY) = 0

ClsY belongs to ClsX
ClsZ belongs to ClsX
ClsZ does not belong to ClsY
ClsX does not belong to ClsY

Is ClsY kind of ClsX => true
Exception 63 - Object of class ClsZ can't be cast to ClsY.

See Also

RTTI Technology

Implementing RTTI Methods for the Derived Class

Registering and Unregistering Classes in a Program

Example of Belonging to an Instance of a Class

Copyright © 2002 – 2020. Open Design Alliance. All rights reserved.