Author Topic: AcDbSymbolTableIterator and shared_ptr (scoped_ptr as well)  (Read 8312 times)

0 Members and 1 Guest are viewing this topic.

pkohut

  • Guest
Re: AcDbSymbolTableIterator and shared_ptr (scoped_ptr as well)
« Reply #15 on: February 22, 2010, 08:10:54 AM »
... (or throw caution to the wind and assume it will always be NULL when the function returns an error).

Ah, good point! I was assuming(I'm probably wrong) that the pointer would always be NULL, otherwise how would the newIterator() function know what status to return? I.e. either Autodesk has overridden new to return NULL on failure or a std::bad_alloc  is thrown .?.?.

After a couple more hours of tinkering tonight Iím settling on this.  It works for both shared_ptr and scoped_ptr (listing 1).  Listing 1a shows how to use it with AcDbSymbolTablePointer derived tables.  Listing 2 is the template code that makes this work, and goes in a header file.

Note if GetSmartPointerIterator returns Acad::eOk then the iterator pointer handled by the scoped_ptr or shared_ptr will be valid and ready to use.  Otherwise the iterator pointer will be NULL and the scoped_ptr or shared_ptr object will be 0. So usage can be:
   GetSmartPointer<AcDbBlockTableIterator> (pBlockTable, pTblIterator);
   If(pTblIterator) { Ö }
or
   es = GetSmartPointer<AcDbBlockTableIterator> (pBlockTable, pTblIterator);
   If(es == Acad::eOk) { Ö }


Listing 1, inside some function (pLayerTable is an AcDbSymbolTable derived type)
Code: [Select]
    boost::[color=brown]scoped_ptr[/color]<AcDbLayerTableIterator> pIter1;
    es = GetSmartPointerIterator<AcDbLayerTableIterator>(pLayerTable, pIter1);

    boost::[color=brown]shared_ptr[/color]<AcDbLayerTableIterator> pIter2;
    es = GetSmartPointerIterator<AcDbLayerTableIterator>(pLayerTable, pIter2);

Listing 1a (pLayerTable is an AcDbSymbolTablePointer derived type)
Code: [Select]
    boost::scoped_ptr<AcDbLayerTableIterator> pIter1;
    es = GetSmartPointerIterator<AcDbLayerTableIterator>([color=brown]pLayerTable.object()[/color], pIter1);

Listing 2 (header file)
Code: [Select]
template<typename Iterator, typename Table, typename SP>
inline Acad::ErrorStatus GetSmartPointerIterator(const Table * pTable, SP &spIterator)
{
[color=brown]    Iterator * pIterator;
    Acad::ErrorStatus es = pTable->newIterator(pIterator);
    if(es == Acad::eOk)
        spIterator.reset(pIterator);
    return es;[/color]
}

template<typename Iterator, typename Table>
inline Acad::ErrorStatus GetSmartPointerIterator(const Table * pTable, boost::shared_ptr<Iterator> &spIterator)
{
    return GetSmartPointerIterator<Iterator, Table, boost::shared_ptr<Iterator>>(pTable, spIterator);
}

template<typename Iterator, typename Table>
inline Acad::ErrorStatus GetSmartPointerIterator(const Table * pTable, boost::scoped_ptr<Iterator> &spIterator)
{
    return GetSmartPointerIterator<Iterator, Table, boost::scoped_ptr<Iterator>>(pTable, spIterator);
}


Final thoughts:
  • Wanted a single function signature to handle both scoped_ptr and shared_ptr
  • Again I tried 3 or 4 different implementation methods and they were either too messy, or in the case of scoped_ptr's would not work because scoped_ptr cannot be returned from a function since the copy operator is private .
  • Might take a bit to get use to the implementation details being hidden in the template, though it is no different than AcDbObjectPointerBase->acquire hiding its implementation details.
  • Not trilled about the name GetSmartPointerIterator.
  • Template error messages are a bear to figure out.

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 7033
  • AKA Daniel
Re: AcDbSymbolTableIterator and shared_ptr (scoped_ptr as well)
« Reply #16 on: February 22, 2010, 08:50:34 AM »
The docs are wrong; it is not necessary to hold the collection open while iterating over it. There may be cases where it's a good idea (i.e. when you need to prevent changes until iteration is complete), and I suspect that's where the statement originates.

found this in the header
Code: [Select]
///// AcDbSymbolTableIterator
//
// This class is responsible for iterating over the records in a symbol
// table.
//
// This is a completely "generic" symbol table iterator in that it can
// be used to iterate over the contents of any AcDbSymbolTable subclass.
// However, symbol table specific iterators are defined below.
//
// One major item of note.  The creation of one of these iterators
// requires the opening (for read) of the table being iterated over.
// This read stays in effect until the iterator is destroyed.
//

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 7033
  • AKA Daniel
Re: AcDbSymbolTableIterator and shared_ptr (scoped_ptr as well)
« Reply #17 on: February 22, 2010, 08:53:31 AM »
After a couple more hours of tinkering tonight Iím settling on this.

Cool, I will have to give this a try  8-)

