Kernel SDK Developer's Guide > Copying Rx Objects > Redefining the Clone Method of Rx Objects
Redefining the Clone Method of Rx Objects

Redefining the Clone Method of Rx Objects

The default implementation of the clone() method is acceptable for the majority of classes. However in some cases, the default implementation can give an undesirable result, for example, when the object stores references to other objects. In this case, the default implementation copies only references and does not copy the objects being referenced. To clone such an object together with referenced objects, a developer needs to redefine the clone() method in the derived class.

For example, consider the reference object that binds two other objects: the xSomeRect class and the xSomeText class that were developed in the Cloning of Rx objects topic. The reference object is defined by the smart pointer to the first object (area or text) and the smart pointer to the second object (area or text). The task requires cloning of the reference object.

When a developer uses the clone() method in the default implementation, the method creates a new instance of the reference object and initializes it using the copyFrom() method. It is logical when the copyFrom() method is defined as the method that copies only content of the reference instance, but in this example the content of the reference object is two smart pointers. As a result, the default clone() method creates a new reference object that contains the duplicates of two smart pointers, and the area or text objects being referenced are not cloned. To clone the reference objects together with objects being referenced, the developer should redefine the clone() method in its own class. The redefined clone() method must create a new instance of the reference object and new instances of the first and second objects that are referenced. Then redefined clone() method must copy the content of each instance being referenced and bind these new instances within the reference object. The following diagram demonstrates this difference between cloning.

The example implements two classes of the reference object. The first class, named xSomeRefObj, must provide the functionality of the reference object as provided by the default implementation of the clone() method (left diagram). The functionality of the first class must include binding of two objects, getting the raw pointer to the first object or second object, and copying the content, that is, the smart pointer values. The second class, named xSomeRefCls, must provide the functionality of the reference object that copies the references together with the cloning of objects being referenced (right diagram).

The example derives the xSomeRefObj class directly from the OdRxObject class and derives the xSomeRefCls class from the xSomeRefObj class. The xSomeRefCls class inherits the functionality of the reference object and redefines its clone() method. To declare the standard RTTI methods, the example uses the ODRX_DECLARE_MEMBERS macro. To implement the standard RTTI methods, the example uses the ODRX_CONS_DEFINE_MEMBERS macro.

Step 1. Implementing the xSomeRefObj class

The xSomeRefObj class defines the references to the first and second objects as non-typified smart pointers. To manipulate objects, this class declares the BindTwoObjects() method that sets the first and second smart pointers to the specified objects, the getObject1() method that returns the raw pointer to the first object, the getObject2() method that returns the raw pointer to the second object, and the copyFrom() method that copies the smart pointers from one reference object to another reference object. The xSomeRefObj class has the following definition:

class xSomeRefObj : public OdRxObjectImpl<OdRxObject>
{
 private:
   OdRxObjectPtr pObj1;
   OdRxObjectPtr pObj2;

 public:
   ODRX_DECLARE_MEMBERS(xSomeRefObj);
   void copyFrom(const OdRxObject* pSource);

   const OdRxObject* getObject1() const;
   const OdRxObject* getObject2() const;
   void BindTwoObjects(const OdRxObject* pToFirst, const OdRxObject* pToSecond);

   xSomeRefObj();
   ~xSomeRefObj();
};

typedef OdSmartPtr<xSomeRefObj> xSomeRefObjPtr;

Its methods have the following implementation:

ODRX_CONS_DEFINE_MEMBERS(xSomeRefObj, OdRxObject, NEWOBJ_CONSTR);

xSomeRefObj::xSomeRefObj() {}

xSomeRefObj::~xSomeRefObj() {}

const OdRxObject* xSomeRefObj::getObject1() const  {  return pObj1.get();  }

const OdRxObject* xSomeRefObj::getObject2() const  {  return pObj2.get();  }

