Drawings SDK Developer Guide > Working with .dwg Files > Working with Databases > Identifying the Objects of Databases > Identification using a Pointer
Identification using a Pointer

A pointer addresses an object at run-time that is located in memory. A pointer can manipulate an object and has access to its properties during the current run-time session.

Pointers exist only within the scope of a program. When a program or procedure is finished, its pointers are destroyed. When a pointer is destroyed, it loses its object. To work with a pointer for an object, the object must be obtained from the database and the database must be loaded in memory. However, multiple procedures can require access to a single object. As a result, a program must share an object and must control access to it. Additionally, a program must control the use of memory reserved for an object. The C++ platform uses smart pointer technology, reference counting, and shared access for objects.

The OdDbObjectPtr class implements a pointer to an object of a database. The OdDbObjectPtr class inherits the standard functionality of the smart pointer and changes the reference counter of the object. When a smart pointer obtains an address of an object, it increases the reference counter of the obtained object and decreases the reference counter of the object to which it referred before. When the reference counter becomes zero, the object is automatically released.

When a program or its procedure needs to work with an object, it must obtain the access and pointer to it. A program can obtain a pointer to an object only if the database is loaded in memory. Additionally, a program can use multiple databases loaded through Xreferences, and the required object can be placed in databases attached to the general database. This requires redirecting between databases. A program accesses an object through the ID associated with the object. When an object is placed in the attached database, its ID is redirected to the original database. Obtaining an object uses two parameters — the open mode and erase status — to get access to an object.

Open mode

The open mode defines the access to an object and can be for reading, writing, or notifying. The OdDb::OpenMode enumerator defines how a database resident object must be opened using the following constants:

  • kForRead (=0) — An object can be opened for reading (up to 256 times). Multiple procedures can open an object for reading any number of times and can read its properties simultaneously.
  • kForWrite (=1) — An object can be opened for reading and writing. Multiple procedures can open an object for writing any number of times and it can modify the object's properties.
  • kForNotify (=2) — An object can be opened for notifying when the object is closed, opened for reading, or opened for writing, but not when it is already opened for notifying. When an event occurs in the system, certain objects, name the notifier, automatically relay the event to other objects. For example, when a user copies, erases, or modifies an object, a corresponding notification for each event is automatically triggered. Other objects receive the events, named reactors, and they declare and use various notification functions. When an event occurs, the notifier automatically invokes the corresponding notification function of each reactor in its reactor list.
  • kNotOpen (–1) — An object is not opened.

The first call into an override function or a method of a derived class usually uses the assertReadEnabled(), assertWriteEnabled(), or assertNotifyEnabled() functions to verify whether an object is opened in the correct mode. The following table shows the possible states for opening an object (read, write, notify) and indicates which assert calls succeed for each state:

Erase status

When an object is erased from the database, the erase operation can do one of the following:

  • Erase the object permanently from the database — The object is destroyed and its memory is freed. As a result, the object information is not reintroduced and the object is deleted.
  • Mark the object and keep it in the database — The object is assigned the "erased" state and becomes inaccessible in the database, but the database continues storing the object. As a result, the object information can be recovered during the current run-time session. When the program recovers an object, the object is assigned the "unerased" state and becomes accessible in the database, just as before it was erased.

By default, functions and methods cannot obtain an erased object in ordinary modes, even if it is stored in the database. The behavior of the erase operation depends on the object type and undo procedure of the database. During database undo procedures, the erase operation marks the record objects and entities, but deletes the objects from dictionaries. When the database is not used with undo procedures, the erase operation deletes all objects. Erased objects are not filed out to DWG or DXF files.

The erase status defines whether the access influences only an existing object or on an erased object too. The database defines the erase status as a Boolean value that is False when a method has access only to an existing object or True when a method has access to both an unerased object and an erased object. All objects of a database have the erase() method that performs the erase operation and changes the erase status of the object. The erase() method requires the erase status as an argument and erases an object when the argument is True or recovers an object when the argument is False. The following table shows the possible states for erasing an object:

Obtaining an object

