LUMIERA.clone/src/lib/iter-source.hpp

501 lines
15 KiB
C++
Raw Normal View History

2010-01-05 02:53:20 +01:00
/*
2025-06-07 23:59:57 +02:00
ITER-SOURCE.hpp - an interface to build an opaque iterator-based data source
2010-12-17 23:28:49 +01:00
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
Copyright (C)
2010, Hermann Vosseler <Ichthyostega@web.de>
2010-12-17 23:28:49 +01:00
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
  **Lumiera** 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. See the file COPYING for further details.
2010-12-17 23:28:49 +01:00
2010-01-05 02:53:20 +01:00
*/
/** @file iter-source.hpp
** Extension module to build an opaque data source, accessible as
** **Lumiera Forward Iterator**. It is based on combining an IterAdapter
2010-01-05 02:53:20 +01:00
** with classical polymorphism; here, the data source, which is addressed
** by IderAdapter through the "iteration control API", is abstracted behind
** an interface (with virtual functions). Together this allows to build
** a simple data source type, without needing to disclose details of
** the implementation.
**
** @todo the design used for the "iteration control API" is misaligned
** with the purpose of this adapter. Rather, it should be shaped
** similar to IterStateWrapper with three control functions //////////////////////////////////////TICKET #1125
**
** # Standard Adapters
** As a complement, this header contains a generic implementation
** of the IterSource interface by wrapping an existing Lumiera Forward Iterator.
** Using this WrappedLumieraIter, the details of this wrapped source iterator
** remain opaque. To ease the use of this adapter, a selection of free functions
** is provided, allowing to build opaque "all elements" or "all keys" iterators
2025-06-07 23:59:57 +02:00
** for various STL containers.
**
2010-01-05 02:53:20 +01:00
** @see iter-adapter.hpp
** @see itertool.hpp
** @see iter-source-test.cpp
**
*/
#ifndef LIB_ITER_SOURCE_H
#define LIB_ITER_SOURCE_H
#include "lib/meta/util.hpp"
#include "lib/meta/function.hpp"
2010-01-05 02:53:20 +01:00
#include "lib/iter-adapter.hpp"
#include "lib/itertools.hpp"
#include "lib/nocopy.hpp"
2010-01-05 02:53:20 +01:00
#include <type_traits>
#include <utility>
#include <string>
#include <memory>
2010-01-05 04:10:23 +01:00
2010-01-05 02:53:20 +01:00
namespace lib {
using std::string;
using std::shared_ptr;
using std::forward;
2010-01-05 02:53:20 +01:00
/**
* Iteration source interface to abstract a data source,
* which then can be accessed through IterAdapter as a frontend,
* allowing to pull individual elements until exhaustion.
*
* This base class is empty and makes no assumptions regarding
2010-01-05 04:10:23 +01:00
* identity, instantiation and copying.
*
2010-01-05 02:53:20 +01:00
* @see PlacementIndex::Table#_eachEntry_4check usage example
* @see iter-source-test.cpp
*/
2010-01-05 04:10:23 +01:00
template<typename TY>
2010-01-05 02:53:20 +01:00
class IterSource
{
2010-01-05 04:10:23 +01:00
protected: /* == data source API to implement == */
2010-01-05 04:10:23 +01:00
2025-06-07 23:59:57 +02:00
using Pos = TY*;
using DataHandle = shared_ptr<IterSource>;
2010-01-05 04:10:23 +01:00
///////////////////////////////////////////////TICKET #1125 : this API should use three control functions, similar to IterStateWrapper
2010-01-05 04:10:23 +01:00
/** iteration start: prepare the first element.
* may return NULL in case of empty data source
*/
virtual Pos firstResult () =0;
/** iteration step: switch on to the next element.
* The pos pointer should be set to NULL to report
2010-01-05 04:10:23 +01:00
* iteration end
*/
virtual void nextResult(Pos& pos) =0;
/** disconnect the data source / iteration frontend.
* Depending on the way the IterSource got created
* this \em might (or might not) be followed by
* destroying the data source.
* @note must not throw
*/
virtual void disconnect () { }
public:
virtual ~IterSource() { }; ///< is ABC
virtual
operator string() const ///< subclasses may offer diagnostics
{
return "IterSource<" + lib::meta::typeStr<TY>()+ ">";
}
2010-01-05 04:10:23 +01:00
/* == Iteration control API for IterAdapter frontend == */
friend bool ////////////////////////////////////////TICKET #1125 : this API should use three control functions, similar to IterStateWrapper
checkPoint (DataHandle const&, Pos const& pos)
2010-01-05 04:10:23 +01:00
{
return bool(pos);
}
friend void
iterNext (DataHandle& source, Pos& pos)
{
ASSERT (source);
2010-01-05 04:10:23 +01:00
source->nextResult(pos);
}
/* == public builder API to create instances == */
struct iterator
: IterAdapter<Pos, DataHandle>
{ ////////////////////////////////////////////////////TICKET #1125 : should be build on top of IterStateWrapper rather than IterAdapter!
using _I = IterAdapter<Pos, DataHandle>;
using _I::IterAdapter;
operator string() const {return _I::source()? string(*_I::source()) : "⟂"; }
};
2010-01-05 04:10:23 +01:00
/** build an iterator frontend for the given source,
* @note the source is allocated separately and
* \em not owned by the iterator frontend
*/
2010-01-05 04:10:23 +01:00
static iterator
build (IterSource& sourceImpl)
{
return startIteration (DataHandle{&sourceImpl, &detach_without_destroy});
}
/** build an iterator frontend, thereby managing
* the given heap allocated source object instance.
* @note we take ownership and destroy the source
* when the last copy of the created iterator
* goes out of scope.
*/
static iterator
build (IterSource* sourceImplObject)
{
return startIteration (DataHandle{sourceImplObject, &destroy_managed_source});
2010-01-05 04:10:23 +01:00
}
static iterator EMPTY_SOURCE;
using value_type = TY;
using reference = TY&;
using pointer = TY*;
private:
static iterator
startIteration (DataHandle sourceHandle)
{
REQUIRE (sourceHandle);
Pos first = sourceHandle->firstResult();
return {move(sourceHandle), first};
}
static void
detach_without_destroy (IterSource * source)
{
WARN_IF (!source, library, "IterSource deleter called with NULL source pointer");
if (source)
source->disconnect();
}
static void
destroy_managed_source (IterSource * source)
{
WARN_IF (!source, library, "IterSource deleter called with NULL source pointer");
if (source)
{
source->disconnect();
delete source;
}
}
2010-01-05 02:53:20 +01:00
};
2010-01-05 04:10:23 +01:00
/** storage for the empty data-source constant */
template<typename TY>
IterSource<TY>::iterator IterSource<TY>::EMPTY_SOURCE = iterator();
2010-01-05 04:10:23 +01:00
/**
* Standard implementation of the IterSource interface:
* a wrapped "Lumiera Forward Iterator". Usually, such a wrapper instance
* is passed to one of the IterSource's builder functions, thereby
* erasing the specific type information of the template parameter IT
*/
template<class IT, class ISO = IterSource<typename meta::ValueTypeBinding<IT>::value_type>>
class WrappedLumieraIter
: public ISO
, util::NonCopyable
{
IT src_;
protected:
using Pos = ISO::Pos;
Pos ////////////////////////////////////////////////////TICKET #1125 : this API should use three control functions, similar to IterStateWrapper
firstResult ()
{
if (!src_)
return 0;
else
return & *src_;
}
void
nextResult (Pos& pos)
{
if (!pos) return;
if (src_) ++src_;
if (src_)
pos = & *src_;
else
pos = 0;
}
public:
WrappedLumieraIter (IT const& orig)
: src_(orig)
{ }
WrappedLumieraIter (IT&& orig)
: src_(forward<IT>(orig))
{ }
protected:
IT& wrappedIter() { return src_; }
IT const& wrappedIter() const { return src_; }
};
2010-01-05 04:10:23 +01:00
/* === pre-defined Adapters for frequently used Containers === */
namespace iter_source {
namespace { // traits and helpers...
2010-01-05 04:10:23 +01:00
template<class CON>
struct _SeqT
{
using Val = CON::iterator::value_type;
using Iter = IterSource<Val>::iterator;
};
template<class IT>
struct _RangeT
{
using Val = IT::value_type;
using Iter = IterSource<Val>::iterator;
};
template<class MAP>
struct _MapT
{
using Key = MAP::key_type;
using Val = MAP::value_type::second_type;
using KeyIter = IterSource<Key>::iterator;
using ValIter = IterSource<Val>::iterator;
};
template<class IT>
struct _IterT
{
using Src = std::remove_reference<IT>::type;
using Val = Src::value_type;
using Iter = IterSource<Val>::iterator;
};
template<class IT, class FUN>
struct _TransformIterT
{
using Src = std::remove_reference<IT>::type;
using ResVal = lib::meta::_Fun<FUN>::Ret;
using TransIter = decltype(lib::transformIter (std::declval<IT>(), std::declval<FUN>()));
using Iter = IterSource<ResVal>::iterator;
};
template<class IT>
struct _PairIterT
{
using Src = std::remove_reference<IT>::type;
using PairType = Src::value_type;
using ValType = PairType::second_type;
using ConstKeyType = PairType::first_type;
// since we're returning the keys always by value,
// we can strip the const added by the STL map types
using KeyType = std::remove_const<ConstKeyType>::type;
static KeyType takeFirst (PairType const& pair) { return pair.first; }
static ValType takeSecond(PairType const& pair) { return pair.second;}
};
template<class IT>
auto
takePairFirst (IT&& source)
{
return transformIter (forward<IT>(source), _PairIterT<IT>::takeFirst );
}
template<class IT>
auto
takePairSecond (IT&& source)
2010-01-05 04:10:23 +01:00
{
return transformIter (forward<IT>(source), _PairIterT<IT>::takeSecond );
}
} //(END) type helpers...
/** wraps a given Lumiera Forward Iterator,
* exposing just a IterSource based frontend.
*/
template<class IT>
_IterT<IT>::Iter
wrapIter (IT&& source)
{
using Src = _IterT<IT>::Src;
using Val = _IterT<IT>::Val;
return IterSource<Val>::build (new WrappedLumieraIter<Src> (forward<IT>(source)));
}
2010-01-05 04:10:23 +01:00
/** an IterSource frontend to return just a single value once.
* @warning behind the scenes, a heap allocation is managed by shared_ptr,
* to maintain a copy of the wrapped element. When passing a reference,
* only a reference will be wrapped, but a heap allocation happens nonetheless
*/
template<typename VAL>
auto
singleVal (VAL&& something)
{
using Src = decltype(singletonIter (forward<VAL>(something)));
using Val = _IterT<Src>::Val;
return IterSource<Val>::build (new WrappedLumieraIter<Src>{singletonIter (forward<VAL>(something))});
}
/** pipes a given Lumiera Forward Iterator through
* a transformation function and wraps the resulting
* transforming Iterator, exposing just an IterSource.
* This convenience shortcut can be used to build a
* processing chain; the resulting IterSource will
* hide any detail types involved.
* @note as with any IterSource, there is one virtual
* function call for every fetched element.
*/
template<class IT, class FUN>
_TransformIterT<IT,FUN>::Iter
transform (IT&& source, FUN processingFunc)
{
using ValType = _TransformIterT<IT,FUN>::ResVal;
using TransIT = _TransformIterT<IT,FUN>::TransIter;
return IterSource<ValType>::build (
new WrappedLumieraIter<TransIT> (
transformIter (forward<IT>(source), processingFunc)));
}
/** @return a Lumiera Forward Iterator to yield
* all the keys of the given Map or Hashtable
*/
2010-01-05 04:10:23 +01:00
template<class MAP>
auto
eachMapKey (MAP& map)
2010-01-05 04:10:23 +01:00
{
using Range = RangeIter<typename MAP::iterator>;
Range contents (map.begin(), map.end());
return wrapIter (takePairFirst (contents));
2010-01-05 04:10:23 +01:00
}
/** @return a Lumiera Forward Iterator to yield
* all the values of the given Map or Hashtable
*/
template<class MAP>
_MapT<MAP>::ValIter
2010-01-06 04:14:16 +01:00
eachMapVal (MAP& map)
{
using Range = RangeIter<typename MAP::iterator>;
Range contents (map.begin(), map.end());
return wrapIter (takePairSecond(contents));
}
/** @return a Lumiera Forward Iterator to yield
* all \em distinct keys of a Multimap
* @warning we do a full table scan to find
* the distinct keys
*/
template<class MAP>
auto
eachDistinctKey (MAP& map)
2010-01-05 04:10:23 +01:00
{
using Range = RangeIter<typename MAP::iterator>;
Range contents (map.begin(), map.end());
return wrapIter (filterRepeat (takePairFirst(contents)));
2010-01-05 04:10:23 +01:00
}
/** @return a Lumiera Forward Iterator to yield all values
* associated to the given key within this Map or Multimap
* @note obviously in case of a Map we'll get at most one result.
*/
template<class MAP>
_MapT<MAP>::ValIter
eachValForKey (MAP& map, typename _MapT<MAP>::Key key)
{
using Pos = MAP::iterator;
using Range = RangeIter<Pos>;
std::pair<Pos,Pos> valuesForKey = map.equal_range(key);
Range contents (valuesForKey.first, valuesForKey.second);
return wrapIter (takePairSecond(contents));
}
/** @param container a STL-like container, providing
2025-06-07 23:59:57 +02:00
* - a typedef `iterator`
* - functions `begin()` and `end()`
* @return a Lumiera Forward Iterator yielding all values
* starting with \c begin and excluding \c end .
*/
template<class CON>
_SeqT<CON>::Iter
eachEntry (CON& container)
2010-01-05 04:10:23 +01:00
{
using ValType = _SeqT<CON>::Val;
using Range = RangeIter<typename CON::iterator>;
Range contents (container.begin(), container.end());
return IterSource<ValType>::build (new WrappedLumieraIter<Range>(contents));
2010-01-05 04:10:23 +01:00
}
/** @return a Lumiera Forward Iterator to yield all values
* defined by a classical Iterator range.
*/
template<class IT>
_RangeT<IT>::Iter
eachEntry (IT const& begin, IT const& end)
{
using ValType = _RangeT<IT>::Val;
using Range = RangeIter<IT>;
Range contents (begin, end);
return IterSource<ValType>::build (new WrappedLumieraIter<Range>(contents));
}
2010-01-05 04:10:23 +01:00
}} // namespace lib::iter_source
2010-01-05 02:53:20 +01:00
#endif