void xSomeRefObj::BindTwoObjects(const OdRxObject* pToFirst, const OdRxObject* pToSecond)
{
  pObj1 = pToFirst;
  pObj2 = pToSecond;
}

void xSomeRefObj::copyFrom(const OdRxObject* pSource)
{
  if(pSource->isA() == this->isA())
  {
    xSomeRefObj* pRefObj = (xSomeRefObj*) pSource;
    this->BindTwoObjects(pRefObj->getObject1(), pRefObj->getObject2());
  }
  else throw OdError(eNotApplicable);
}

Step 2. Implementing the xSomeRefCls class

The xSomeRefCls class is derived from the xSomeRefObj class and inherits its functionality. The xSomeRefCls class only redefines the clone() method. The clone() method creates a new instance of the reference objects using its pseudo-constructor, creates a new instance of the first object using its pseudo-constructor and the getObject1() method, creates a new instance of the second object using its pseudo-constructor and the getObject2() method, copies the content of the first and second objects using their copyFrom() methods, and binds these new instances in the reference object using the BindTwoObjects() method. The xSomeRefCls class has the following definition:

class xSomeRefCls : public xSomeRefObj
{
 public:
   ODRX_DECLARE_MEMBERS(xSomeRefCls);
   OdRxObjectPtr clone() const;

   xSomeRefCls();
   ~xSomeRefCls();
};

typedef OdSmartPtr<xSomeRefCls> xSomeRefClsPtr;

Its methods have the following implementation:

DRX_CONS_DEFINE_MEMBERS(xSomeRefCls, xSomeRefObj, NEWOBJ_CONSTR);

xSomeRefCls::xSomeRefCls() : xSomeRefObj() {}

xSomeRefCls::~xSomeRefCls() {}

OdRxObjectPtr xSomeRefCls::clone() const
{
  xSomeRefClsPtr pNewObj = this->isA()->create();
  OdRxObjectPtr pNewObj1 = this->getObject1()->isA()->create();
  OdRxObjectPtr pNewObj2 = this->getObject2()->isA()->create();

  pNewObj1->copyFrom(this->getObject1());
  pNewObj2->copyFrom(this->getObject2());

  pNewObj->BindTwoObjects(pNewObj1, pNewObj2);

  return (OdRxObjectPtr) pNewObj;
}

Step 3. Implementing the testing functions

For testing, the example implements the main() function that creates the source instance of the xSomeRect class, creates the source instance of the xSomeText class, and initializes them. This function declares the smart pointer to the xSomeRefObj class, creates the instance of this class, and binds it with the created source instances (area and text). Then, the main() function clones the instance of the xSomeRefObj object using the default clone() method. Analogically, the main() function declares the smart pointer to the xSomeRefCls class, creates the instance of this class, and binds it with the created source instances (area and text). Then, the main() function clones the instance of the xSomeRefCls class using the redefined clone() method. The example also implements the PrintAboutObject() function that prints the information about instances of the xSomeRefCls, xSomeRefObj, xSomeRect, and aSomeText classes using the RTTI data about these classes. The Main module has the following implementation:

#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() {};
};

void PrintAboutObject(const OdRxObject*  pObj)
{
  if((pObj->isA()->name().compare(L"xSomeRefObj") == 0) || 
     (pObj->isA()->name().compare(L"xSomeRefCls") == 0))
  {
    xSomeRefObj* pRefObj = (xSomeRefObj*) pObj;
    odPrintConsoleString(L"\n%s  [%x]  (%x) -- (%x)", pRefObj->isA()->name().c_str(), pRefObj, pRefObj->getObject1(), pRefObj->getObject2());
    PrintAboutObject(pRefObj->getObject1());
    PrintAboutObject(pRefObj->getObject2());
  }
  else if(pObj->isA()->name().compare(L"xSomeRect") == 0)
  {
    xSomeRect* pRect = (xSomeRect*) pObj;
    odPrintConsoleString(L"\nRect [%x] (%g,%g) - (%g,%g)", pRect, pRect->getLeft(), pRect->getLower(), pRect->getRight(), pRect->getUpper());
  }
  else if(pObj->isA()->name().compare(L"xSomeText") == 0)
  {
    xSomeText* pText = (xSomeText*) pObj;
    odPrintConsoleString(L"\nText [%x] (%g,%g) \"%s\"", pText, pText->getX(), pText->getY(), pText->getText());
  }
}

