Kernel SDK Developer's Guide > Copying Rx Objects > Cloning Rx Objects
Cloning Rx Objects

The OdRxObject class declares the clone() virtual method that clones a specified instance. The clone() method creates a new dynamic instance of the same class that is a duplicate of the specified instance and returns the non-typified smart pointer to the duplicate. The specified instance can be both static and dynamic. The default implementation of the clone() method calls the pseudo-constructor of the class to create a new instance and calls the copyFrom() method to initialize the new instance. Developers can redefine the clone() method in their own derived classes.

For example, consider two classes: a rectangular area and a text label. The area object is defined by two-dimensional coordinates of two opposite corners, and the label object is defined by a text string and two-dimensional coordinates of the text position. To define a string, the label object uses the OdChar type and the OD_T macro. The example first implements the xSomeRect class that provides the functionality of rectangular areas which must include creating an area, setting a position, and getting the left, right, lower, or upper scopes. Then the example implements the xSomeText class that provides the functionality of text labels that must include creating a text string, setting a position, and getting the X-coordinate or Y-coordinate. Last, the example implements the main() function that creates two static instances of each class, initializes them, and clones each of them using the clone() method. Then the main() function clones the duplicates of each instances.

The example derives the xSomeRect and xSomeText classes directly from the OdRxObject class. 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. The xSomeRect class and xSomeText class must redefine the copyFrom() method. The example implements the copyFrom() method as a method that copies an instance of the same class or throws an exception when classes of instances are different.

Step 1. Implementing the xSomeRect class

The xSomeRect class defines the left, right, lower, and upper coordinates as double values. To manipulate areas, this class declares the setRect() method that specifies the position, the getLeft() method that returns the X-coordinate of the left scope, the getRight() method that returns the X-coordinate of the right scope, the getLower() method that returns the Y-coordinate of the lower scope, the getUpper() method that returns the Y-coordinate of the upper scope, and redefines the copyFrom() method that copies the coordinates from one area object to other area object. The xSomeRect class has the following definition:

class xSomeRect : public OdRxObjectImpl<OdRxObject>
{
 private:
   double left;
   double right;
   double lower;
   double upper;

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

   double getLeft() const;
   double getRight() const;
   double getLower() const;
   double getUpper() const;
   void setRect(double newLeft, double newLower, double newRight, double newUpper);

   xSomeRect();
   ~xSomeRect();
};

typedef OdSmartPtr<xSomeRect> xSomeRectPtr;

Its methods have the following implementation:

ODRX_CONS_DEFINE_MEMBERS(xSomeRect, OdRxObject, NEWOBJ_CONSTR);

xSomeRect::xSomeRect()  
{
  left = lower = 0.0;   right = upper = 1.0;
}

xSomeRect::~xSomeRect() {}

double xSomeRect::getLeft() const  {  return left;  }

double xSomeRect::getRight() const  {  return right;  }

double xSomeRect::getLower() const  {  return lower;  }

double xSomeRect::getUpper() const  {  return upper;  }

void xSomeRect::setRect(double newLeft, double newLower, double newRight, double newUpper)
{
  if(newLeft > newRight) 
  {
    left = newRight;
    right = newLeft;
  }
  else
  {
    left = newLeft;
    right = newRight;
  }
  if(newLower > newUpper) 
  {
    lower = newUpper;
    upper = newLower;
  }
  else
  {
    lower = newLower;
    upper = newUpper;
  }
}

void xSomeRect::copyFrom(const OdRxObject* pSource)
{
  if(pSource->isA() == this->isA())
  {
    xSomeRect* pRect = (xSomeRect*) pSource;
    this->setRect(pRect->getLeft(), pRect->getLower(), pRect->getRight(), pRect->getUpper());
  }
  else throw OdError(eNotApplicable);
}

Step 2. Implementing the xSomeText class