The odOpenObject() global function opens the object associated with the specified ID in the specified open mode for the specified erase status, obtains the smart pointer to it, and returns the result code as a value of the OdResult type. When the object is opened successfully, the function returns the eOk code. When the object is permanently erased, the function returns the ePermanentlyErased code. When the object is opened for notifying, the function returns the eWasNotifying code. When the object is opened for undoing, the function returns the eWasOpenForUndo code.

Note: The odOpenObject() function is implemented for internal usage; it should not be used.

The OdDbObjectId class implements the openObject() and safeOpenObject() methods for obtaining an object from a database during run-time. The openObject() method has two implementations; the safeOpenObject() method has one implementation. In the examples below, the pHost variable stores a pointer to the host application object, and the pDb variable stores a pointer to the database object.


OdDbDatabasePtr pDb = pHost->createDatabase();
OdDbObjectId idObject = pDb->getOdDbObjectId(0x1A);

To demonstrate obtaining an object, the general example creates four IDs, a database object, and three objects and adds them into the database. Then the example erases one object permanently and erases another object by marking it. The first ID is set to kNull, the second ID refers to the existing object, the third ID refers to deleted object, and the fourth ID refers to the object marked as "erased". Then the example displays the properties of the ID objects.


OdDbObjectPtr pObject0;
OdDbObjectId idObject0;

OdDbObjectPtr pObject1 = OdDbColor::createObject();
OdDbObjectId idObject1 = pDb->addOdDbObject(pObject1);

OdDbObjectPtr pObject2 = OdDbColor::createObject();
OdDbObjectId idObject2 = pDb->addOdDbObject(pObject2);

OdDbObjectPtr pObject3 = OdDbColor::createObject();
OdDbObjectId idObject3 = pDb->addOdDbObject(pObject3);

pObject2->erase(true);
pDb->startUndoRecord();
pObject3->erase(true);

odPrintConsoleString(L"\n0) pObj=%lx, Handle=%llx, isNull=%d, isErased=%d, isValid=%d", pObject0, 
                     idObject0.getHandle(), idObject0.isNull(), idObject0.isErased(), idObject0.isValid());
odPrintConsoleString(L"\n1) pObj=%lx, Handle=%llx, isNull=%d, isErased=%d, isValid=%d", pObject1, 
                     idObject1.getHandle(), idObject1.isNull(), idObject1.isErased(), idObject1.isValid());
odPrintConsoleString(L"\n2) pObj=%lx, Handle=%llx, isNull=%d, isErased=%d, isValid=%d", pObject2, 
                     idObject2.getHandle(), idObject2.isNull(), idObject2.isErased(), idObject2.isValid());
odPrintConsoleString(L"\n3) pObj=%lx, Handle=%llx, isNull=%d, isErased=%d, isValid=%d", pObject3, 
                     idObject3.getHandle(), idObject3.isNull(), idObject3.isErased(), idObject3.isValid());

The result is the following:


0) pObj=0,      Handle=0,   isNull=1,  isErased=1,  isValid=0
1) pObj=d3ab8,  Handle=40,  isNull=0,  isErased=0,  isValid=1
2) pObj=d3b10,  Handle=41,  isNull=0,  isErased=1,  isValid=0
3) pObj=d3b90,  Handle=42,  isNull=0,  isErased=1,  isValid=1

The first implementation of the openObject() method requires a reference to the smart pointer in which this method saves the pointer to the object associated with the object ID as the first argument, the open mode to be accessed as the second argument, the erase status as the third argument, and returns the result of the opening operation as a value of the OdResult type. The first argument is mandatory and is used for returning a pointer to the obtained object in the calling function. The second argument is optional and is set to kForRead (for reading) by default. The third argument is optional and is set to False (only existing) by default. If an object is opened successfully, the openObject() method returns eOk. In the first implementation, the openObject() method checks whether the specified object ID is valid, can be redirected to another database, and whether the associated object is not erased. When the object ID is kNull, the method returns the eNullObjectId code which corresponds to the exception "21 - Null object Id". When the object ID is redirected, the method converts it to the original database using the convertToRedirectedId() method and obtains the object from it. When the associated object is erased by marking and the third argument is False, the method returns the eWasErased code, which corresponds to the exception "88 - Object was erased". When the associated object is permanently erased and the third argument is True, the method returns the eWasPermanentlyErased code which corresponds to the exception "89 - Object was permanently erased". After checking, the method calls the odOpenObject() global function and passes to it the ID as the first argument, reference to the smart pointer as the second argument, open mode as the third argument, and erase status as the fourth argument. Then, the openObject() method returns the resulting code that is returned by the odOpenObject() function and passes the obtained smart pointer through the second argument in the calling function.