#include <conio.h>

int main()
{
  OdStaticRxObject<MyApp> svcs;

  odInitialize(&svcs);
  xSomeRect::rxInit();
  xSomeText::rxInit();
  xSomeRefObj::rxInit();
  xSomeRefCls::rxInit();

  /* Creating the source instances. */

  xSomeRect rect;
  rect.setRect(1.0, 1.0, 4.0, 3.0);
  odPrintConsoleString(L"\nOriginal Rect [%x] (%g,%g) - (%g,%g)", &rect, rect.getLeft(), rect.getLower(), rect.getRight(), rect.getUpper());
  getch();

  xSomeText text;
  text.setText(2.5, 1.5, OD_T("Info"));
  odPrintConsoleString(L"\nOriginal Text [%x] (%g,%g) \"%s\"", &text, text.getX(), text.getY(), text.getText());
  getch();

  /* Creating the simple reference instances. */

  xSomeRefObjPtr pRefObj = xSomeRefObj::createObject();
  pRefObj->BindTwoObjects(&rect, &text);

  odPrintConsoleString(L"\n\nThe original reference objects:");
  PrintAboutObject(pRefObj);
  getch();

  /* Cloning the simple reference instances. */

  xSomeRefObjPtr pRefObjClone = pRefObj->clone();

  odPrintConsoleString(L"\n\nThe cloned reference objects:");
  PrintAboutObject(pRefObjClone);
  getch();

  /* Creating the reference instances. */

  xSomeRefClsPtr pRefCls = xSomeRefCls::createObject();
  pRefCls->BindTwoObjects(&text, &rect);

  odPrintConsoleString(L"\n\nThe original reference objects:");
  PrintAboutObject(pRefCls);
  getch();

  /* Cloning the reference instances with bound instances. */

  xSomeRefClsPtr pRefClsClone = pRefCls->clone();

  odPrintConsoleString(L"\n\nThe cloned reference objects:");
  PrintAboutObject(pRefClsClone);
  getch();
	
  xSomeRefCls::rxUninit();
  xSomeRefObj::rxUninit();
  xSomeText::rxUninit();
  xSomeRect::rxUninit();
  odUninitialize();

  return 0;
}

The result of cloning for these instances is the following:

Original Rect [12feb8] (1,1) - (4,3)
Original Text [12fe98] (2.5,1.5) "Info"

The original reference objects:
xSomeRefObj  [135a28]  (12feb8) -- (12fe98)
Rect [12feb8] (1,1) - (4,3)
Text [12fe98] (2.5,1.5) "Info"

The cloned reference objects:
xSomeRefObj  [13a6e1]  (12feb8) -- (12fe98)
Rect [12feb8] (1,1) - (4,3)
Text [12fe98] (2.5,1.5) "Info"

The original reference objects:
xSomeRefCls  [14a5b4]  (12fe98) -- (12feb8)
Text [12fe98] (2.5,1.5) "Info"
Rect [12feb8] (1,1) - (4,3)

The cloned reference objects:
xSomeRefCls  [14d7b6]  (e2b470) -- (e35abc)
Text [e2b470] (2.5,1.5) "Info"
Rect [e35abc] (1,1) - (4,3)

The cloned xSomeRefObj object contains the same addresses of the original area and text objects, that is, this object clones only smart pointers to the original objects. The cloned xSomeRefCls object contains the addresses that are different from the original area and text objects; the content of the new area and new text objects coincide with the original area and text objects, that is, this object clones the linked original objects together with the reference object.

See Also

Copying Rx Objects

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