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

329 lines
8.3 KiB
C++
Raw Normal View History

/*
2025-06-07 23:59:57 +02:00
ITER-STACK.hpp - a stack which can be popped by iterating
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)
2012, Hermann Vosseler <Ichthyostega@web.de>
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.
*/
/** @file iter-stack.hpp
** Conveniently iterable stack and queue containers.
** Implemented as thin wrapper on top of std::deque
** In addition to providing a simplified interface, these wrappers comply
** to the "Lumiera Forward Iterator" concept, allowing to retrieve and discharge the
** contents of the container in a standard fashion, especially by using util::for_each,
** or by feeding them into a iterator based pipeline.
**
** \par the builder extension point
** while Lumiera Forward Iterators only allow to retrieve some elements once,
** the special extension point \c build(sequence) allows abstracted access to some
** limited manipulation of the sequence, without tying the client to a specific
** sequence or iterator implementation. This is intended for library templates,
** which typically can't make any assumptions regarding the iterator types
** used for instantiation.
**
** @remarks contrary to the usual (STL) containers, this wrapper \em is an iterator
** and a container at the same time. Which opens the interesting possibility
** to intermix retrieval and feeding of new elements. It can be seen as a
** zero overhead adapter to ease the use within Lumiera's library framework.
2025-06-07 23:59:57 +02:00
** @note EX_STRONG, since std::deque gives this guarantee for push and pop operations
** @warning STL containers aren't thread safe.
**
** @see IterStack_test
** @see IterQueue_test
** @see iter-adapter.hpp
** @see itertools.hpp
** @see IterSource (completely opaque iterator)
**
*/
#ifndef LIB_ITER_STACK_H
#define LIB_ITER_STACK_H
#include "lib/error.hpp"
#include "lib/iter-adapter.hpp"
#include "lib/nocopy.hpp"
#include "lib/util.hpp"
#include <deque>
#include <utility>
namespace lib {
using util::unConst;
namespace iter {
/**
* Wrapper to mark a std::deque instance for use
* as "state core" within lib::IterStateWrapper.
* Iteration will pop elements from the back side.
*/
template<class TY>
struct IterDequeStorage
: std::deque<TY>
{
/* === Iteration control API for IterStateWrapper == */
bool
checkPoint() const
{
return not this->empty();
}
TY &
yield() const
{
REQUIRE (not this->empty());
return unConst(this)->back();
}
void
iterNext()
{
REQUIRE (not this->empty());
this->pop_back();
}
};
} // namespace lib::iter
/**
* A Stack which can be popped by iterating.
* This is a simple helper built by wrapping up
* STL's double ended queue (deque).
* Thus, each instance holds the full state, which is
* actually kept in heap allocated storage. Pushing of
* new elements and iterator use may be mixed.
*
* Contrary to just using std::stack
* - the iteration is compliant to the Lumiera Forward Iterator concept
* - there is a simplified #pop() function which removes and returns at once
*/
template<class TY>
struct IterStack
: IterStateWrapper<iter::IterDequeStorage<TY>>
{
// using default create and copy operations
IterStack&
push (TY const& elm)
{
this->stateCore().push_back (elm);
return *this;
}
IterStack&
push (TY&& elm)
{
this->stateCore().emplace_back (std::move (elm));
return *this;
}
IterStack&
insert (TY const& elm)
{
return push(elm);
}
TY
pop()
{
this->__throw_if_empty();
TY topElement (std::move (this->stateCore().back()));
this->stateCore().pop_back();
return topElement;
}
void
clear()
{
this->stateCore().clear();
}
/* == diagnostics == */
size_t
size() const
{
return unConst(this)->stateCore().size();
}
bool
empty() const
{
return 0 == size();
}
};
2025-06-07 23:59:57 +02:00
/**
* A Queue which can be pulled by iterating.
* As a variation of the IterStack frontend, here the
* exposed additional interface works like a queue:
* New elements can be fed, and the iteration will
* consume the contents first-in first-out. The
* implementation is based on std::deque, providing
* both feed and the iterating operation in amortised
* constant time, using chunk wise heap allocations.
* Feeding of new elements into the queue and
2025-06-07 23:59:57 +02:00
* retrieving old elements by iteration
* may be mixed freely.
*/
template<class TY>
struct IterQueue
: IterStateWrapper<iter::IterDequeStorage<TY>>
{
// using default create and copy operations
IterQueue&
feed (TY const& elm)
{
this->stateCore().push_front (elm);
return *this;
}
IterQueue&
feed (TY&& elm)
{
this->stateCore().emplace_front (std::move (elm));
return *this;
}
IterQueue&
insert (TY const& elm)
{
return feed(elm);
}
TY
pop()
{
this->__throw_if_empty();
TY firstElement (std::move (this->stateCore().back()));
this->stateCore().pop_back();
return firstElement;
}
/* == diagnostics == */
size_t
size() const
{
return unConst(this)->stateCore().size();
}
bool
empty() const
{
return 0 == size();
}
2025-06-07 23:59:57 +02:00
/**
* Adapter for use as opaque sequence.
* This builder exposes generic operations
* to prepare and pre-fill a sequence
*/
struct Builder
Invocation: develop a concept for handling parameter data ...as part of the rendering process, executed on top of the low-level-model (Render Node network) as conceived thus far. Parameter handling could be ''encoded'' into render nodes altogether, or, at the other extreme, an explicit parameter handling could be specified as part of the Render Node execution. As both extremes will lead to some unfavourable consequences, I am aiming at a middle ground: largely, the ''automation computation'' will be encoded and hidden within the network, implying that this topic remains to be addressed as part of defining the Builder semantics and implementation. Yet in part the required processing structure can be foreseen at an abstract level, and thus the essential primitive operations are specified explicitly as part of the Render Node definition. Notably the ''standard Weaving Pattern'' will include a ''parameter tuple'' into each `FeedManifold` and require a binding function, which accepts this tuple as first argument. Moreover — at implementation level, a library facility must be provided to support handling of ''arbitrary heterogeneous data values'' embedded directly into stack frame memory, together with a type-safe compile-time overlay, which allows the builder to embed specific ''accessor handles'' into functor bindings, even while the actual storage location is not yet known at that time (obviously, as being located on the stack). __Note__: a recurring topic is how to return descriptor objects from builder functions; for this purpose, I am adjusting the semantics of `lib/nocopy.hpp` to be more specific...
2024-12-09 21:55:48 +01:00
: util::NonCopyable
{
Builder(IterQueue& initialElements)
: queue_(initialElements)
{ }
// standard copy operations allowed
template<typename IT>
IterQueue &
usingSequence (IT src)
{
for ( ; src; ++src )
queue_.feed (*src);
return queue_;
}
IterQueue &
wrapping (TY const& elm)
{
queue_.feed (elm);
return queue_;
}
private:
IterQueue& queue_;
};
/** Extension point to be picked up by ADL.
* The exposed Builder object allows client code
* to prepare and pre-fill the queue, without
* being tied to the implementation type
* @param initial the initial contents of the queue
* to start with, maybe empty. This parameter
* also acts as type tag to pick this \c build()
2025-06-07 23:59:57 +02:00
* function by ADL
*/
friend IterQueue::Builder
build (IterQueue& initial)
{
return Builder(initial);
}
};
/** convenience free function to build an iterable sequence */
template<typename T>
inline IterQueue<T>
elements (T const& elm)
{
return IterQueue<T>().feed(elm);
}
template<typename T>
inline IterQueue<T>
elements (T const& e0, T const& e1)
{
return IterQueue<T>().feed(e0).feed(e1);
}
template<typename T>
inline IterQueue<T>
elements (T const& e0, T const& e1, T const& e2)
{
return IterQueue<T>().feed(e0).feed(e1).feed(e2);
}
template<typename T>
inline IterQueue<T>
elements (T const& e0, T const& e1, T const& e2, T const& e3)
{
return IterQueue<T>().feed(e0).feed(e1).feed(e2).feed(e3);
}
template<typename T>
inline IterQueue<T>
elements (T const& e0, T const& e1, T const& e2, T const& e3, T const& e4)
{
return IterQueue<T>().feed(e0).feed(e1).feed(e2).feed(e3).feed(e4);
}
} // namespace lib
#endif