kicad/include/multivector.h

324 lines
9.7 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2017 CERN
* Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
* @author Bernhard Stegmaier <stegmaier@sw-systems.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef MULTIVECTOR_H
#define MULTIVECTOR_H
#include <boost/ptr_container/ptr_vector.hpp>
#include <stdexcept>
/**
* Multivector container type.
*
* Keeps items segregated by their type in multiple ptr_vectors. Provides both
* access as a flat list as well as access by type of item.
*
* T is the stored type, needs to provide Type() method used to segregate items.
* FIRST_TYPE_VAL is the lower boundary value of the types stored in the container.
* LAST_TYPE_VAL is the upper boundary value of the types stored in the container.
*/
template<typename T, int FIRST_TYPE_VAL, int LAST_TYPE_VAL>
class MULTIVECTOR
{
public:
/**
* Type value to indicate no specific type. Mostly used to access the container as a flat list
* or to return data for the whole container.
*/
static constexpr int UNDEFINED_TYPE = 0;
static_assert( FIRST_TYPE_VAL > UNDEFINED_TYPE,
"FIRST_TYPE_VAL must be greater than UNDEFINED_TYPE" );
static_assert( FIRST_TYPE_VAL < LAST_TYPE_VAL,
"FIRST_TYPE_VAL must be greater than LAST_TYPE_VAL" );
/**
* Helper for defining a list of library draw object pointers.
*
* The Boost pointer containers are responsible for deleting object pointers placed
* in them. If you access a object pointer from the list, do not delete it directly.
*/
typedef boost::ptr_vector<T> ITEM_PTR_VECTOR;
/**
* Generic implementation of a flat const/non-const iterator over contained items.
*/
template<typename ITEM_TYPE, typename ITEM_CONTAINER, typename ITEM_CONTAINER_IT>
class ITERATOR_BASE
{
public:
ITEM_TYPE& operator*()
{
return *m_it;
}
ITEM_TYPE* operator->()
{
return &( *m_it );
}
ITERATOR_BASE& operator++()
{
if( m_it != (*m_parent)[ m_curType ].end() )
++m_it;
validate();
return *this;
}
bool operator!=( const ITERATOR_BASE& aOther ) const
{
if( aOther.m_parent != m_parent )
return true;
if( aOther.m_filter != m_filter )
return true;
if( aOther.m_curType != m_curType )
return true;
return aOther.m_it != m_it;
}
protected:
/**
* @param aItems is the container to wrap.
* @param aIt is the iterator to initialize this iterator (usually some begin() or end()
* iterator).
* @param aBucket is the type ID of the given iterator.
* @param aType enables item type filtering. When aType is UNDEFINED_TYPE, there is no
* filtering and all item types are accessible by the iterator.
*/
ITERATOR_BASE( ITEM_CONTAINER* aItems, ITEM_CONTAINER_IT aIt,
int aBucket, int aType = UNDEFINED_TYPE )
: m_parent( aItems ), m_it( aIt ), m_curType( aBucket )
{
m_filter = ( aType != UNDEFINED_TYPE );
}
///< Assures the iterator is in a valid state.
void validate()
{
// for all-items iterators (unfiltered): check if this is the end of the
// current type container, if so switch to the next non-empty container
if( !m_filter && m_it == (*m_parent)[ m_curType ].end() )
{
// switch to the next type (look for a not empty container)
int nextType = m_curType;
do
++nextType;
while( ( nextType <= LAST_TYPE ) && (*m_parent)[ nextType ].empty() );
// there is another not empty container, so make the iterator point to it,
// otherwise it means the iterator points to the last item
if( nextType <= LAST_TYPE )
{
m_curType = nextType;
m_it = (*m_parent)[ m_curType ].begin();
}
}
}
///< Wrapped container
ITEM_CONTAINER* m_parent;
///< Iterator for one of the ptr_vector containers stored in the array
ITEM_CONTAINER_IT m_it;
///< Flag indicating whether type filtering is enabled
bool m_filter;
///< Type of the currently iterated items
int m_curType;
friend class MULTIVECTOR;
};
///< The non-const iterator
typedef ITERATOR_BASE<T, MULTIVECTOR<T, FIRST_TYPE_VAL, LAST_TYPE_VAL>,
typename ITEM_PTR_VECTOR::iterator> ITERATOR;
///< The const iterator
typedef ITERATOR_BASE<const T, const MULTIVECTOR<T, FIRST_TYPE_VAL, LAST_TYPE_VAL>,
typename ITEM_PTR_VECTOR::const_iterator> CONST_ITERATOR;
MULTIVECTOR()
{
}
void push_back( T* aItem )
{
operator[]( aItem->Type() ).push_back( aItem );
}
ITERATOR erase( const ITERATOR& aIterator )
{
ITERATOR it( aIterator );
it.m_it = (*aIterator.m_parent)[ aIterator.m_curType ].erase( aIterator.m_it );
it.validate();
return it;
}
ITERATOR begin( int aType = UNDEFINED_TYPE )
{
int bucket = ( aType != UNDEFINED_TYPE ) ? aType : first();
return ITERATOR( this, operator[]( bucket ).begin(), bucket, aType );
}
ITERATOR end( int aType = UNDEFINED_TYPE )
{
int bucket = ( aType != UNDEFINED_TYPE ) ? aType : last();
return ITERATOR( this, operator[]( bucket ).end(), bucket, aType );
}
CONST_ITERATOR begin( int aType = UNDEFINED_TYPE ) const
{
int bucket = ( aType != UNDEFINED_TYPE ) ? aType : first();
return CONST_ITERATOR( this, operator[]( bucket ).begin(), bucket, aType );
}
CONST_ITERATOR end( int aType = UNDEFINED_TYPE ) const
{
int bucket = ( aType != UNDEFINED_TYPE ) ? aType : last();
return CONST_ITERATOR( this, operator[]( bucket ).end(), bucket, aType );
}
void clear( int aType = UNDEFINED_TYPE )
{
if( aType != UNDEFINED_TYPE )
{
operator[]( aType ).clear();
}
else
{
for( int i = 0; i < TYPES_COUNT; ++i)
m_data[ i ].clear();
}
}
size_t size( int aType = UNDEFINED_TYPE ) const
{
if( aType != UNDEFINED_TYPE )
{
return operator[]( aType ).size();
}
else
{
size_t cnt = 0;
for( int i = 0; i < TYPES_COUNT; ++i)
cnt += m_data[ i ].size();
return cnt;
}
}
bool empty( int aType = UNDEFINED_TYPE ) const
{
return ( size( aType ) == 0 );
}
void sort()
{
for( int i = 0; i < TYPES_COUNT; ++i )
m_data[ i ].sort();
}
/**
* Remove duplicate elements in list
*/
void unique()
{
for( int i = 0; i < TYPES_COUNT; ++i )
{
if( m_data[ i ].size() > 1 )
m_data[ i ].unique();
}
}
ITEM_PTR_VECTOR& operator[]( int aType )
{
if( ( aType < FIRST_TYPE ) || ( aType > LAST_TYPE ) )
{
wxFAIL_MSG( wxT( "Attempted access to type not within MULTIVECTOR" ) );
// return type is a reference so we have to return something...
aType = FIRST_TYPE;
}
return m_data[ aType - FIRST_TYPE ];
}
const ITEM_PTR_VECTOR& operator[]( int aType ) const
{
if( ( aType < FIRST_TYPE ) || ( aType > LAST_TYPE ) )
{
wxFAIL_MSG( wxT( "Attempted access to type not within MULTIVECTOR" ) );
// return type is a reference so we have to return something...
aType = FIRST_TYPE;
}
return m_data[ aType - FIRST_TYPE ];
}
// Range of valid types handled by the iterator
static constexpr int FIRST_TYPE = FIRST_TYPE_VAL;
static constexpr int LAST_TYPE = LAST_TYPE_VAL;
static constexpr int TYPES_COUNT = LAST_TYPE - FIRST_TYPE + 1;
private:
///< Get first non-empty type or first type if all are empty.
int first() const
{
int i = 0;
while( ( i < TYPES_COUNT ) && ( m_data[ i ].empty() ) )
++i;
return ( i == TYPES_COUNT ) ? FIRST_TYPE : FIRST_TYPE + i;
}
///< Get last non-empty type or first type if all are empty.
int last() const
{
int i = TYPES_COUNT - 1;
while( ( i >= 0 ) && ( m_data[ i ].empty() ) )
--i;
return ( i < 0 ) ? FIRST_TYPE : FIRST_TYPE + i;
}
///< Contained items by type
ITEM_PTR_VECTOR m_data[TYPES_COUNT];
};
#endif /* MULTIVECTOR_H */