The xSomeText class defines the coordinates of the position as double values and the text content as the OdChar string. To manipulate text labels, this class declares the setText() method that specifies the text position and text content, the getX() method that returns the current X-coordinate, the getY() method that returns the current Y-coordinate, the getText() method that returns the text content, and the copyFrom() method that copies the coordinates and text content from one text object to other text object. The xSomeText class has the following definition:

class xSomeText : public OdRxObjectImpl<OdRxObject>
{
 private:
   double pos_x;
   double pos_y;
   OdString text;

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

   double getX() const;
   double getY() const;
   const OdChar* getText() const;
   void setText(double newX, double newY, const OdChar* newText);

   xSomeText();
   ~xSomeText();
};

typedef OdSmartPtr<xSomeText> xSomeTextPtr;

Its methods have the following implementation:

ODRX_CONS_DEFINE_MEMBERS(xSomeText, OdRxObject, NEWOBJ_CONSTR);

xSomeText::xSomeText()  
{
  pos_x = pos_y = 0.0;
  text = OD_T(" ");
}

xSomeText::~xSomeText() {}

double xSomeText::getX() const  {  return pos_x;  }

double xSomeText::getY() const  {  return pos_y;  }

const OdChar* xSomeText::getText() const  {  return text.c_str();  }

void xSomeText::setText(double newX, double newY, const OdChar* newText)
{
  pos_x = newX;
  pos_y = newY;
  text = newText;
}

void xSomeText::copyFrom(const OdRxObject* pSource)
{
  if(pSource->isA() == this->isA())
  {
    xSomeText* pText = (xSomeText*) pSource;
    this->setText(pText->getX(), pText->getY(), pText->getText());
  }
  else throw OdError(eNotApplicable);
}

Step 3. Implementing the testing functions

For testing these classes, the main() function creates the source area instance, source text instance, and initializes them. Both instances are static. Then the main() function declares the smart pointers and calls the clone() method first for static instances of each class, and then for dynamic cloned instances of each class. 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() {};
};

#include <conio.h>

int main()
{
  OdStaticRxObject<MyApp> svcs;

  odInitialize(&svcs);
  xSomeRect::rxInit();
  xSomeText::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();

  /* Cloning the static instances. The cloned instance is a dynamic. */

  xSomeRectPtr pRectClone = rect.clone();
  odPrintConsoleString(L"\nCloned Rect [%x] (%g,%g) - (%g,%g)", pRectClone.get(), pRectClone->getLeft(), pRectClone->getLower(), pRectClone->getRight(), pRectClone->getUpper());
  getch();

  xSomeTextPtr pTextClone = text.clone();
  odPrintConsoleString(L"\nCloned Text [%x] (%g,%g) \"%s\"", pTextClone.get(), pTextClone->getX(), pTextClone->getY(), pTextClone->getText());
  getch();

  /* Cloning the dynamic instances. The cloned instance is a dynamic. */

  xSomeRectPtr pRect = pRectClone->clone();
  odPrintConsoleString(L"\nCloned Rect [%x] (%g,%g) - (%g,%g)", pRect.get(), pRect->getLeft(), pRect->getLower(), pRect->getRight(), pRect->getUpper());
  getch();

  xSomeTextPtr pText = pTextClone->clone();
  odPrintConsoleString(L"\nCloned Text [%x] (%g,%g) \"%s\"", pText.get(), pText->getX(), pText->getY(), pText->getText());
  getch();
	
  xSomeText::rxUninit();
  xSomeRect::rxUninit();
  odUninitialize();

  return 0;
}

The result of the cloning for these instances is the following:

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

Cloned Rect [13f6e0] (1,1) - (4,3)
Cloned Text [13b470] (2.5,1.5) "Info"

Cloned Rect [14a9e8] (1,1) - (4,3)
Cloned Text [14ad20] (2.5,1.5) "Info"

The test function has obtained a different instance (instances have different addresses) with the same content (instances have the same coordinates and text). If the source instance is static, the cloned instance is dynamic. If the source instance is dynamic, the cloned instance is also dynamic.

See Also

Copying Rx Objects

Redefining the Clone Method of Rx Objects

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