Reputation: 1435
I must construct a graph from 2 different custom types that I cannot change.
Because their signatures and purpose are almost the same I have some code duplication I would like to remove.
Problem is they return a different iterator.
for (auto itFrom = m_pModelSpace->newIterator(); !itFrom->done() && nodesPassed < maxNodes; itFrom->step(), ++nodesPassed)
{
/*
...
*/
for (auto itTo = m_pModelSpace->newIterator(); itTo->objectId() != itFrom->objectId(); itTo->step())
{
/*
...
*/
}
}
for (auto itFrom = m_pSelectionSet->newIterator(); !itFrom->done() && nodesPassed < maxNodes; itFrom->next(), ++nodesPassed)
{
/*
...
*/
for (auto itTo = m_pSelectionSet->newIterator(); itTo->objectId() != itFrom->objectId(); itTo->next())
{
/*
...
*/
}
}
The comments are exactly the same code.
Is there any way to work around this?
I was thinking about a wrapper with 2 constructors for each type but then I also need a wrapper for the object iterator.
Another idea was multiple inheritance of the 2 types but that also doesn't really feel right.
Any advice?
Summurarized solution
Thanks to Mark B I have come to the following solution:
namespace {
OdDbObjectIteratorPtr get_iterator(OdSmartPtr<OdDbBlockTableRecord>& pEntityCollection)
{
return pEntityCollection->newIterator();
}
OdDbSelectionSetIteratorPtr get_iterator(OdSmartPtr<OdSelectionSet>& pEntityCollection)
{
return pEntityCollection->newIterator();
}
void increment_iterator(OdDbObjectIteratorPtr& iter)
{
iter->step();
}
void increment_iterator(OdDbSelectionSetIteratorPtr& iter)
{
iter->next();
}
}
namespace spax{
template <typename Collection>
void ConnectionGraph::ConstructGraph(Collection& pEntityCollection, int maxNodes)
{
// ...
for (auto itFrom = get_iterator(pEntityCollection); !itFrom->done() && nodesPassed < maxNodes; increment_iterator(itFrom), ++nodesPassed)
{
//...
for (auto itTo = get_iterator(pEntityCollection); itTo->objectId() != itFrom->objectId(); increment_iterator(itTo))
{
//...
}
}
}
}
Because the Selection set returned a parent OdSelectionSetIteratorPtr of OdDbSelectionSetIteratorPtr I added another function inspired by this solution to get the iterators. Thanks for the help, I'm really pleased with the outcome.
Upvotes: 2
Views: 158
Reputation: 44268
You can write a function with m_pModelSpace
as parameter. In case you have to pass to match that is used in that code from local context use lambda:
auto advance = []( auto &it ) { it->step() };
auto loop = [&]( auto p ) {
for (auto itFrom = p->newIterator(); !itFrom->done() && nodesPassed < maxNodes; advance( itFrom ), ++nodesPassed)
{
/*
...
*/
for (auto itTo = p->newIterator(); itTo->objectId() != itFrom->objectId(); advance( itTo ) )
{
/*
...
*/
}
}
};
loop( m_pModelSpace );
advance = []( auto &it ) { it->next() };
loop( m_pSelectionSet );
Note: I changed lamdas parameter type to auto
as you said that container types are unrelated. This would work for c++14 or later, otherwise you can use template.
Upvotes: 2
Reputation: 96271
What about using a template function with a level of indirection to handle the iterator increment?
void increment_iterator(<model space iterator type>& iter) { iter->step(); }
void increment_iterator(<selection set iterator type>& iter) { iter->next(); }
template <typename Container>
void execute_nested_loop(Container* c)
{
for (auto itFrom = c->newIterator(); !itFrom->done() && nodesPassed < maxNodes; increment_iterator(itFrom), ++nodesPassed)
{
/*
...
*/
for (auto itTo = c->newIterator(); itTo->objectId() != itFrom->objectId(); increment_iterator(itTo))
{
/*
...
*/
}
}
}
Then call it:
execute_nested_loop(m_pModelSpace);
execute_nested_loop(m_pSelectionSet);
Upvotes: 4
Reputation: 5728
In C++, normalize the interfaces (get rid of step()
vs next()
), then use a template function.
Upvotes: 1
Reputation: 687
Create a function that takes as entry the parameters that you use and where you put your identical code and call it in each loop and
Upvotes: 1