Skip to content
Snippets Groups Projects
SelectableObject.h 18.2 KiB
Newer Older
//======================================================================================================================
//
//  This file is part of waLBerla. waLBerla 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 3 of
//  the License, or (at your option) any later version.
//
//  waLBerla 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
//  You should have received a copy of the GNU General Public License along
//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
//
//! \file SelectableObject.h
//! \ingroup core
//! \author Florian Schornbaum <florian.schornbaum@fau.de>
//
//======================================================================================================================

#pragma once

#include "core/debug/Debug.h"

#include <iostream>
#include <sstream>
#include <string>
#include <vector>


namespace walberla {
namespace selectable {



//**********************************************************************************************************************
/*!
*   \brief Abstract base class for storing and selecting objects of type T which are attached with selection attributes
*          of type A
*
*   The sole purpose of this class is to store selectable objects of type T, meaning objects which are attached with
*   additional selection attributes of type A, in order to later retrieve only those objects which match certain
*   requested attributes. For the retrieval (see functions "get") a selection object of type S is used (type S may or
*   may not be equal to type A).
*
*   Requirements for type A:
*
*      - objects of type A must be comparable with operator "!="
*      - A must implement operator "<<" for output via a std::ostream object
*
*   Additionally, it must be possible to store objects of both types T and A in a std::vector.
*
*   Objects can be added by calling the function "add" (for every object that is added an identifier string can be
*   provided) and be retrieved by calling "get". For additional information refer to the documentation of these
*   functions.
*
*   Every class that derives from SelectableObject must implement the function
*      \code void select( std::vector< size_t >& index, const S& selector ) const \endcode
*   After returning from the call to this function, the vector "index" must contain the index of every object in
*   "attributes_" that matches with the provided selector "selector" (for an actual implementation of SelectableObject
*   see "SetSelectableObject.h").
*/
//**********************************************************************************************************************

template< typename T, typename A, typename S >
class SelectableObject {

public:

   //**Iterators********************************************************************************************************
   /*! \name Iterators */
   //@{
   class const_iterator;
   friend class const_iterator;

   class iterator;
   friend class iterator;
   class iterator {

      friend class const_iterator;
      friend class SelectableObject;

   public:

      iterator( const iterator& it ) : selectable_( it.selectable_ ), index_( it.index_ ) {}

      iterator& operator++() { ++index_; return *this; }                            // prefix ++X
      iterator  operator++(int) { iterator it( *this ); operator++(); return it; }; // postfix X++

      bool operator==( const iterator& rhs ) const { return selectable_ == rhs.selectable_ && index_ == rhs.index_; }
      bool operator!=( const iterator& rhs ) const { return !operator==(rhs); }

      T *                        get() { WALBERLA_ASSERT_LESS( index_, selectable_->object_.size() );     return &(selectable_->object_[index_]); }
      const std::string & identifier() { WALBERLA_ASSERT_LESS( index_, selectable_->identifier_.size() ); return selectable_->identifier_[index_]; }

      T & operator*()  { WALBERLA_ASSERT_LESS( index_, selectable_->object_.size() ); return   selectable_->object_[index_]; }
      T * operator->() { WALBERLA_ASSERT_LESS( index_, selectable_->object_.size() ); return &(selectable_->object_[index_]); }

   private:

      iterator( SelectableObject* const selectable, const size_t index ) : selectable_( selectable ), index_( index ) {}

      SelectableObject * const selectable_;
      size_t index_;
   };

   class const_iterator {

      friend class SelectableObject;

   public:

      const_iterator( const       iterator& it ) : selectable_( it.selectable_ ), index_( it.index_ ) {}
      const_iterator( const const_iterator& it ) : selectable_( it.selectable_ ), index_( it.index_ ) {}

      const_iterator& operator++() { ++index_; return *this; }                                  // prefix ++X
      const_iterator  operator++(int) { const_iterator it( *this ); operator++(); return it; }; // postfix X++

      bool operator==( const const_iterator& rhs ) const { return selectable_ == rhs.selectable_ && index_ == rhs.index_; }
      bool operator!=( const const_iterator& rhs ) const { return !operator==(rhs); }

      const T *                  get() const { WALBERLA_ASSERT_LESS( index_, selectable_->object_.size() );     return &(selectable_->object_[index_]); }
      const std::string & identifier() const { WALBERLA_ASSERT_LESS( index_, selectable_->identifier_.size() ); return selectable_->identifier_[index_]; }

      const T & operator*()  const { WALBERLA_ASSERT_LESS( index_, selectable_->object_.size() ); return   selectable_->object_[index_]; }
      const T * operator->() const { WALBERLA_ASSERT_LESS( index_, selectable_->object_.size() ); return &(selectable_->object_[index_]); }

   private:

      const_iterator( const SelectableObject* const selectable, const size_t index ) : selectable_( selectable ), index_( index ) {}