pkohut

  • Guest
Re: AcDbSymbolTableIterator and shared_ptr (scoped_ptr as well)
« Reply #18 on: February 22, 2010, 09:17:55 AM »
found this in the header
Code: [Select]
///// AcDbSymbolTableIterator
//
// This class is responsible for iterating over the records in a symbol
// table.
//
// This is a completely "generic" symbol table iterator in that it can
// be used to iterate over the contents of any AcDbSymbolTable subclass.
// However, symbol table specific iterators are defined below.
//
[color=brown]// One major item of note.  The creation of one of these iterators
// requires the opening (for read) of the table being iterated over.
// This read stays in effect until the iterator is destroyed.[/color]
//

Nice find.  Quick test reveals this may not always be the case.  :-(


Code: [Select]
    Acad::ErrorStatus es;
    AcDbLayerTable * pLayerTable;
    es = pDb->getLayerTable(pLayerTable, AcDb::kForRead);
    if(es != Acad::eOk)
        return es;

[color=brown]    bool bVal = pLayerTable->isReallyClosing();[/color][color=navy] // bVal == true[/color]
    AcDbLayerTableIterator * pIter;
    es = pLayerTable->newIterator(pIter);
[color=brown]    bVal = pLayerTable->isReallyClosing();[/color][color=navy] // bVal == true[/color]
    pLayerTable->close();
    delete pIter;

owenwengerd

  • Bull Frog
  • Posts: 441
Re: AcDbSymbolTableIterator and shared_ptr (scoped_ptr as well)
« Reply #19 on: February 22, 2010, 09:25:40 AM »
// One major item of note.  The creation of one of these iterators
// requires the opening (for read) of the table being iterated over.
// This read stays in effect until the iterator is destroyed.

That may have been true in R13, but I suspect it is no longer true.

owenwengerd

  • Bull Frog
  • Posts: 441
Re: AcDbSymbolTableIterator and shared_ptr (scoped_ptr as well)
« Reply #20 on: February 22, 2010, 09:51:51 AM »
I've never monkeyed around with an iterator smart pointer, but this thread got me thinking about how I would want it to work if I did. I think I'd prefer something simpler and more straightforward:
Code: [Select]
AcDbBlockTableRecord* pBlockTable = OpenBlockTable();
{ //local scope for limiting pIter lifetime
  MyBlockTableIteratorPtr pIter;
  Acad::ErrorStatus es = pBlockTable->newIterator( pIter );
  pBlockTable->close();
  if( es != Acad::eOk )
    return es;
  for( pIter->start(); !pIter->done(); pIter->step() )
  {
    //use pIter
    //here we can cleanly exit the scope without leaking anything
  }
} //pIter is automatically destroyed here

Looking at this code, I can see why I never bothered. The only real benefit is that I can cleanly exit the iterator loop without worrying about cleanup.  However, if this is ever necessary, it's not much more difficult to just set an error status, then break out of the loop, delete the iterator, then check the error status and exit at that point if there was an error.

In the end, I'm just not convinced that there is enough bnefit to a smart iterator pointer in most cases to warrant the effort and hiding of the implementation.

pkohut

  • Guest
Re: AcDbSymbolTableIterator and shared_ptr (scoped_ptr as well)
« Reply #21 on: February 22, 2010, 11:28:10 AM »
Looking at this code, I can see why I never bothered. The only real benefit is that I can cleanly exit the iterator loop without worrying about cleanup.  However, if this is ever necessary, it's not much more difficult to just set an error status, then break out of the loop, delete the iterator, then check the error status and exit at that point if there was an error.

In the end, I'm just not convinced that there is enough bnefit to a smart iterator pointer in most cases to warrant the effort and hiding of the implementation.

I agree.  In the situation I'm dealing with there are a number of resources being gathered, each handled with smart pointers of some sort.  The iterators were the only resource that wasn't (past tense) being handled.  During the gathering phase of the program there is a potential for an exception to be thrown, and I want all the gathered resources to be released in such a case.  Without smart pointers this is delt with in try/catch blocks and declared/aquired pointers need to be kept in sync with the catch block throughout an application's development life time and maintance.  So I'm trying to limit my use of try/catch.
« Last Edit: February 22, 2010, 11:35:16 AM by pkohut »