To obtain an object, declare a variable of the OdResult type and declare a smart pointer of the type of the object to be obtained or the OdDbObjectPtr type, call the openObject() method and pass to it the pointer, open mode, and erase status. After calling, check the result code. For example:


OdDbObjectPtr pObject;
OdResult eResult = idObject.openObject(pObject, OdDb::kForRead, false);
if(eResult == eOk)
  odPrintConsoleString(L"\nObject=%lx, Handle=%llx, Type=%s", pObject, 
                       pObject->handle(), pObject->isA()->name().c_str());
else
  odPrintConsoleString(L"\nObject is not opened, error %d occurs.", eResult);

To continue the general example, use the openObject() method in the first implementation and check the resulting code for each situation:


OdResult eRes0, eRes1, eRes2f, eRes3f, eRes2t, eRes3t;
OdDbObjectPtr pObj0, pObj1, pObj2f, pObj3f, pObj2t, pObj3t;

eRes0 = idObject0.openObject(pObj0);
odPrintConsoleString(L"\n0) Obj=%lx, Handle=%llx, Status: %d - %s", pObj0, idObject0.getHandle(), 
                            eRes0, pHost->getErrorDescription(eRes0).c_str());

eRes1 = idObject1.openObject(pObj1, OdDb::kForRead);
odPrintConsoleString(L"\n1) Obj=%lx, Handle=%llx, Status: %d - %s", pObj1, idObject1.getHandle(), 
                            eRes1, pHost->getErrorDescription(eRes1).c_str());

eRes2f = idObject2.openObject(pObj2f, OdDb::kForRead, false);
odPrintConsoleString(L"\n2) Obj=%lx, Handle=%llx, Status: %d - %s", pObj2f, idObject2.getHandle(), 
                            eRes2f, pHost->getErrorDescription(eRes2f).c_str());

eRes3f = idObject3.openObject(pObj3f, OdDb::kForRead, false);
odPrintConsoleString(L"\n3) Obj=%lx, Handle=%llx, Status: %d - %s", pObj3f, idObject3.getHandle(), 
                            eRes3f, pHost->getErrorDescription(eRes3f).c_str());

eRes2t = idObject2.openObject(pObj2t, OdDb::kForRead, true);
odPrintConsoleString(L"\n2) Obj=%lx, Handle=%llx, Status: %d - %s", pObj2t, idObject2.getHandle(), 
                            eRes2t, pHost->getErrorDescription(eRes2t).c_str());

eRes3t = idObject3.openObject(pObj3t, OdDb::kForRead, true);
odPrintConsoleString(L"\n3) Obj=%lx, Handle=%llx, Status: %d - %s", pObj3t, idObject3.getHandle(), 
                            eRes3t, pHost->getErrorDescription(eRes3t).c_str());

The result is the following:


0) Obj=0,      Handle=0,   Status: 21 - Null object Id
1) Obj=d3ab8,  Handle=40,  Status:  0 - No error

2) Obj=0,      Handle=41,  Status: 88 - Object was erased
3) Obj=0,      Handle=42,  Status: 88 - Object was erased

2) Obj=0,      Handle=41,  Status: 89 - Object was permanently erased
3) Obj=d3b90,  Handle=42,  Status:  0 - No error

