AcDbObjectPointer
class AcDbObjectPointer;
typedef AcDbObjectPointer<AcDbDictionary> AcDbDictionaryPointer;
typedef AcDbObjectPointer<AcDbEntity> AcDbEntityPointer;
AcDbSymbolTablePointer
typedef AcDbSymbolTablePointer<AcDbBlockTable> AcDbBlockTablePointer;
typedef AcDbSymbolTablePointer<AcDbDimStyleTable> AcDbDimStyleTablePointer;
typedef AcDbSymbolTablePointer<AcDbLayerTable> AcDbLayerTablePointer;
typedef AcDbSymbolTablePointer<AcDbLinetypeTable> AcDbLinetypeTablePointer;
typedef AcDbSymbolTablePointer<AcDbRegAppTable> AcDbRegAppTablePointer;
typedef AcDbSymbolTablePointer<AcDbTextStyleTable> AcDbTextStyleTablePointer;
typedef AcDbSymbolTablePointer<AcDbUCSTable> AcDbUCSTablePointer;
typedef AcDbSymbolTablePointer<AcDbViewTable> AcDbViewTablePointer;
typedef AcDbSymbolTablePointer<AcDbViewportTable> AcDbViewportTablePointer;
AcDbSymbolTableRecordPointer
typedef AcDbSymbolTableRecordPointer<AcDbBlockTableRecord>
AcDbBlockTableRecordPointer;
typedef AcDbSymbolTableRecordPointer<AcDbDimStyleTableRecord>
AcDbDimStyleTableRecordPointer;
typedef AcDbSymbolTableRecordPointer<AcDbLayerTableRecord>
AcDbLayerTableRecordPointer;
typedef AcDbSymbolTableRecordPointer<AcDbLinetypeTableRecord>
AcDbLinetypeTableRecordPointer;
typedef AcDbSymbolTableRecordPointer<AcDbRegAppTableRecord>
AcDbRegAppTableRecordPointer;
typedef AcDbSymbolTableRecordPointer<AcDbTextStyleTableRecord>
AcDbTextStyleTableRecordPointer;
typedef AcDbSymbolTableRecordPointer<AcDbUCSTableRecord>
AcDbUCSTableRecordPointer;
typedef AcDbSymbolTableRecordPointer<AcDbViewTableRecord>
AcDbViewTableRecordPointer;
typedef AcDbSymbolTableRecordPointer<AcDbViewportTableRecord>
AcDbViewportTableRecordPointer;
Example
AcDbBlockTableRecordPointer curSpace(
curDoc()->database()->currentSpaceId(),
AcDb::kForWrite);
if (curSpace.openStatus() == Acad::eOk)
{
// Perform operations on the block table record
}
AcDbObjectPointer<AcDbLine> line;
line.create();
line->setStartPoint(AcGePoint3d(0, 0, 0));
line->setEndPoint(AcGePoint3d(10, 10, 0));
AcDbBlockTableRecordPointer curSpace2(
acDocManager->curDocument()->database()->currentSpaceId(),
AcDb::kForWrite);
if (curSpace2.openStatus() == Acad::eOk)
{
curSpace2->appendAcDbEntity(line);
}
// This example adds extension dictionary data to an AcDbEntity using
// AcDbEntityPointer, AcDbDictionaryPointer, and AcDbObjectPointer<AcDbXrecord>.
// When these smart pointer instances are destroyed, the underlying Arx
// entities/objects are automatically closed, even if exceptions occur.
bool SetEntityDictFromRbChain(AcDbObjectId entId, ACHAR *strDictName, resbuf* pRbValue)
{
AcDbEntityPointer pObj(entId, AcDb::kForWrite);
if (pObj.openStatus() != Acad::eOk)
return false;
AcDbObjectId extDictId = pObj->extensionDictionary();
if (extDictId == AcDbObjectId::kNull)
{
if (pObj->createExtensionDictionary() != Acad::eOk)
return false;
if ((extDictId = pObj->extensionDictionary()) == AcDbObjectId::kNull)
return false;
}
AcDbDictionaryPointer pDict(extDictId, AcDb::kForRead);
if (pDict.openStatus() != Acad::eOk)
return false;
AcDbObjectId xRecId;
if (pDict->getAt(strDictName, xRecId) != Acad::eOk)
{
auto pXrec = new AcDbXrecord(); // Could use scoped_ptr for safety
pDict->upgradeOpen();
if (pDict->setAt(strDictName, pXrec, xRecId) != Acad::eOk)
{
delete pXrec;
return false;
}
pXrec->close();
}
AcDbObjectPointer<AcDbXrecord> pXrecord(xRecId, AcDb::kForWrite);
if (pXrecord.openStatus() != Acad::eOk)
return false;
if (pXrecord->setFromRbChain(*pRbValue) != Acad::eOk)
return false;
acutRelRb(pRbValue);
return true;
}
Automatic Closing of AutoCAD Objects with ObjectARX Smart Pointers
Those of us who regularly write ObjectARX code to manipulate the AutoCAD drawing database are fully aware of the mechanism for opening an object for read (simply to access data inside it) or write (to update it with new data). Oh, and I almost forgot — followed by a call to close() when you are done.
But here lies a very common problem illustrated by that last sentence; the problems start when you accidentally forget to close an object once you are finished with it. AutoCAD follows a strict set of rules which allows the checking-in/out of data inside AutoCAD and these rules must be adhered to. If not, then AutoCAD will abort in order to do its best to save the previous valid state of the database.
"You must be very careful to close your objects when you are finished with them." It’s very easy for me to say that, but even I, the person shaking his finger saying those infamous words, will fail to remember my own advice time and time again. This is why using ObjectARX SmartPointers is a must.
So let’s look at this thing in ObjectARX called SmartPointers.
What are they? First, take a look at the MSDN article on "Template Classes" as it explains the basic concept. Leading on from that article, and now in my own words, ObjectARX SmartPointers are C++ template classes which wrap an underlying AutoCAD AcDbObject-derived class pointer, and simply provide automatic closure of that pointer, if valid, on destruction of the ObjectARX SmartPointer class (so at the end of a function or closing brace }).
A question that often arises is on the usage of this class, in particular the way to access the member functions. The template class itself has been implemented so that if you reference a member function with the dot . operator,
line.openStatus()
you reference the ObjectARX SmartPointer specific functions. If you reference a member function with the arrow -> operator,
line->setStartPoint()
because the arrow operator has been overridden to return the underlying AcDbObject pointer, you simply reference the underlying AcDbObject-derived class, in this case AcDbLine::setStartPoint().
So how do we use them then…? Let’s start by showing old-style ObjectARX code which adds an AcDbLine to the current space using manual open and close.
// Create a new line
AcDbLine *line = new AcDbLine();
// Set its properties
line->setStartPoint(AcGePoint3d(10,10,0));
line->setEndPoint(AcGePoint3d(20,30,0));
// Now add it to the current space
AcDbBlockTableRecord *curSpace = nullptr;
// Open the current space for write
Acad::ErrorStatus es =
acdbOpenObject(
(AcDbBlockTableRecord *&)curSpace,
curDoc()->database()->currentSpaceId(),
AcDb::kForWrite
);
if (es == Acad::eOk)
{
// Add the line
es = curSpace->appendAcDbEntity(line);
// Check for errors
if (es != Acad::eOk)
{
delete line;
return;
}
// Close everything
line->close();
curSpace->close();
}
It’s the two close() calls at the end wich are, first of all, very easy to forget. But also notice there is a return right before them which bypasses the close of curSpace in a rare failure scenario. This is where ObjectARX SmartPointers not only provide automatic closure and cleanup, but also peace of mind.
Let’s take a look at the same code, this time using ObjectARX SmartPointers.
// Create a new line using a smart pointer
AcDbObjectPointer<AcDbLine> line;
line.create();
// Set its properties
line->setStartPoint(AcGePoint3d(10,10,0));
line->setEndPoint(AcGePoint3d(20,30,0));
// Open the current space for write using a smart pointer
AcDbBlockTableRecordPointer curSpace(
curDoc()->database()->currentSpaceId(),
AcDb::kForWrite
);
if (curSpace.openStatus() == Acad::eOk)
{
Acad::ErrorStatus es = curSpace->appendAcDbEntity(line);
if (es != Acad::eOk)
{
// No need for a manual delete; the smart pointer handles it
return;
}
}
// Everything is automatically closed when the smart pointers go out of scope
Not only is this ObjectARX code safe from missing close() calls, it is also memory-leak safe. Also, look how much tidier it is. Much more friendly in my opinion!
Here’s some more SmartPointer code which selects an entity on screen and opens it for read as an example.
ads_name ename;
ads_point pt;
// Pick an entity
int res = acedEntSel(_T("\nPick a Line : "), ename, pt);
if (res == RTNORM)
{
AcDbObjectId objId;
acdbGetObjectId(objId, ename);
// Open the entity for read
AcDbObjectPointer<AcDbLine> ent(objId, AcDb::kForRead);
if (ent.openStatus() == Acad::eOk)
{
AcGePoint3d startPnt;
ent->startPoint(startPnt);
// Do something with the start point
}
}
But what if you have a large amount of existing code using the old-style open/close, and you want to migrate to ObjectARX SmartPointers with the least effort? We’ve tried to make it easy for you. Since ObjectARX 2007, in dbobjptr.h, simply uncomment the line #define DBOBJPTR_EXPOSE_PTR_REF and life becomes easy! (Well, with one exception — see the note below.)
Here is the converted version of the original code we used at the beginning, showing how easy the migration can be (changes are highlighted in bold).
// Create a new line
AcDbObjectPointer<AcDbLine> line = new AcDbLine(); // Changed from raw pointer
// Set its properties
line->setStartPoint(AcGePoint3d(10,10,0));
line->setEndPoint(AcGePoint3d(20,30,0));
// Now add it to the current space
AcDbBlockTableRecordPointer curSpace = nullptr; // Changed from raw pointer
// Open the current space for write
Acad::ErrorStatus es =
acdbOpenObject((AcDbBlockTableRecord *&)curSpace, // Cast accepted with macro
curDoc()->database()->currentSpaceId(),
AcDb::kForWrite
);
if (es == Acad::eOk)
{
es = curSpace->appendAcDbEntity(line);
if (es != Acad::eOk)
{
delete line; // Still needs delete if line was created with raw new
return;
}
line->close();
curSpace->close();
}
Notice that I did not bother to remove the two close() calls at the end. There is no need. If you close them manually, or forget, it’s all good with ObjectARX SmartPointers.
Note: In order to get acdbOpenObject to accept the same code pattern as before, in dbobjptr.h at line 467 (ObjectARX 2009 SDK), there is an assertion which needs to be omitted. Either #define NDEBUG or, I recommend that you simply enclose the assert with #ifndef DBOBJPTR_EXPOSE_PTR_REF as shown below:
AcDbObjectPointerBase<T_OBJECT>::object(){
#ifndef DBOBJPTR_EXPOSE_PTR_REF
assert(m_status == Acad::eOk);
#endif // DBOBJPTR_EXPOSE_PTR_REF
Last but not least is the new AcDbSmartObjectPointer template class in ObjectARX 2009, defined in the header file dbobjptr2.h.
This new template class works in the same way as AcDbObjectPointer except that it does not open an object at all if its open state is already what was requested, nor does it attempt to close it multiple times before opening in the desired manner. It merely hands you the already opened object pointer for your use. This makes it much more efficient and powerful. It also treats kForNotify and kForRead in the same manner (effectively kForRead).
One feature of this new SmartPointer class that I’d like to talk about is the ability to multiply open an object for write from different places at the same time, a bit like a transaction can — this is extremely powerful when you think about it.
At the same time, I find that thinking about the power this can provide may start generating some other complex thoughts and scenarios that we should be cautious about. The bottom line is that you should be very careful about multiply opening an object for write, no matter how good the controlling class is.
An example where this type of functionality might be useful to us developers is in an object reactor callback. Quite often you might want to modify the current object’s state, but you cannot because it is already open for notify. Using this new SmartPointer class makes it possible to modify the object as you see fit in this context, but be careful to handle the recursive object modified notifications that will be fired.
All in all, a very exciting new addition to the ObjectARX API — make sure you check it out.