A library for working with phylogenetic and population genetic data.
v0.27.0
lambda_iterator.hpp
Go to the documentation of this file.
1 #ifndef GENESIS_UTILS_CONTAINERS_LAMBDA_ITERATOR_H_
2 #define GENESIS_UTILS_CONTAINERS_LAMBDA_ITERATOR_H_
3 
4 /*
5  Genesis - A toolkit for working with phylogenetic data.
6  Copyright (C) 2014-2022 Lucas Czech
7 
8  This program is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program. If not, see <http://www.gnu.org/licenses/>.
20 
21  Contact:
22  Lucas Czech <lczech@carnegiescience.edu>
23  Department of Plant Biology, Carnegie Institution For Science
24  260 Panama Street, Stanford, CA 94305, USA
25 */
26 
28 
29 #include <cassert>
30 #include <functional>
31 #include <future>
32 #include <memory>
33 #include <stdexcept>
34 #include <string>
35 #include <utility>
36 #include <vector>
37 
38 namespace genesis {
39 namespace utils {
40 
41 // =================================================================================================
42 // Helpers
43 // =================================================================================================
44 
53 {};
54 
55 // =================================================================================================
56 // Lambda Iterator
57 // =================================================================================================
58 
149 template<class T, class D = EmptyLambdaIteratorData>
151 {
152 public:
153 
154  // -------------------------------------------------------------------------
155  // Member Types
156  // -------------------------------------------------------------------------
157 
159  using value_type = T;
160  using pointer = value_type const*;
161  using reference = value_type const&;
162  using difference_type = std::ptrdiff_t;
163  using iterator_category = std::input_iterator_tag;
164 
165  using Data = D;
166 
174  static size_t const DEFAULT_BLOCK_SIZE = 0;
175 
176  // ======================================================================================
177  // Internal Iterator
178  // ======================================================================================
179 
183  class Iterator
184  {
185  public:
186 
187  // -------------------------------------------------------------------------
188  // Constructors and Rule of Five
189  // -------------------------------------------------------------------------
190 
192  using value_type = T;
193  using pointer = value_type const*;
194  using reference = value_type const&;
195  using difference_type = std::ptrdiff_t;
196  using iterator_category = std::input_iterator_tag;
197 
198  using Data = D;
199 
207  Iterator() = default;
208 
209  private:
210 
217  Iterator(
218  LambdaIterator const* generator
219  )
220  : generator_( generator )
221  , current_block_( std::make_shared<std::vector<T>>() )
222  , buffer_block_( std::make_shared<std::vector<T>>() )
223  , thread_pool_( std::make_shared<utils::ThreadPool>( 1 ))
224  , future_( std::make_shared<std::future<size_t>>() )
225  {
226  // We use the generator as a check if this Iterator is intended to be a begin()
227  // or end() iterator. If its the former, init and get the first element block.
228  // We could also just use the default constructor to create end() iterators,
229  // this would have the same effect.
230  // After we are done iterating the input, we then set the generator_ to nullptr,
231  // as a sign that we are done. This allows us also to know if we reached end() without
232  // having to store the end() iterator when using this class.
233  if( generator_ ) {
234  if( ! generator_->get_element_ ) {
235  throw std::invalid_argument(
236  "Cannot use LambdaIterator without a function to get elements."
237  );
238  }
239 
240  // Initialize the current_block_ and buffer_block_,
241  // and read the first block(s) of the file.
242  init_();
243  }
244  }
245 
246  public:
247 
248  ~Iterator() = default;
249 
250  Iterator( self_type const& ) = default;
251  Iterator( self_type&& ) = default;
252 
253  Iterator& operator= ( self_type const& ) = default;
254  Iterator& operator= ( self_type&& ) = default;
255 
257 
258  // -------------------------------------------------------------------------
259  // Accessors
260  // -------------------------------------------------------------------------
261 
262  value_type const * operator->() const
263  {
264  assert( current_block_ );
265  assert( current_pos_ < end_pos_ );
266  assert( current_pos_ < current_block_->size() );
267  return &((*current_block_)[current_pos_]);
268  }
269 
271  {
272  assert( current_block_ );
273  assert( current_pos_ < end_pos_ );
274  assert( current_pos_ < current_block_->size() );
275  return &((*current_block_)[current_pos_]);
276  }
277 
278  value_type const & operator*() const
279  {
280  assert( current_block_ );
281  assert( current_pos_ < end_pos_ );
282  assert( current_pos_ < current_block_->size() );
283  return (*current_block_)[current_pos_];
284  }
285 
287  {
288  assert( current_block_ );
289  assert( current_pos_ < end_pos_ );
290  assert( current_pos_ < current_block_->size() );
291  return (*current_block_)[current_pos_];
292  }
293 
297  Data const& data() const
298  {
299  if( ! generator_ ) {
300  throw std::runtime_error(
301  "Cannot access default constructed or past-the-end LambdaIterator content."
302  );
303  }
304  assert( generator_ );
305  return generator_->data_;
306  }
307 
308  // We do not offer non-const access at the moment. If we did,
309  // the generator pointer stored in this iterator would need to be non-const as well.
310 
311  // /* *
312  // * @brief Access the data stored in the iterator.
313  // */
314  // Data& data()
315  // {
316  // if( ! generator_ ) {
317  // throw std::runtime_error(
318  // "Cannot access default constructed or past-the-end LambdaIterator content."
319  // );
320  // }
321  // assert( generator_ );
322  // return generator_->data_;
323  // }
324 
325  // -------------------------------------------------------------------------
326  // Iteration
327  // -------------------------------------------------------------------------
328 
330  {
331  increment_();
332  return *this;
333  }
334 
335  // self_type operator ++(int)
336  // {
337  // auto cpy = *this;
338  // increment_();
339  // return cpy;
340  // }
341 
346  operator bool() const
347  {
348  // We here rely on the fact that we set the generator_ to nullptr if this is either
349  // the end() iterator or if the iteration has reached its end after the data is done.
350  return generator_ != nullptr;
351  }
352 
362  bool operator==( self_type const& other ) const
363  {
364  // We compare the generators as a baseline - two past-the-end iterator shall
365  // always compare equal. If only one of them is past-the-end, they will compare false.
366  // Only if both are valid (not past-the-end) iterators, we compare their current
367  // position in the block - two iterators to the same position in the same block
368  // (using pointer comparison for the block - not actual data comparison) are equal.
369  if( ! generator_ || ! other.generator_ ) {
370  // generator_ is used as the indicator whether this is a past-the-end iterator
371  // (in which case it is a nullptr), or not.
372  return generator_ == other.generator_;
373  }
374  assert( generator_ && other.generator_ );
375  return current_block_ == other.current_block_ && current_pos_ == other.current_pos_;
376  }
377 
378  bool operator!=( self_type const& other ) const
379  {
380  return !(*this == other);
381  }
382 
383  // -------------------------------------------------------------------------
384  // Internal Members
385  // -------------------------------------------------------------------------
386 
387  private:
388 
389  void init_()
390  {
391  // Check that they are set up from the constructor.
392  assert( generator_ );
393  assert( current_block_ );
394  assert( buffer_block_ );
395 
396  // Edge case: no buffering.
397  // Block size zero indicates to use no buffering, so we just use a single element.
398  if( generator_->block_size_ == 0 ) {
399  // Make space for it, read the first element, and then we are done here.
400  current_block_->resize( 1 );
401  end_pos_ = 1;
402  increment_();
403  return;
404  }
405 
406  // Init the records and create empty VcfRecord to read into.
407  // The blocks have been initialized in the contructor already; assert this.
408  current_block_->resize( generator_->block_size_ );
409  buffer_block_->resize( generator_->block_size_ );
410  assert( current_block_->size() == generator_->block_size_ );
411  assert( buffer_block_->size() == generator_->block_size_ );
412 
413  // Read the first block synchronously,
414  // so that there is data initialized to be dereferenced.
415  end_pos_ = read_block_( generator_, current_block_, generator_->block_size_ );
416  assert( current_pos_ == 0 );
417 
418  // If there is less data than the block size, the file is already done.
419  // No need to start the async buffering, we can just get out of here.
420  if( end_pos_ < generator_->block_size_ ) {
421 
422  // Edge case: zero elements read. We are already done then.
423  if( end_pos_ == 0 ) {
424  generator_ = nullptr;
425  }
426  return;
427  }
428 
429  // If we are here, the first block was fully read,
430  // so we start the async worker thread to fill the buffer.
431  assert( end_pos_ == generator_->block_size_ );
432  fill_buffer_block_();
433  }
434 
435  void increment_()
436  {
437  // Make sure that all things are still in place.
438  assert( generator_ );
439  assert( current_block_ && current_block_->size() > 0 );
440 
441  // Make sure that sizes have not been changed in the parent class.
442  assert(
443  current_block_->size() == generator_->block_size_ ||
444  (
445  generator_->block_size_ == 0 &&
446  current_block_->size() == 1
447  )
448  );
449  assert( buffer_block_->size() == generator_->block_size_ );
450 
451  // Edge case: no buffering.
452  // Read the next element. If there is none, we are done.
453  if( generator_->block_size_ == 0 ) {
454  assert( current_block_->size() == 1 );
455  if( ! get_next_element_( generator_, (*current_block_)[0] )) {
456  generator_ = nullptr;
457  }
458  return;
459  }
460 
461  // Finish the reading (potentially waiting if not yet finished in the worker thread).
462  // The future returns how much data there was to be read, which we use as our status.
463  // After that, swap the buffer and start a new reading operation in the worker thread.
464 
465  // Move to the next element in the vector.
466  ++current_pos_;
467  assert( current_pos_ <= end_pos_ );
468 
469  // If we are at the end of the data, we are either done iterating,
470  // or reached the end of the current buffer block.
471  if( current_pos_ == end_pos_ ) {
472 
473  // If we did not get a full block size when reading, we are done iterating.
474  // Indicate this by unsetting the generator_ pointer.
475  if( end_pos_ < generator_->block_size_ ) {
476  generator_ = nullptr;
477  return;
478  }
479 
480  // If we are at the end of the record vector, and if that vector was full
481  // (complete block size), there is more data, so start reading.
482 
483  assert( end_pos_ == generator_->block_size_ );
484  assert( future_ );
485  assert( future_->valid() );
486 
487  // Get how many records were read into the buffer, which also waits for the reading
488  // if necessary. If we did not read any now, that means that the number of total
489  // elements is a multiple of the block size, which we handle as a special case here.
490  // There is probably some better way to restructure the code to avoid this edge
491  // case... but good enough for now.
492  end_pos_ = future_->get();
493  if( end_pos_ == 0 ) {
494  generator_ = nullptr;
495  return;
496  }
497 
498  // Here, we know that there is more data, so we can swap the buffer,
499  // start reading again, and set or internal current location
500  // to the first element of the vector again.
501  assert( end_pos_ > 0 && end_pos_ <= generator_->block_size_ );
502  std::swap( buffer_block_, current_block_ );
503  fill_buffer_block_();
504  current_pos_ = 0;
505  }
506  }
507 
508  void fill_buffer_block_()
509  {
510  // Those shared pointers have been initialized in the constructor; let's assert this.
511  assert( generator_ );
512  assert( thread_pool_ );
513  assert( future_ );
514 
515  // This function is only every called after we finished any previous operations,
516  // so let's assert that the thread pool and future are in the states that we expect.
517  assert( thread_pool_->load() == 0 );
518  assert( ! future_->valid() );
519 
520  // Make sure that sizes have not been changed in the parent class.
521  assert(
522  current_block_->size() == generator_->block_size_ ||
523  (
524  generator_->block_size_ == 0 &&
525  current_block_->size() == 1
526  )
527  );
528  assert( buffer_block_->size() == generator_->block_size_ );
529 
530  // In order to use lambda captures by copy for class member variables in C++11, we first
531  // have to make local copies, and then capture those. Capturing the class members direclty
532  // was only introduced later. Bit cumbersome, but gets the job done.
533  auto generator = generator_;
534  auto buffer_block = buffer_block_;
535  auto block_size = generator_->block_size_;
536 
537  // The lambda returns the result of read_block_ call, that is, the number of records
538  // that have been read, and which we later (in the future_) use to see how much data we got.
539  *future_ = thread_pool_->enqueue(
540  [ generator, buffer_block, block_size ](){
541  return read_block_( generator, buffer_block, block_size );
542  }
543  );
544  }
545 
549  static size_t read_block_(
550  LambdaIterator const* generator,
551  std::shared_ptr<std::vector<T>> buffer_block,
552  size_t block_size
553  ) {
554  // This is a static function that does not depend on the class member data, so that
555  // we can use it from the future lambda in the thread pool above without having to worry
556  // about lambda captures of `this` going extinct... which was an absolutely nasty bug to
557  // find! Such a rookie mistake! For that reason, we also take all arguments as shared
558  // pointers, so that they are kept alive while the thread pool is working.
559  // However, once its done with its work, the function (the one that we give to the thread
560  // pool with a lambda) is popped from the thread queue, so that the shared pointer can
561  // be freed again - that is, we do not need to worry about the lambda keeping the shared
562  // pointer from freeing its memory indefinitely.
563 
564  assert( generator );
565  assert( buffer_block->size() == block_size );
566 
567  // Read as long as there is data. Return number of read records.
568  size_t i = 0;
569  for( ; i < block_size; ++i ) {
570  if( ! get_next_element_( generator, (*buffer_block)[i] )) {
571  break;
572  }
573  }
574  return i;
575  }
576 
584  static bool get_next_element_(
585  LambdaIterator const* generator,
586  T& target
587  ) {
588  assert( generator );
589  bool usable_element = false;
590  while( true ) {
591  // Get the next element from the input source.
592  bool const got_element = generator->get_element_( target );
593 
594  if( got_element ) {
595  // If this is an element (not yet at the end of the data),
596  // apply all transforms and filters, and get out of here if all of them
597  // return `true`, that is, if none of them wants to filter out the element.
598  // If however one of them returns `false`, we need to find another element.
599  usable_element = true;
600  for( auto const& tra_fil : generator->transforms_and_filters_ ) {
601  usable_element = tra_fil( target );
602  if( ! usable_element ) {
603  // If one of the transforms/filters does not want us to continue,
604  // we do not call the others. Break out of the inner loop.
605  break;
606  }
607  }
608 
609  // If we got out of the above loop without any failing filters,
610  // we have fonud an element, so let's move it to our target.
611  if( usable_element ) {
612  // We already checked that got_element holds data, so no need to repeat,
613  // just assert it. Done here, break out of the while loop.
614  assert( got_element );
615  break;
616  }
617  // Else (usable_element == false): Loop again, try the next element.
618 
619  } else {
620  // If this is not a valid element, we reached the end of the input.
621  // We note this in the return value, then break out of the while loop.
622  assert( ! got_element );
623  usable_element = false;
624  break;
625  }
626  }
627  return usable_element;
628  }
629 
630  private:
631 
632  // Parent.
633  LambdaIterator const* generator_ = nullptr;
634 
635  // Buffer buffering data, and positions in it.
636  std::shared_ptr<std::vector<T>> current_block_;
637  std::shared_ptr<std::vector<T>> buffer_block_;
638  size_t current_pos_ = 0;
639  size_t end_pos_ = 0;
640 
641  // Thread pool to run the buffering in the background.
642  // Also, store the future_ used to keep track of the background task. It returns the number of
643  // elements that have been read into the buffer (block_size_, or less at the end of the file).
644  std::shared_ptr<utils::ThreadPool> thread_pool_;
645  std::shared_ptr<std::future<size_t>> future_;
646 
647  };
648 
649  // ======================================================================================
650  // Main Class
651  // ======================================================================================
652 
653  // -------------------------------------------------------------------------
654  // Constructors and Rule of Five
655  // -------------------------------------------------------------------------
656 
657  LambdaIterator() = default;
658 
667  std::function<bool(value_type&)> get_element,
669  )
670  : get_element_(get_element)
671  , block_size_( block_size )
672  {}
673 
682  std::function<bool(value_type&)> get_element,
683  Data const& data,
685  )
686  : get_element_(get_element)
687  , data_(data)
688  , block_size_( block_size )
689  {}
690 
697  std::function<bool(value_type&)> get_element,
698  Data&& data,
700  )
701  : get_element_(get_element)
702  , data_( std::move( data ))
703  , block_size_( block_size )
704  {}
705 
706  ~LambdaIterator() = default;
707 
708  LambdaIterator( self_type const& ) = default;
709  LambdaIterator( self_type&& ) = default;
710 
711  self_type& operator= ( self_type const& ) = default;
712  self_type& operator= ( self_type&& ) = default;
713 
714  friend Iterator;
715 
716  // -------------------------------------------------------------------------
717  // Iteration
718  // -------------------------------------------------------------------------
719 
721  {
722  return Iterator( this );
723  }
724 
726  {
727  return Iterator( nullptr );
728  }
729 
734  operator bool() const
735  {
736  return static_cast<bool>( get_element_ );
737  }
738 
742  Data const& data() const
743  {
744  return data_;
745  }
746 
751  {
752  return data_;
753  }
754 
755  // -------------------------------------------------------------------------
756  // Filters and Transformations
757  // -------------------------------------------------------------------------
758 
767  self_type& add_transform( std::function<void(T&)> transform )
768  {
769  transforms_and_filters_.push_back(
770  [transform]( T& element ){
771  transform( element );
772  return true;
773  }
774  );
775  return *this;
776  }
777 
785  self_type& add_filter( std::function<bool(T const&)> filter )
786  {
787  transforms_and_filters_.push_back(
788  [filter]( T& element ){
789  return filter( element );
790  }
791  );
792  return *this;
793  }
794 
805  self_type& add_transform_filter( std::function<bool(T&)> filter )
806  {
807  transforms_and_filters_.push_back(
808  [filter]( T& element ){
809  return filter( element );
810  }
811  );
812  return *this;
813  }
814 
819  {
820  transforms_and_filters_.clear();
821  }
822 
823  // -------------------------------------------------------------------------
824  // Other Settings
825  // -------------------------------------------------------------------------
826 
830  size_t block_size() const
831  {
832  return block_size_;
833  }
834 
844  self_type& block_size( size_t value )
845  {
846  block_size_ = value;
847  return *this;
848  }
849 
850  // -------------------------------------------------------------------------
851  // Data Members
852  // -------------------------------------------------------------------------
853 
854 private:
855 
856  // std::vector<std::function<void(T&)>> transforms_;
857  // std::vector<std::function<bool(T const&)>> filters_;
858  std::vector<std::function<bool(T&)>> transforms_and_filters_;
859 
860  // Underlying iterator and associated data.
861  std::function<bool( value_type& )> get_element_;
862  Data data_;
863 
864  // Block buffering settings.
865  size_t block_size_ = DEFAULT_BLOCK_SIZE;
866 
867 };
868 
869 } // namespace utils
870 } // namespace genesis
871 
872 #endif // include guard
genesis::placement::swap
void swap(Sample &lhs, Sample &rhs)
Definition: sample.cpp:104
genesis::utils::LambdaIterator
Type erasure for iterators, using std::function to eliminate the underlying input type.
Definition: lambda_iterator.hpp:150
genesis::utils::LambdaIterator::LambdaIterator
LambdaIterator(std::function< bool(value_type &)> get_element, Data &&data, size_t block_size=DEFAULT_BLOCK_SIZE)
Create an iterator over some underlying content.
Definition: lambda_iterator.hpp:696
genesis::utils::LambdaIterator::difference_type
std::ptrdiff_t difference_type
Definition: lambda_iterator.hpp:162
genesis::utils::LambdaIterator::~LambdaIterator
~LambdaIterator()=default
genesis::utils::LambdaIterator::add_filter
self_type & add_filter(std::function< bool(T const &)> filter)
Add a filter function that is applied to each element of the iteration.
Definition: lambda_iterator.hpp:785
genesis::utils::LambdaIterator::add_transform
self_type & add_transform(std::function< void(T &)> transform)
Add a transformation function that is applied to each element of the iteration.
Definition: lambda_iterator.hpp:767
genesis::utils::LambdaIterator::Data
D Data
Definition: lambda_iterator.hpp:165
genesis::utils::EmptyLambdaIteratorData
Empty helper data struct to serve as a dummy for LambdaIterator.
Definition: lambda_iterator.hpp:52
genesis::utils::LambdaIterator::Iterator
friend Iterator
Definition: lambda_iterator.hpp:714
genesis::utils::LambdaIterator::Iterator::data
Data const & data() const
Access the data stored in the iterator.
Definition: lambda_iterator.hpp:297
genesis::utils::LambdaIterator::value_type
T value_type
Definition: lambda_iterator.hpp:159
genesis::utils::LambdaIterator::Iterator::operator->
value_type * operator->()
Definition: lambda_iterator.hpp:270
genesis::utils::LambdaIterator::LambdaIterator
LambdaIterator()=default
genesis::utils::LambdaIterator::begin
Iterator begin()
Definition: lambda_iterator.hpp:720
genesis::utils::LambdaIterator::operator=
self_type & operator=(self_type const &)=default
genesis::utils::LambdaIterator::Iterator::pointer
value_type const * pointer
Definition: lambda_iterator.hpp:193
genesis::utils::LambdaIterator::end
Iterator end()
Definition: lambda_iterator.hpp:725
genesis::utils::LambdaIterator::Iterator
Internal iterator over the data.
Definition: lambda_iterator.hpp:183
genesis::utils::LambdaIterator::Iterator::Data
D Data
Definition: lambda_iterator.hpp:198
genesis::utils::LambdaIterator::Iterator::iterator_category
std::input_iterator_tag iterator_category
Definition: lambda_iterator.hpp:196
genesis::utils::LambdaIterator::Iterator::operator=
Iterator & operator=(self_type const &)=default
genesis::utils::LambdaIterator::Iterator::value_type
T value_type
Definition: lambda_iterator.hpp:192
genesis::utils::ThreadPool
Thread pool for distributed work.
Definition: thread_pool.hpp:77
genesis::utils::LambdaIterator::data
Data const & data() const
Access the data stored in the iterator.
Definition: lambda_iterator.hpp:742
genesis::utils::LambdaIterator::Iterator::operator*
const value_type & operator*() const
Definition: lambda_iterator.hpp:278
genesis::utils::LambdaIterator::LambdaIterator
LambdaIterator(std::function< bool(value_type &)> get_element, size_t block_size=DEFAULT_BLOCK_SIZE)
Create an iterator over some underlying content.
Definition: lambda_iterator.hpp:666
genesis::utils::LambdaIterator::Iterator::reference
value_type const & reference
Definition: lambda_iterator.hpp:194
genesis
Container namespace for all symbols of genesis in order to keep them separate when used as a library.
Definition: placement/formats/edge_color.cpp:42
genesis::utils::LambdaIterator::Iterator::operator==
bool operator==(self_type const &other) const
Compare two iterators for equality.
Definition: lambda_iterator.hpp:362
genesis::utils::LambdaIterator::Iterator::operator->
const value_type * operator->() const
Definition: lambda_iterator.hpp:262
genesis::utils::LambdaIterator::Iterator::operator++
self_type & operator++()
Definition: lambda_iterator.hpp:329
genesis::utils::LambdaIterator::block_size
self_type & block_size(size_t value)
Set the block size used for buffering the input data.
Definition: lambda_iterator.hpp:844
genesis::utils::LambdaIterator::Iterator::Iterator
Iterator()=default
Default constructor for empty (past-the-end) data.
genesis::utils::LambdaIterator::iterator_category
std::input_iterator_tag iterator_category
Definition: lambda_iterator.hpp:163
genesis::utils::LambdaIterator::reference
value_type const & reference
Definition: lambda_iterator.hpp:161
genesis::utils::LambdaIterator::DEFAULT_BLOCK_SIZE
static const size_t DEFAULT_BLOCK_SIZE
Default size for block buffering.
Definition: lambda_iterator.hpp:174
genesis::utils::LambdaIterator::LambdaIterator
LambdaIterator(std::function< bool(value_type &)> get_element, Data const &data, size_t block_size=DEFAULT_BLOCK_SIZE)
Create an iterator over some underlying content.
Definition: lambda_iterator.hpp:681
genesis::utils::LambdaIterator::Iterator::~Iterator
~Iterator()=default
genesis::utils::LambdaIterator::block_size
size_t block_size() const
Get the currenlty set block size used for buffering the input data.
Definition: lambda_iterator.hpp:830
genesis::utils::LambdaIterator::Iterator::LambdaIterator
friend LambdaIterator
Definition: lambda_iterator.hpp:256
genesis::utils::LambdaIterator::Iterator::operator!=
bool operator!=(self_type const &other) const
Definition: lambda_iterator.hpp:378
genesis::utils::LambdaIterator::self_type
LambdaIterator self_type
Definition: lambda_iterator.hpp:158
thread_pool.hpp
genesis::utils::LambdaIterator::Iterator::operator*
value_type & operator*()
Definition: lambda_iterator.hpp:286
genesis::utils::LambdaIterator::add_transform_filter
self_type & add_transform_filter(std::function< bool(T &)> filter)
Add a transformation and filter function that is applied to each element of the iteration.
Definition: lambda_iterator.hpp:805
genesis::utils::LambdaIterator::pointer
value_type const * pointer
Definition: lambda_iterator.hpp:160
genesis::utils::LambdaIterator::Iterator::difference_type
std::ptrdiff_t difference_type
Definition: lambda_iterator.hpp:195
genesis::utils::LambdaIterator::data
Data & data()
Access the data stored in the iterator.
Definition: lambda_iterator.hpp:750
genesis::utils::LambdaIterator::clear_filters_and_transformations
self_type & clear_filters_and_transformations()
Clear all filters and transformations.
Definition: lambda_iterator.hpp:818