The second implementation of the openObject() method and the single implementation of the safeOpenObject() method are syntactically identical and have a difference only in behavior. Both methods require the open mode as the first argument, the erase status as the second argument, and both return the smart pointer to the object opened in the specified mode. The first argument is optional and is set to kForRead (for reading) by default. The second argument is optional and is set to False (only existing) by default. When an object cannot be opened in the specified mode, is absent, or is permanently erased, the openObject() method returns NULL, while the safeOpenObject() method generates an exception. Both methods have the identical internal implementation. They declare the variable of the OdDbObjectPtr type for saving the smart pointer to the opened object, call the first implementation of the openObject() method, and pass to it the reference to the smart pointer, open mode, and erase status. After calling, these methods return the obtained smart pointer to an opened object. The safeOpenObject() method additionally never returns NULL and checks whether the result is successful. If the result is not eOk, the method generates the exception and passes the error code with it.

To obtain an object, declare a smart pointer of the required type or the OdDbObjectPtr type, call the openObject() method, and pass to it the open mode and erase status. After calling, check the smart pointer. For example:


OdDbObjectPtr pObject = idObject.openObject(OdDb::kForRead, false);
if(pObject.isNull())
  odPrintConsoleString(L"\nObject is not opened, errors occur.");
else
  odPrintConsoleString(L"\npObject=%lx, handle=%llx, type=%s", pObject, 
                       idObject.getHandle(), pObject->isA()->name().c_str());

To continue the general example, use the openObject() method in the second implementation and display the results:


pObj0 = idObject0.openObject();
odPrintConsoleString(L"\n0) Obj=%lx, Handle=%llx", pObj0, idObject0.getHandle());

pObj1 = idObject1.openObject(OdDb::kForRead);
odPrintConsoleString(L"\n1) Obj=%lx, Handle=%llx", pObj1, idObject1.getHandle());

pObj2f = idObject2.openObject(OdDb::kForRead, false);
odPrintConsoleString(L"\n2) Obj=%lx, Handle=%llx", pObj2f, idObject2.getHandle());

pObj3f = idObject3.openObject(OdDb::kForRead, false);
odPrintConsoleString(L"\n3) Obj=%lx, Handle=%llx", pObj3f, idObject3.getHandle());

pObj2t = idObject2.openObject(OdDb::kForRead, true);
odPrintConsoleString(L"\n2) Obj=%lx, Handle=%llx", pObj2t, idObject2.getHandle());

pObj3t = idObject3.openObject(OdDb::kForRead, true);
odPrintConsoleString(L"\n3) Obj=%lx, Handle=%llx", pObj3t, idObject3.getHandle());

The result is the following:


0) Obj=0,      Handle=0
1) Obj=d3ab8,  Handle=40

2) Obj=0,      Handle=41
3) Obj=0,      Handle=42

2) Obj=0,      Handle=41
3) Obj=d3b90,  Handle=42

To obtain an object, declare a smart pointer of the required type or the OdDbObjectPtr type, call the safeOpenObject() method, pass to it the open mode and erase status, and catch exceptions using the try...catch statement. For example:


OdDbObjectPtr pObject;
try {
  pObject = idObject.safeOpenObject(OdDb::kForWrite, false);

  odPrintConsoleString(L"\nObject=%lx, Handle=%llx, Type=%s", pObject, 
                       pObject->handle(), pObject->isA()->name().c_str());
}
catch(OdError& e) 
{ 
  odPrintConsoleString(L"\nObject is not opened, error: %d - %s", e.code(), e.description());
}

To continue the general example, use the safeOpenObject() method, the try...catch statement, and display the results:


try {
  pObj0 = idObject0.safeOpenObject();
  odPrintConsoleString(L"\n0) Obj=%lx, Handle=%llx, Status: eOk!", pObj0, idObject0.getHandle());
}
catch(OdError& e) { odPrintConsoleString(L"\n0) Obj=%lx, Handle=%llx, Status: %d - %s ", pObj0, 
                                         idObject0.getHandle(), e.code(), e.description().c_str()); }
try {
  pObj1 = idObject1.safeOpenObject(OdDb::kForRead);
  odPrintConsoleString(L"\n1) Obj=%lx, Handle=%llx, Status: eOk!", pObj1, idObject1.getHandle());
}
catch(OdError& e) { odPrintConsoleString(L"\n1) Obj=%lx, Handle=%llx, Status: %d - %s ", pObj1, 
                                         idObject1.getHandle(), e.code(), e.description().c_str()); }