      const SelectableObject * const selectable_;
      size_t index_;
   };
   //@}
   //*******************************************************************************************************************



   virtual ~SelectableObject() = default;

   void add( const T& object, const A& attributes, const std::string& identifier = std::string() );

   iterator       begin()       { return       iterator( this, 0 ); }
   const_iterator begin() const { return const_iterator( this, 0 ); }

   iterator       end()       { return       iterator( this, object_.size() ); }
   const_iterator end() const { return const_iterator( this, object_.size() ); }

   size_t getNumberOfMatching( const S& selector ) const;

   size_t get(              T& object, const S& selector ) const;
   void   get( std::vector<T>& object, const S& selector ) const;

   size_t get(              T& object,                std::string& identifier, const S& selector ) const;
   void   get( std::vector<T>& object, std::vector< std::string >& identifier, const S& selector ) const;

         T* getUnique( const S& selector );
   const T* getUnique( const S& selector ) const;
         T* getUnique( const S& selector, std::string & identifierOut );
   const T* getUnique( const S& selector, std::string & identifierOut ) const;

          void        toStream( std::ostream& os ) const;
   inline std::string toString() const;

   size_t size() const { WALBERLA_ASSERT_EQUAL( object_.size(), identifier_.size() );   return object_.size();  }
   bool  empty() const { WALBERLA_ASSERT_EQUAL( object_.empty(), identifier_.empty() ); return object_.empty(); }

private:

   //*******************************************************************************************************************
   /*!
   *   Every class that derives from SelectableObject must implement this function. After returning from the call to
   *   this function, the vector "index" must contain the index of every object in "attributes_" that matches with the
   *   provided selector "selector".
   */
   //*******************************************************************************************************************
   virtual void select( std::vector< size_t >& index, const S& selector ) const = 0;

   std::vector< T >           object_;
   std::vector< std::string > identifier_;

protected:

   std::vector< A >           attributes_;

}; // class SelectableObject



//**********************************************************************************************************************
/*!
*   This function is used to add an object together with its selection attributes stored in "attributes". Optionally,
*   an identifier string can be provided which is used during output (see "toStream" and "toString"). In debug mode,
*   this function triggers an assertion and fails if another object with the same selection attributes already exists.
*/
//**********************************************************************************************************************
template< typename T, typename A, typename S >
void SelectableObject<T,A,S>::add( const T& object, const A& attributes, const std::string& identifier ) {

   object_.push_back( object );
   identifier_.push_back( identifier.empty() ? std::string( "[anonymous]" ) : identifier );
   attributes_.push_back( attributes );
}

//**********************************************************************************************************************
/*!
*   Returns the number of objects matching the specified "selector".
 */
//**********************************************************************************************************************
template< typename T, typename A, typename S >
size_t SelectableObject<T,A,S>::getNumberOfMatching( const S& selector ) const {

   std::vector< size_t > index;

   select( index, selector );
   if( !index.empty() ) {
      WALBERLA_ASSERT_LESS( index[0], object_.size() );
   }

   return index.size();
}

//**********************************************************************************************************************
/*!
*   This function can be used to retrieve the one object whose attributes match with "selector". Depending on "selector"
*   and the actual implementation of the function "select", no object, one object, or multiple objects may be found. If
*   only one object is found, it is returned via the parameter "object". If multiple objects are found, the first object
*   whose attributes match is returned via "object". In any case, the number of objects that match with "selector" is
*   returned by this function - and only if the return value is equal to '1' exactly one object was found and stored in
*   "object".
*/
//**********************************************************************************************************************
template< typename T, typename A, typename S >
size_t SelectableObject<T,A,S>::get( T& object, const S& selector ) const {

   std::vector< size_t > index;

   select( index, selector );

   if( !index.empty() ) {
      WALBERLA_ASSERT_LESS( index[0], object_.size() );
      object = object_[ index[0] ];
   }

   return index.size();
}



//**********************************************************************************************************************
/*!
*   This function can be used to retrieve all objects whose attributes match with the selector "selector". The objects
*   are returned using the provided vector "objects" (this function may return none, exactly one, ore multiple objects
*   depending on "selector" and the actual implementation of the function "select").
*/
//**********************************************************************************************************************
template< typename T, typename A, typename S >
void SelectableObject<T,A,S>::get( std::vector<T>& object, const S& selector ) const {

   std::vector< size_t > index;

   select( index, selector );

   for( size_t i = 0; i != index.size(); ++i ) {
      WALBERLA_ASSERT_LESS( index[i], object_.size() );
      object.push_back( object_[ index[i] ] );
   }
}



//**********************************************************************************************************************
/*!
*   This function can be used to retrieve the one object whose attributes match with "selector". Depending on "selector"
*   and the actual implementation of the function "select", no object, one object, or multiple objects may be found. If
*   only one object is found, it is returned via the parameter "object". If multiple objects are found, the first object
*   whose attributes match is returned via "object". In any case, the number of objects that match with "selector" is
*   returned by this function - and only if the return value is equal to '1' exactly one object was found and stored in
*   "object". Additionally, the corresponding identifier is also returned via the parameter "identifier".
*/
//**********************************************************************************************************************
template< typename T, typename A, typename S >
size_t SelectableObject<T,A,S>::get( T& object, std::string& identifier, const S& selector ) const {

   std::vector< size_t > index;

   select( index, selector );

   if( !index.empty() ) {
      WALBERLA_ASSERT_LESS( index[0], object_.size() );
      object = object_[ index[0] ];
      identifier = identifier_[ index[0] ];
   }

   return index.size();
}



//**********************************************************************************************************************
/*!
*   This function can be used to retrieve all objects whose attributes match with the selector "selector". The objects
*   are returned using the provided vector "objects" (this function may return none, exactly one, ore multiple objects
*   depending on "selector" and the actual implementation of the function "select"). Additionally, for every object that
*   is found, its corresponding identifier is also returned via "identifier".
*/
//**********************************************************************************************************************
template< typename T, typename A, typename S >
void SelectableObject<T,A,S>::get( std::vector<T>& object, std::vector< std::string >& identifier, const S& selector ) const {

   std::vector< size_t > index;

   select( index, selector );

   for( size_t i = 0; i != index.size(); ++i ) {
      WALBERLA_ASSERT_LESS( index[i], object_.size() );
      object.push_back( object_[ index[i] ] );
      identifier.push_back( identifier_[ index[i] ] );
   }
}



//**********************************************************************************************************************
/*!
*   This function can be used to retrieve a pointer (!) to the object whose attributes match with "selector". Depending
*   on "selector" and the actual implementation of the function "select", no object, one object, or multiple objects may
*   be found. If no objects or multiple objects are found, NULL is returned.
*   Attention: The pointer may get invalidated by subsequent calls to the member function "add".
*/
//**********************************************************************************************************************
template< typename T, typename A, typename S >
const T* SelectableObject<T,A,S>::getUnique( const S& selector ) const {

   std::vector< size_t > index;

   select( index, selector );

   if( index.size() == 1 ) {
      WALBERLA_ASSERT_LESS( index[0], object_.size() );
      return &(object_[ index[0] ]);
   }

   return nullptr;
}


//**********************************************************************************************************************
/*!
*   Non-const version of getUnique().
*/
//**********************************************************************************************************************
template< typename T, typename A, typename S >
T* SelectableObject<T,A,S>::getUnique( const S& selector ) {

   const SelectableObject<T,A,S>& const_this = *this;
   return const_cast<T*>( const_this.getUnique( selector ) );
}



//**********************************************************************************************************************
/*!
*   This function is similar to getUnique(const S &selector) but takes an additional output parameter
*   identifier, which is set to the string identifier of the selected object, if a non-zero pointer is
*   returned, otherwise the identifier string is not changed
*/
//**********************************************************************************************************************
template< typename T, typename A, typename S >
const T* SelectableObject<T,A,S>::getUnique( const S& selector, std::string & identifier ) const {

   std::vector< size_t > index;

   select( index, selector );

   if( index.size() == 1 ) {
      WALBERLA_ASSERT_LESS( index[0], object_.size() );
      identifier = identifier_[ index[0] ];
      return &(object_[ index[0] ]);
   }

   return nullptr;
}


//**********************************************************************************************************************
/*!
*   Non-const version of getUnique(const S&, std::string& ).
*/
//**********************************************************************************************************************
template< typename T, typename A, typename S >
T* SelectableObject<T,A,S>::getUnique( const S& selector, std::string & identifier ) {

   const SelectableObject<T,A,S>& const_this = *this;
   return const_cast<T*>( const_this.getUnique( selector,identifier ) );
}





template< typename T, typename A, typename S >
void SelectableObject<T,A,S>::toStream( std::ostream& os ) const {

   WALBERLA_ASSERT_EQUAL( identifier_.size(), attributes_.size() );

   os << "[ ";

   for( size_t i = 0; i != identifier_.size(); ++i )
      os << "( " << identifier_[i] << " : " << attributes_[i] << ( ( i+1 == identifier_.size() ) ? " ) " : " ), " );

   os << "]";
}



template< typename T, typename A, typename S >
inline std::string SelectableObject<T,A,S>::toString() const {

   std::ostringstream oss;
   toStream( oss );

   return oss.str();
}



//////////////////////
// Global Functions //
//////////////////////



template< typename T, typename A, typename S >
inline std::ostream& operator<<( std::ostream& os, const SelectableObject<T,A,S>& selectableObject ) {

   selectableObject.toStream( os );
   return os;
}



} // namespace selectable
} // namespace walberla