try {
  pObj2f = idObject2.safeOpenObject(OdDb::kForRead, false);
  odPrintConsoleString(L"\n2) Obj=%lx, Handle=%llx, Status: eOk!", pObj2f, idObject2.getHandle());
}
catch(OdError& e) { odPrintConsoleString(L"\n2) Obj=%lx, Handle=%llx, Status: %d - %s ", pObj2f, 
                                         idObject2.getHandle(), e.code(), e.description().c_str()); }
try {
  pObj3f = idObject3.safeOpenObject(OdDb::kForRead, false);
  odPrintConsoleString(L"\n3) Obj=%lx, Handle=%llx, Status: eOk!", pObj3f, idObject3.getHandle());
}
catch(OdError& e) { odPrintConsoleString(L"\n3) Obj=%lx, Handle=%llx, Status: %d - %s ", pObj3f, 
                                         idObject3.getHandle(), e.code(), e.description().c_str()); }
try {
  pObj2t = idObject2.safeOpenObject(OdDb::kForRead, true);
  odPrintConsoleString(L"\n2) Obj=%lx, Handle=%llx, Status: eOk!", pObj2t, idObject2.getHandle());
}
catch(OdError& e) { odPrintConsoleString(L"\n2) Obj=%lx, Handle=%llx, Status: %d - %s ", pObj2t, 
                                         idObject2.getHandle(), e.code(), e.description().c_str()); }
try {
  pObj3t = idObject3.safeOpenObject(OdDb::kForRead, true);
  odPrintConsoleString(L"\n3) Obj=%lx, Handle=%llx, Status: eOk!", pObj3t, idObject3.getHandle());
}
catch(OdError& e) { odPrintConsoleString(L"\n3) Obj=%lx, Handle=%llx, Status: %d - %s ", pObj3t, 
                                         idObject3.getHandle(), e.code(), e.description().c_str()); }

The result is the following:


0) Obj=0,      Handle=0,   Status: 21 - Null object Id
1) Obj=d3ab8,  Handle=40,  Status: eOk!

2) Obj=0,      Handle=41,  Status: 88 - Object was erased
3) Obj=d3b90,  Handle=42,  Status: 88 - Object was erased

2) Obj=0,      Handle=41,  Status: 89 - Object was permanently erased
3) Obj=d3b90,  Handle=42,  Status: eOk!

When an object is opened for reading and a program tries to modify a property, the exception "62 - Not opened for write" occurs. When an object is opened for writing or notifying and the program tries to modify a property, the property is modified. For example:


pColor = NULL;
try {
  pColor = idObject1.safeOpenObject(OdDb::kForRead);
  pColor->setColor( OdCmColor(OdCmEntityColor::kByColor) );
  odPrintConsoleString(L"\nHandle=%llx, Color=%x", pColor->handle(), pColor->entityColor().color());
}
catch(OdError& e) { odPrintConsoleString(L"\nStatus: %d - %s ", e.code(), e.description().c_str()); }

pColor = NULL;
try {
  pColor = idObject1.safeOpenObject(OdDb::kForWrite);
  pColor->setColor( OdCmColor(OdCmEntityColor::kByBlock) );
  odPrintConsoleString(L"\nHandle=%llx, Color=%x", pColor->handle(), pColor->entityColor().color());
}
catch(OdError& e) { odPrintConsoleString(L"\nStatus: %d - %s ", e.code(), e.description().c_str()); }

pColor = NULL;
try {
  pColor = idObject1.safeOpenObject(OdDb::kForNotify);
  pColor->setColor( OdCmColor(OdCmEntityColor::kByLayer) );
  odPrintConsoleString(L"\nHandle=%llx, Color=%x", pColor->handle(), pColor->entityColor().color());
}
catch(OdError& e) { odPrintConsoleString(L"\nStatus: %d - %s ", e.code(), e.description().c_str()); }

The result is the following:


Status: 62 - Not opened for write
Handle=40, Color=c1000000
Handle=40, Color=c0000000

Releasing an object

To release an object, delete all pointers to it or set them to NULL. When the last reference to an object is lost, the object is released. Released objects can be obtained by another procedure in the current run-time session.

See Also

Identifying the Objects of Databases

Working with Smart Pointers

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