From ce7c38312dae72b2ff6531f17c215f5055e6d6a6 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 5 Jun 2015 18:32:42 +0200 Subject: [PATCH 01/47] iterator improvements: use Lumiera Forward Iterator in range for loops This is kind of the logic consequence, since we consider our functional iterator concept still superior and will continue to rely on it. For some time now, I've considered to build a generic bridge function, to use enable_if and metaprogramming to figure out if some type is a "Lumiera Forward Iterator" automatically. But since our concept is to some degree a contract regarding semantics, which never can be captured by any kind of introspection, such a bridge implementation would be rather heuristic and bears the danger to trigger on types actually not intended as iterator at all. So I consider such a solution as dangerous and we'll settle with just supplying the necessary bridge functions as free functions injected for ADL on a case by case base --- src/lib/iter-adapter-ptr-deref.hpp | 878 +++++++++++++++++++++++++++++ src/lib/iter-adapter.hpp | 19 +- src/lib/itertools.hpp | 9 +- 3 files changed, 902 insertions(+), 4 deletions(-) create mode 100644 src/lib/iter-adapter-ptr-deref.hpp diff --git a/src/lib/iter-adapter-ptr-deref.hpp b/src/lib/iter-adapter-ptr-deref.hpp new file mode 100644 index 000000000..ef3332ddd --- /dev/null +++ b/src/lib/iter-adapter-ptr-deref.hpp @@ -0,0 +1,878 @@ +/* + ITER-ADAPTER-PTR-DEREF.hpp - helpers for building simple forward iterators + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file iter-adapter.hpp + ** Helper template(s) for creating lumiera forward iterators. + ** These are the foundation to build up iterator like types from scratch. + ** Usually, these templates will be created and provided by a custom + ** container type and accessed by the client through a typedef name + ** "iterator" (similar to the usage within the STL). For more advanced + ** usage, the providing container might want to subclass these iterators, + ** e.g. to provide an additional, specialised API. + ** + ** Depending on the concrete situation, several flavours are provided: + ** - the IterAdapter retains an active callback connection to the + ** controlling container, thus allowing arbitrary complex behaviour. + ** - the IterStateWrapper uses a variation of that approach, where the + ** representation of the current state is embedded as an state value + ** element right into the iterator instance. + ** - the RangeIter allows just to expose a range of elements defined + ** by a STL-like pair of "start" and "end" iterators + ** - often, objects are managed internally by pointers, while allowing + ** the clients to use direct references; to support this usage scenario, + ** PtrDerefIter wraps an existing iterator, while dereferencing any value + ** automatically on access. + ** - for some (very specific) usage situations we intend to explore the + ** contents of a stable and unmodifiable data structure through pointers. + ** The AddressExposingIter wraps another Lumiera Forward Iterator and + ** exposes addresses -- assuming the used source iterator is exposing + ** references to pre-existing storage locations (not temporaries). + ** + ** There are many further ways of yielding a Lumiera forward iterator. + ** For example, lib::IterSource builds a "iterable" source of data elements, + ** while hiding the actual container or generator implementation behind a + ** vtable call. Besides, there are adapters for the most common usages + ** with STL containers, and such iterators can also be combined and + ** extended with the help of itertools.hpp + ** + ** Basically every class in compliance with our specific iterator concept + ** can be used as a building block within this framework. + ** + ** + ** \par Lumiera forward iterator concept + ** + ** Similar to the STL, instead of using a common "Iterator" base class, + ** we rather define a common set of functions and behaviour which can + ** be expected from any such iterator. These rules are similar to STL's + ** "forward iterator", with the addition of an bool check to detect + ** iteration end. The latter is inspired by the \c hasNext() function + ** found in many current languages supporting iterators. In a similar + ** vein (inspired from functional programming), we deliberately don't + ** support the various extended iterator concepts from STL and boost + ** (random access iterators, output iterators, arithmetics, difference + ** between iterators and the like). According to this concept, + ** an iterator is a promise for pulling values, + ** and nothing beyond that. + ** + ** - Any Lumiera forward iterator can be in a "exhausted" (invalid) state, + ** which can be checked by the bool conversion. Especially, any instance + ** created by the default ctor is always fixed to that state. This + ** state is final and can't be reset, meaning that any iterator is + ** a disposable one-way-off object. + ** - iterators are copyable and equality comparable + ** - when an iterator is \em not in the exhausted state, it may be + ** \em dereferenced to yield the "current" value. + ** - moreover, iterators may be incremented until exhaustion. + ** + ** @see iter-adapter-test.cpp + ** @see itertools.hpp + ** @see IterSource (completely opaque iterator) + ** @see iter-type-binding.hpp + ** + */ + + +#ifndef LIB_ITER-ADAPTER-PTR-DEREF_H +#define LIB_ITER-ADAPTER-PTR-DEREF_H + + +#include "lib/error.hpp" +#include "lib/bool-checkable.hpp" +#include "lib/iter-type-binding.hpp" + +#include + + +namespace lib { + + + namespace { // internal helpers + void + _throwIterExhausted() + { + throw lumiera::error::Invalid ("Can't iterate further", + lumiera::error::LUMIERA_ERROR_ITER_EXHAUST); + } + } + + + + /** + * Adapter for building an implementation of the lumiera forward iterator concept. + * The "current position" is represented as an opaque element (usually a nested iterator), + * with callbacks into the controlling container instance to manage this position. + * This allows to influence and customise the iteration process to a large extent. + * Basically such an IterAdapter behaves like the similar concept from STL, but + * - it is not just a disguised pointer (meaning, it's more expensive) + * - it checks validity on every operation and may throw + * - it has a distinct back-link to the source container + * - the source container needs to provide hasNext() and iterNext() free functions. + * - we may need friendship to implement those extension points on the container + * - the end-of-iteration can be detected by bool check + * @note it is possible to "hide" a smart-ptr within the CON template parameter. + * + * \par Stipulations + * - POS refers to the current position within the data source of this iterator. + * -# it should be default constructible + * -# it should be copy constructible + * -# when IterAdapter is supposed to be assignable, then POS should be + * -# it should provide embedded typedefs for pointer, reference and value_type, + * or alternatively resolve these types through specialisation of iter::TypeBinding. + * -# it should be convertible to the pointer type it declares + * -# dereferencing should yield a type that is convertible to the reference type + * - CON points to the data source of this iterator (typically a data container type) + * We store a pointer-like backlink to invoke a special iteration control API: + * -# \c checkPoint yields true iff the source has yet more result values to yield + * -# \c iterNext advances the POS to the next element + * + * @see scoped-ptrvect.hpp usage example + * @see iter-type-binding.hpp + * @see iter-adaptor-test.cpp + */ + template + class IterAdapter + : public lib::BoolCheckable > + { + CON source_; + mutable POS pos_; + + public: + typedef typename iter::TypeBinding::pointer pointer; + typedef typename iter::TypeBinding::reference reference; + typedef typename iter::TypeBinding::value_type value_type; + + IterAdapter (CON src, POS const& startpos) + : source_(src) + , pos_(startpos) + { + check(); + } + + IterAdapter () + : source_() + , pos_() + { } + + + /* === lumiera forward iterator concept === */ + + reference + operator*() const + { + _maybe_throw(); + return *pos_; + } + + pointer + operator->() const + { + _maybe_throw(); + return pos_; + } + + IterAdapter& + operator++() + { + _maybe_throw(); + iterate(); + return *this; + } + + bool + isValid () const + { + return check(); + } + + bool + empty () const + { + return !isValid(); + } + + + protected: /* === iteration control interface === */ + + /** ask the controlling container if this position is valid. + * @note this function is called before any operation, + * thus the container may adjust the position value, + * for example setting it to a "stop iteration" mark. + */ + bool + check() const + { + return source_ && checkPoint (source_,pos_); // extension point: free function checkPoint(...) + } + + /** ask the controlling container to yield the next position. + * The call is dispatched only if the current position is valid; + * any new position reached will typically be validated prior + * to any further access, through invocation of #check. + */ + void + iterate() + { + if (check()) + iterNext (source_,pos_); // extension point: free function iterNext(...) + } + + + private: + void + _maybe_throw() const + { + if (!isValid()) + _throwIterExhausted(); + } + + /// comparison is allowed to access impl iterator + template + friend bool operator== (IterAdapter const&, IterAdapter const&); + }; + + + /// Supporting equality comparisons... + template + bool operator== (IterAdapter const& il, IterAdapter const& ir) { return il.pos_ == ir.pos_; } + + template + bool operator!= (IterAdapter const& il, IterAdapter const& ir) { return !(il == ir); } + + + + + /** + * Another Lumiera Forward Iterator building block, based on incorporating a state type + * right into the iterator. Contrast this to IterAdapter, which refers to a managing + * container behind the scenes. Here, all of the state is assumed to live in the + * custom type embedded into this iterator, accessed and manipulated through + * a set of free functions, picked up through ADL. + * + * \par Assumptions when building iterators based on IterStateWrapper + * There is a custom state representation type ST. + * - default constructible + * - this default state represents the \em bottom (invalid) state. + * - copyable, because iterators are passed by value + * - this type needs to provide an iteration control API through free functions + * -# \c checkPoint establishes if the given state element represents a valid state + * -# \c iterNext evolves this state by one step (sideeffect) + * -# \c yield realises the given state, yielding an element of result type T + * + * @see IterExplorer an iterator monad built on top of IterStateWrapper + * @see iter-explorer-test.hpp + * @see iter-adaptor-test.cpp + */ + template + class IterStateWrapper + : public lib::BoolCheckable > + { + ST core_; + + public: + typedef T* pointer; + typedef T& reference; + typedef T value_type; + + IterStateWrapper (ST const& initialState) + : core_(initialState) + { + checkPoint (core_); // extension point: checkPoint + } + + IterStateWrapper () + : core_() + { } + + + /* === lumiera forward iterator concept === */ + + reference + operator*() const + { + __throw_if_empty(); + return yield (core_); // extension point: yield + } + + pointer + operator->() const + { + __throw_if_empty(); + return & yield(core_); // extension point: yield + } + + IterStateWrapper& + operator++() + { + __throw_if_empty(); + iterNext (core_); // extension point: iterNext + return *this; + } + + bool + isValid () const + { + return checkPoint(core_); // extension point: checkPoint + } + + bool + empty () const + { + return !isValid(); + } + + protected: + + /** allow derived classes to + * access state representation */ + ST & + stateCore() + { + return core_; + } + + void + __throw_if_empty() const + { + if (!isValid()) + _throwIterExhausted(); + } + + + /// comparison is allowed to access state implementation core + template + friend bool operator== (IterStateWrapper const&, IterStateWrapper const&); + }; + + + + /// Supporting equality comparisons of equivalent iterators (same state type)... + template + bool operator== (IterStateWrapper const& il, IterStateWrapper const& ir) + { + return (il.empty() && ir.empty()) + || (il.isValid() && ir.isValid() && il.core_ == ir.core_); + } + + template + bool operator!= (IterStateWrapper const& il, IterStateWrapper const& ir) + { + return ! (il == ir); + } + + + + + + + + + /** + * Accessing a STL element range through a Lumiera forward iterator, + * An instance of this iterator adapter is completely self-contained + * and allows to iterate once over the range of elements, until + * \c pos==end . Thus, a custom container may expose a range of + * elements of an embedded STL container, without controlling + * the details of the iteration (as is possible using the + * more generic IterAdapter). + */ + template + class RangeIter + : public lib::BoolCheckable > + { + IT p_; + IT e_; + + public: + typedef typename iter::TypeBinding::pointer pointer; + typedef typename iter::TypeBinding::reference reference; + typedef typename iter::TypeBinding::value_type value_type; + + RangeIter (IT const& start, IT const& end) + : p_(start) + , e_(end) + { } + + RangeIter () + : p_() + , e_() + { } + + + /** allow copy, + * when the underlying iterators + * are compatible or convertible */ + template + RangeIter (I2 const& oIter) + : p_(oIter.getPos()) + , e_(oIter.getEnd()) + { } + + + /* === lumiera forward iterator concept === */ + + reference + operator*() const + { + _maybe_throw(); + return *p_; + } + + pointer + operator->() const + { + _maybe_throw(); + return &(*p_); + } + + RangeIter& + operator++() + { + _maybe_throw(); + ++p_; + return *this; + } + + bool + isValid () const + { + return (p_!= IT()) && (p_ != e_); + } + + bool + empty () const + { + return !isValid(); + } + + + /** access wrapped STL iterator */ + const IT& getPos() const { return p_; } + const IT& getEnd() const { return e_; } + + + private: + + void + _maybe_throw() const + { + if (!isValid()) + _throwIterExhausted(); + } + }; + + + + /// Supporting equality comparisons... + template + bool operator== (RangeIter const& il, RangeIter const& ir) { return (!il && !ir) || (il.getPos() == ir.getPos()); } + + template + bool operator!= (RangeIter const& il, RangeIter const& ir) { return !(il == ir); } + + + + + + namespace { + + /** helper to remove pointer, + * while retaining const */ + template + struct RemovePtr { typedef T Type; }; + + template + struct RemovePtr { typedef T Type; }; + + template + struct RemovePtr { typedef const T Type; }; + + template + struct RemovePtr { typedef const T Type; }; + + template + struct RemovePtr { typedef const T Type; }; + + } + + + /** + * Helper for type rewritings: + * get the element type for an iterator like entity + */ + template + struct IterType; + + template class Iter, class TY, class CON> + struct IterType > + { + typedef CON Container; + typedef TY ElemType; + + template + struct SimilarIter ///< rebind to a similarly structured Iterator with value type T2 + { + typedef Iter Type; + }; + }; + + template + struct IterType > + : IterType + { + template + struct SimilarIter ///< rebind to rewritten Iterator wrapped into RangeIter + { + typedef typename IterType::template SimilarIter::Type WrappedIter; + typedef RangeIter Type; + }; + }; + + + + /** + * wrapper for an existing Iterator type, + * automatically dereferencing the output of the former. + * For this to work, the "source" iterator is expected + * to be declared on \em pointers rather than on values. + * @note bool checkable if and only if source is... + */ + template + class PtrDerefIter + : public lib::BoolCheckable > + { + IT i_; ///< nested source iterator + + + public: + typedef typename IT::value_type pointer; + typedef typename RemovePtr::Type value_type; + typedef value_type& reference; + + // for use with STL algorithms + typedef void difference_type; + typedef std::forward_iterator_tag iterator_category; + + + // the purpose of the following typedefs is to ease building a correct "const iterator" + + typedef typename boost::remove_const::type ValueTypeBase; // value_type without const + + typedef typename IterType::template SimilarIter< ValueTypeBase* * >::Type WrappedIterType; + typedef typename IterType::template SimilarIter::Type WrappedConstIterType; + + typedef PtrDerefIter IterType; + typedef PtrDerefIter ConstIterType; + + + + /** PtrDerefIter is always created + * by wrapping an existing iterator. + */ + explicit + PtrDerefIter (IT srcIter) + : i_(srcIter) + { } + + + /** allow copy initialisation also when + * the wrapped iterator is based on some variation of a pointer. + * Especially, this includes initialisation of the "const variant" + * from the "normal variant" of PtrDerefIter. Actually, we need to convert + * in this case by brute force, because indeed (const TY *)* is not assignable + * from (TY *)* -- just we know that our intention is to dereference both levels + * of pointers, and then the resulting conversion is correct. + * @note in case IT == WrappedIterType, this is just a redefinition of the + * default copy ctor. In all other cases, this is an additional + * ctor besides the default copy ctor */ + PtrDerefIter (PtrDerefIter const& oIter) + : i_(reinterpret_cast (oIter.getBase())) + { } + + PtrDerefIter& + operator= (PtrDerefIter const& ref) + { + i_ = reinterpret_cast (ref.getBase()); + return *this; + } + + + /** explicit builder to allow creating a const variant from the basic srcIter type. + * Again, the reason necessitating this "backdoor" is that we want to swallow one level + * of indirection. Generally speaking `const T **` is not the same as `T * const *`, + * but in our specific case the API ensures that a `PtrDerefIter` + * only exposes const elements. + */ + static PtrDerefIter + build_by_cast (WrappedIterType const& srcIter) + { + return PtrDerefIter (reinterpret_cast (srcIter)); + } + + static PtrDerefIter + nil() + { + return PtrDerefIter (IT()); + } + + + + + + + /* === lumiera forward iterator concept === */ + + reference + operator*() const + { + return *(*i_); + } + + pointer + operator->() const + { + return *i_; + } + + PtrDerefIter& + operator++() + { + ++i_; + return *this; + } + + bool + isValid () const + { + return bool(i_); + } + + bool + empty () const + { + return !isValid(); + } + + + /** access the wrapped implementation iterator */ + IT const& + getBase() const + { + return i_; + } + }; + + + /// Supporting equality comparisons... + template + bool operator== (PtrDerefIter const& il, PtrDerefIter const& ir) { return il.getBase() == ir.getBase(); } + + template + bool operator!= (PtrDerefIter const& il, PtrDerefIter const& ir) { return !(il == ir); } + + + + + + /** + * wrapper for an existing Iterator type to expose the address of each value yielded. + * Typically this can be used to build visitation sequences based on values living + * within a stable data structure (e.g. unmodifiable STL vector) + * @warning use of this wrapper might lead to taking the address of temporaries. + * The continued existence of the exposed storage locations must be guaranteed. + * @note bool checkable if and only if source is... + */ + template + class AddressExposingIter + : public lib::BoolCheckable > + { + typedef typename IT::pointer _Ptr; + + IT i_; ///< nested source iterator + + mutable _Ptr currPtr_; + + + void + takeAddress() + { + if (i_.isValid()) + currPtr_ = & (*i_); + else + currPtr_ = 0; + } + + + public: + typedef typename IT::pointer const* pointer; + typedef typename IT::pointer const& reference; + typedef typename IT::pointer const value_type; + + + /** AddressExposingIter is always created + * by wrapping an existing iterator. + */ + explicit + AddressExposingIter (IT srcIter) + : i_(srcIter) + { + takeAddress(); + } + + + + + + /* === lumiera forward iterator concept === */ + + /** @return address of the source iteraor's current result + * @warning exposing a reference to an internal pointer for sake of compatibility. + * Clients must not store that reference, but rather use it to initialise + * a copy. The internal pointer exposed here will be changed on increment. + */ + reference + operator*() const + { + return currPtr_; + } + + _Ptr + operator->() const + { + return currPtr_; + } + + AddressExposingIter& + operator++() + { + ++i_; + takeAddress(); + return *this; + } + + bool + isValid () const + { + return bool(i_); + } + + bool + empty () const + { + return !isValid(); + } + + + /** access the wrapped implementation iterator */ + IT const& + getBase() const + { + return i_; + } + }; + + + /// Supporting equality comparisons... + template + bool operator== (AddressExposingIter const& il, AddressExposingIter const& ir) { return il.getBase() == ir.getBase(); } + + template + bool operator!= (AddressExposingIter const& il, AddressExposingIter const& ir) { return !(il == ir); } + + + + + + + /** wrapper to declare exposed values const */ + template + class ConstIter + : public lib::BoolCheckable > + { + IT i_; ///< nested source iterator + + + public: + typedef const typename IT::value_type value_type; + typedef const typename IT::pointer pointer; + typedef const typename IT::reference reference; + + ConstIter (IT srcIter) + : i_(srcIter) + { } + + + + + /* === lumiera forward iterator concept === */ + + reference + operator*() const + { + return *i_; + } + + pointer + operator->() const + { + return i_.operator->(); + } + + ConstIter& + operator++() + { + ++i_; + return *this; + } + + bool + isValid () const + { + return bool(i_); + } + + bool + empty () const + { + return !isValid(); + } + + + /** access the wrapped implementation iterator */ + IT const& + getBase() const + { + return i_; + } + }; + + + /// Supporting equality comparisons... + template + bool operator== (ConstIter const& il, ConstIter const& ir) { return il.getBase() == ir.getBase(); } + + template + bool operator!= (ConstIter const& il, ConstIter const& ir) { return !(il == ir); } + + + +}// namespace lib +#endif /*LIB_ITER-ADAPTER-PTR-DEREF_H*/ diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index 1f668f446..2c05650d0 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -114,6 +114,12 @@ namespace lib { } } + /** use a given Lumiera Forward Iterator in standard "range for loops" */ +#define ENABLE_USE_IN_STD_RANGE_FOR_LOOPS(ITER) \ + friend ITER begin (ITER const& it){ return it; } \ + friend ITER&& begin (ITER&& it) { return static_cast (it); } \ + friend ITER end (ITER const&) { return ITER(); } + /** @@ -244,6 +250,9 @@ namespace lib { _throwIterExhausted(); } + + ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (IterAdapter); + /// comparison is allowed to access impl iterator template friend bool operator== (IterAdapter const&, IterAdapter const&); @@ -357,6 +366,9 @@ namespace lib { } + + ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (IterStateWrapper); + /// comparison is allowed to access state implementation core template friend bool operator== (IterStateWrapper const&, IterStateWrapper const&); @@ -469,6 +481,9 @@ namespace lib { const IT& getEnd() const { return e_; } + ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (RangeIter); + + private: void @@ -874,5 +889,5 @@ namespace lib { -} // namespace lib -#endif +}// namespace lib +#endif /*LIB_ITER_ADAPTER_H*/ diff --git a/src/lib/itertools.hpp b/src/lib/itertools.hpp index 69ff3124a..f33d972fd 100644 --- a/src/lib/itertools.hpp +++ b/src/lib/itertools.hpp @@ -281,8 +281,8 @@ namespace lib { } - mutable bool isOK_; mutable bool cached_; + mutable bool isOK_; bool currVal_isOK () const ///< @return (maybe cached) result of filter predicate @@ -304,7 +304,8 @@ namespace lib { FilterCore (IT const& source, PRED prediDef) : Raw(source) , predicate_(prediDef) // induces a signature check - , cached_(false) + , cached_(false) // not yet cached + , isOK_() // some value { } }; @@ -331,6 +332,8 @@ namespace lib { FilterIter (IT const& src, PRED filterPredicate) : _Impl(_Filter(src,filterPredicate)) { } + + ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (FilterIter) }; @@ -471,6 +474,8 @@ namespace lib { TransformIter (IT const& src, FUN trafoFunc) : _IteratorImpl(_Trafo(src,trafoFunc)) { } + + ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (TransformIter) }; From 46e573efb730691dee8ee9e721fb267607d1d2c1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 5 Jun 2015 18:52:56 +0200 Subject: [PATCH 02/47] includes: split out rarely used PtrDerefIter this allows us to avoid a boost include otherwise dragged in through the widely used iter-adapter.hpp --- src/lib/iter-adapter-ptr-deref.hpp | 586 +--------------------------- src/lib/iter-adapter-stl.hpp | 2 +- src/lib/iter-adapter.hpp | 281 +------------ src/lib/scoped-ptrvect.hpp | 2 +- tests/library/iter-adapter-test.cpp | 15 +- 5 files changed, 35 insertions(+), 851 deletions(-) diff --git a/src/lib/iter-adapter-ptr-deref.hpp b/src/lib/iter-adapter-ptr-deref.hpp index ef3332ddd..7d76ec811 100644 --- a/src/lib/iter-adapter-ptr-deref.hpp +++ b/src/lib/iter-adapter-ptr-deref.hpp @@ -1,8 +1,8 @@ /* - ITER-ADAPTER-PTR-DEREF.hpp - helpers for building simple forward iterators + ITER-ADAPTER-PTR-DEREF.hpp - wrapping iterator to dereference pointers automatically Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2015, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -20,478 +20,39 @@ */ -/** @file iter-adapter.hpp - ** Helper template(s) for creating lumiera forward iterators. - ** These are the foundation to build up iterator like types from scratch. - ** Usually, these templates will be created and provided by a custom - ** container type and accessed by the client through a typedef name - ** "iterator" (similar to the usage within the STL). For more advanced - ** usage, the providing container might want to subclass these iterators, - ** e.g. to provide an additional, specialised API. + +/** @file iter-adapter-ptr-deref.hpp + ** Extension adapter for Lumiera Forward Iterators to dereference + ** any pointer values automatically. Sometimes, the iteration of some + ** container naturally just yields pointers to contained values. But, + ** from a viewpoint of interface design, we'd prefer the iterator to + ** expose direct references (NULL values can be excluded). This helper + ** template does precisely this: it wraps up any other entity conforming + ** to the »Lumiera Forward Iterator« concept and, on access, automatically + ** dereferences the exposed pointer. ** - ** Depending on the concrete situation, several flavours are provided: - ** - the IterAdapter retains an active callback connection to the - ** controlling container, thus allowing arbitrary complex behaviour. - ** - the IterStateWrapper uses a variation of that approach, where the - ** representation of the current state is embedded as an state value - ** element right into the iterator instance. - ** - the RangeIter allows just to expose a range of elements defined - ** by a STL-like pair of "start" and "end" iterators - ** - often, objects are managed internally by pointers, while allowing - ** the clients to use direct references; to support this usage scenario, - ** PtrDerefIter wraps an existing iterator, while dereferencing any value - ** automatically on access. - ** - for some (very specific) usage situations we intend to explore the - ** contents of a stable and unmodifiable data structure through pointers. - ** The AddressExposingIter wraps another Lumiera Forward Iterator and - ** exposes addresses -- assuming the used source iterator is exposing - ** references to pre-existing storage locations (not temporaries). - ** - ** There are many further ways of yielding a Lumiera forward iterator. - ** For example, lib::IterSource builds a "iterable" source of data elements, - ** while hiding the actual container or generator implementation behind a - ** vtable call. Besides, there are adapters for the most common usages - ** with STL containers, and such iterators can also be combined and - ** extended with the help of itertools.hpp - ** - ** Basically every class in compliance with our specific iterator concept - ** can be used as a building block within this framework. - ** - ** - ** \par Lumiera forward iterator concept - ** - ** Similar to the STL, instead of using a common "Iterator" base class, - ** we rather define a common set of functions and behaviour which can - ** be expected from any such iterator. These rules are similar to STL's - ** "forward iterator", with the addition of an bool check to detect - ** iteration end. The latter is inspired by the \c hasNext() function - ** found in many current languages supporting iterators. In a similar - ** vein (inspired from functional programming), we deliberately don't - ** support the various extended iterator concepts from STL and boost - ** (random access iterators, output iterators, arithmetics, difference - ** between iterators and the like). According to this concept, - ** an iterator is a promise for pulling values, - ** and nothing beyond that. - ** - ** - Any Lumiera forward iterator can be in a "exhausted" (invalid) state, - ** which can be checked by the bool conversion. Especially, any instance - ** created by the default ctor is always fixed to that state. This - ** state is final and can't be reset, meaning that any iterator is - ** a disposable one-way-off object. - ** - iterators are copyable and equality comparable - ** - when an iterator is \em not in the exhausted state, it may be - ** \em dereferenced to yield the "current" value. - ** - moreover, iterators may be incremented until exhaustion. + ** In addition, the reversed operation is also supported through another + ** helper template: to take the address of any value exposed by the given + ** underlying iterator ** ** @see iter-adapter-test.cpp + ** @see iter-adapter.hpp basic iterator adapters ** @see itertools.hpp - ** @see IterSource (completely opaque iterator) - ** @see iter-type-binding.hpp ** */ -#ifndef LIB_ITER-ADAPTER-PTR-DEREF_H -#define LIB_ITER-ADAPTER-PTR-DEREF_H +#ifndef LIB_ITER_ADAPTER_PTR_DEREF_H +#define LIB_ITER_ADAPTER_PTR_DEREF_H -#include "lib/error.hpp" -#include "lib/bool-checkable.hpp" -#include "lib/iter-type-binding.hpp" +#include "lib/iter-adapter.hpp" #include namespace lib { - - namespace { // internal helpers - void - _throwIterExhausted() - { - throw lumiera::error::Invalid ("Can't iterate further", - lumiera::error::LUMIERA_ERROR_ITER_EXHAUST); - } - } - - - - /** - * Adapter for building an implementation of the lumiera forward iterator concept. - * The "current position" is represented as an opaque element (usually a nested iterator), - * with callbacks into the controlling container instance to manage this position. - * This allows to influence and customise the iteration process to a large extent. - * Basically such an IterAdapter behaves like the similar concept from STL, but - * - it is not just a disguised pointer (meaning, it's more expensive) - * - it checks validity on every operation and may throw - * - it has a distinct back-link to the source container - * - the source container needs to provide hasNext() and iterNext() free functions. - * - we may need friendship to implement those extension points on the container - * - the end-of-iteration can be detected by bool check - * @note it is possible to "hide" a smart-ptr within the CON template parameter. - * - * \par Stipulations - * - POS refers to the current position within the data source of this iterator. - * -# it should be default constructible - * -# it should be copy constructible - * -# when IterAdapter is supposed to be assignable, then POS should be - * -# it should provide embedded typedefs for pointer, reference and value_type, - * or alternatively resolve these types through specialisation of iter::TypeBinding. - * -# it should be convertible to the pointer type it declares - * -# dereferencing should yield a type that is convertible to the reference type - * - CON points to the data source of this iterator (typically a data container type) - * We store a pointer-like backlink to invoke a special iteration control API: - * -# \c checkPoint yields true iff the source has yet more result values to yield - * -# \c iterNext advances the POS to the next element - * - * @see scoped-ptrvect.hpp usage example - * @see iter-type-binding.hpp - * @see iter-adaptor-test.cpp - */ - template - class IterAdapter - : public lib::BoolCheckable > - { - CON source_; - mutable POS pos_; - - public: - typedef typename iter::TypeBinding::pointer pointer; - typedef typename iter::TypeBinding::reference reference; - typedef typename iter::TypeBinding::value_type value_type; - - IterAdapter (CON src, POS const& startpos) - : source_(src) - , pos_(startpos) - { - check(); - } - - IterAdapter () - : source_() - , pos_() - { } - - - /* === lumiera forward iterator concept === */ - - reference - operator*() const - { - _maybe_throw(); - return *pos_; - } - - pointer - operator->() const - { - _maybe_throw(); - return pos_; - } - - IterAdapter& - operator++() - { - _maybe_throw(); - iterate(); - return *this; - } - - bool - isValid () const - { - return check(); - } - - bool - empty () const - { - return !isValid(); - } - - - protected: /* === iteration control interface === */ - - /** ask the controlling container if this position is valid. - * @note this function is called before any operation, - * thus the container may adjust the position value, - * for example setting it to a "stop iteration" mark. - */ - bool - check() const - { - return source_ && checkPoint (source_,pos_); // extension point: free function checkPoint(...) - } - - /** ask the controlling container to yield the next position. - * The call is dispatched only if the current position is valid; - * any new position reached will typically be validated prior - * to any further access, through invocation of #check. - */ - void - iterate() - { - if (check()) - iterNext (source_,pos_); // extension point: free function iterNext(...) - } - - - private: - void - _maybe_throw() const - { - if (!isValid()) - _throwIterExhausted(); - } - - /// comparison is allowed to access impl iterator - template - friend bool operator== (IterAdapter const&, IterAdapter const&); - }; - - - /// Supporting equality comparisons... - template - bool operator== (IterAdapter const& il, IterAdapter const& ir) { return il.pos_ == ir.pos_; } - - template - bool operator!= (IterAdapter const& il, IterAdapter const& ir) { return !(il == ir); } - - - - - /** - * Another Lumiera Forward Iterator building block, based on incorporating a state type - * right into the iterator. Contrast this to IterAdapter, which refers to a managing - * container behind the scenes. Here, all of the state is assumed to live in the - * custom type embedded into this iterator, accessed and manipulated through - * a set of free functions, picked up through ADL. - * - * \par Assumptions when building iterators based on IterStateWrapper - * There is a custom state representation type ST. - * - default constructible - * - this default state represents the \em bottom (invalid) state. - * - copyable, because iterators are passed by value - * - this type needs to provide an iteration control API through free functions - * -# \c checkPoint establishes if the given state element represents a valid state - * -# \c iterNext evolves this state by one step (sideeffect) - * -# \c yield realises the given state, yielding an element of result type T - * - * @see IterExplorer an iterator monad built on top of IterStateWrapper - * @see iter-explorer-test.hpp - * @see iter-adaptor-test.cpp - */ - template - class IterStateWrapper - : public lib::BoolCheckable > - { - ST core_; - - public: - typedef T* pointer; - typedef T& reference; - typedef T value_type; - - IterStateWrapper (ST const& initialState) - : core_(initialState) - { - checkPoint (core_); // extension point: checkPoint - } - - IterStateWrapper () - : core_() - { } - - - /* === lumiera forward iterator concept === */ - - reference - operator*() const - { - __throw_if_empty(); - return yield (core_); // extension point: yield - } - - pointer - operator->() const - { - __throw_if_empty(); - return & yield(core_); // extension point: yield - } - - IterStateWrapper& - operator++() - { - __throw_if_empty(); - iterNext (core_); // extension point: iterNext - return *this; - } - - bool - isValid () const - { - return checkPoint(core_); // extension point: checkPoint - } - - bool - empty () const - { - return !isValid(); - } - - protected: - - /** allow derived classes to - * access state representation */ - ST & - stateCore() - { - return core_; - } - - void - __throw_if_empty() const - { - if (!isValid()) - _throwIterExhausted(); - } - - - /// comparison is allowed to access state implementation core - template - friend bool operator== (IterStateWrapper const&, IterStateWrapper const&); - }; - - - - /// Supporting equality comparisons of equivalent iterators (same state type)... - template - bool operator== (IterStateWrapper const& il, IterStateWrapper const& ir) - { - return (il.empty() && ir.empty()) - || (il.isValid() && ir.isValid() && il.core_ == ir.core_); - } - - template - bool operator!= (IterStateWrapper const& il, IterStateWrapper const& ir) - { - return ! (il == ir); - } - - - - - - - - - /** - * Accessing a STL element range through a Lumiera forward iterator, - * An instance of this iterator adapter is completely self-contained - * and allows to iterate once over the range of elements, until - * \c pos==end . Thus, a custom container may expose a range of - * elements of an embedded STL container, without controlling - * the details of the iteration (as is possible using the - * more generic IterAdapter). - */ - template - class RangeIter - : public lib::BoolCheckable > - { - IT p_; - IT e_; - - public: - typedef typename iter::TypeBinding::pointer pointer; - typedef typename iter::TypeBinding::reference reference; - typedef typename iter::TypeBinding::value_type value_type; - - RangeIter (IT const& start, IT const& end) - : p_(start) - , e_(end) - { } - - RangeIter () - : p_() - , e_() - { } - - - /** allow copy, - * when the underlying iterators - * are compatible or convertible */ - template - RangeIter (I2 const& oIter) - : p_(oIter.getPos()) - , e_(oIter.getEnd()) - { } - - - /* === lumiera forward iterator concept === */ - - reference - operator*() const - { - _maybe_throw(); - return *p_; - } - - pointer - operator->() const - { - _maybe_throw(); - return &(*p_); - } - - RangeIter& - operator++() - { - _maybe_throw(); - ++p_; - return *this; - } - - bool - isValid () const - { - return (p_!= IT()) && (p_ != e_); - } - - bool - empty () const - { - return !isValid(); - } - - - /** access wrapped STL iterator */ - const IT& getPos() const { return p_; } - const IT& getEnd() const { return e_; } - - - private: - - void - _maybe_throw() const - { - if (!isValid()) - _throwIterExhausted(); - } - }; - - - - /// Supporting equality comparisons... - template - bool operator== (RangeIter const& il, RangeIter const& ir) { return (!il && !ir) || (il.getPos() == ir.getPos()); } - - template - bool operator!= (RangeIter const& il, RangeIter const& ir) { return !(il == ir); } - - - - - namespace { /** helper to remove pointer, @@ -514,41 +75,8 @@ namespace lib { } - /** - * Helper for type rewritings: - * get the element type for an iterator like entity - */ - template - struct IterType; - template class Iter, class TY, class CON> - struct IterType > - { - typedef CON Container; - typedef TY ElemType; - - template - struct SimilarIter ///< rebind to a similarly structured Iterator with value type T2 - { - typedef Iter Type; - }; - }; - - template - struct IterType > - : IterType - { - template - struct SimilarIter ///< rebind to rewritten Iterator wrapped into RangeIter - { - typedef typename IterType::template SimilarIter::Type WrappedIter; - typedef RangeIter Type; - }; - }; - - - - /** + /** * wrapper for an existing Iterator type, * automatically dereferencing the output of the former. * For this to work, the "source" iterator is expected @@ -802,77 +330,5 @@ namespace lib { - /** wrapper to declare exposed values const */ - template - class ConstIter - : public lib::BoolCheckable > - { - IT i_; ///< nested source iterator - - - public: - typedef const typename IT::value_type value_type; - typedef const typename IT::pointer pointer; - typedef const typename IT::reference reference; - - ConstIter (IT srcIter) - : i_(srcIter) - { } - - - - - /* === lumiera forward iterator concept === */ - - reference - operator*() const - { - return *i_; - } - - pointer - operator->() const - { - return i_.operator->(); - } - - ConstIter& - operator++() - { - ++i_; - return *this; - } - - bool - isValid () const - { - return bool(i_); - } - - bool - empty () const - { - return !isValid(); - } - - - /** access the wrapped implementation iterator */ - IT const& - getBase() const - { - return i_; - } - }; - - - /// Supporting equality comparisons... - template - bool operator== (ConstIter const& il, ConstIter const& ir) { return il.getBase() == ir.getBase(); } - - template - bool operator!= (ConstIter const& il, ConstIter const& ir) { return !(il == ir); } - - - }// namespace lib -#endif /*LIB_ITER-ADAPTER-PTR-DEREF_H*/ +#endif /*LIB_ITER_ADAPTER_PTR_DEREF_H*/ diff --git a/src/lib/iter-adapter-stl.hpp b/src/lib/iter-adapter-stl.hpp index 703216e53..7941974f3 100644 --- a/src/lib/iter-adapter-stl.hpp +++ b/src/lib/iter-adapter-stl.hpp @@ -42,7 +42,7 @@ #include "lib/iter-adapter.hpp" - +#include "lib/iter-adapter-ptr-deref.hpp" diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index 2c05650d0..69d5407fd 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -37,6 +37,9 @@ ** element right into the iterator instance. ** - the RangeIter allows just to expose a range of elements defined ** by a STL-like pair of "start" and "end" iterators + ** + ** Some more specific use cases are provided in the extension header + ** iter-adapter-ptr-deref.hpp ** - often, objects are managed internally by pointers, while allowing ** the clients to use direct references; to support this usage scenario, ** PtrDerefIter wraps an existing iterator, while dereferencing any value @@ -99,8 +102,6 @@ #include "lib/bool-checkable.hpp" #include "lib/iter-type-binding.hpp" -#include - namespace lib { @@ -507,28 +508,6 @@ namespace lib { - namespace { - - /** helper to remove pointer, - * while retaining const */ - template - struct RemovePtr { typedef T Type; }; - - template - struct RemovePtr { typedef T Type; }; - - template - struct RemovePtr { typedef const T Type; }; - - template - struct RemovePtr { typedef const T Type; }; - - template - struct RemovePtr { typedef const T Type; }; - - } - - /** * Helper for type rewritings: * get the element type for an iterator like entity @@ -563,260 +542,6 @@ namespace lib { - /** - * wrapper for an existing Iterator type, - * automatically dereferencing the output of the former. - * For this to work, the "source" iterator is expected - * to be declared on \em pointers rather than on values. - * @note bool checkable if and only if source is... - */ - template - class PtrDerefIter - : public lib::BoolCheckable > - { - IT i_; ///< nested source iterator - - - public: - typedef typename IT::value_type pointer; - typedef typename RemovePtr::Type value_type; - typedef value_type& reference; - - // for use with STL algorithms - typedef void difference_type; - typedef std::forward_iterator_tag iterator_category; - - - // the purpose of the following typedefs is to ease building a correct "const iterator" - - typedef typename boost::remove_const::type ValueTypeBase; // value_type without const - - typedef typename IterType::template SimilarIter< ValueTypeBase* * >::Type WrappedIterType; - typedef typename IterType::template SimilarIter::Type WrappedConstIterType; - - typedef PtrDerefIter IterType; - typedef PtrDerefIter ConstIterType; - - - - /** PtrDerefIter is always created - * by wrapping an existing iterator. - */ - explicit - PtrDerefIter (IT srcIter) - : i_(srcIter) - { } - - - /** allow copy initialisation also when - * the wrapped iterator is based on some variation of a pointer. - * Especially, this includes initialisation of the "const variant" - * from the "normal variant" of PtrDerefIter. Actually, we need to convert - * in this case by brute force, because indeed (const TY *)* is not assignable - * from (TY *)* -- just we know that our intention is to dereference both levels - * of pointers, and then the resulting conversion is correct. - * @note in case IT == WrappedIterType, this is just a redefinition of the - * default copy ctor. In all other cases, this is an additional - * ctor besides the default copy ctor */ - PtrDerefIter (PtrDerefIter const& oIter) - : i_(reinterpret_cast (oIter.getBase())) - { } - - PtrDerefIter& - operator= (PtrDerefIter const& ref) - { - i_ = reinterpret_cast (ref.getBase()); - return *this; - } - - - /** explicit builder to allow creating a const variant from the basic srcIter type. - * Again, the reason necessitating this "backdoor" is that we want to swallow one level - * of indirection. Generally speaking `const T **` is not the same as `T * const *`, - * but in our specific case the API ensures that a `PtrDerefIter` - * only exposes const elements. - */ - static PtrDerefIter - build_by_cast (WrappedIterType const& srcIter) - { - return PtrDerefIter (reinterpret_cast (srcIter)); - } - - static PtrDerefIter - nil() - { - return PtrDerefIter (IT()); - } - - - - - - - /* === lumiera forward iterator concept === */ - - reference - operator*() const - { - return *(*i_); - } - - pointer - operator->() const - { - return *i_; - } - - PtrDerefIter& - operator++() - { - ++i_; - return *this; - } - - bool - isValid () const - { - return bool(i_); - } - - bool - empty () const - { - return !isValid(); - } - - - /** access the wrapped implementation iterator */ - IT const& - getBase() const - { - return i_; - } - }; - - - /// Supporting equality comparisons... - template - bool operator== (PtrDerefIter const& il, PtrDerefIter const& ir) { return il.getBase() == ir.getBase(); } - - template - bool operator!= (PtrDerefIter const& il, PtrDerefIter const& ir) { return !(il == ir); } - - - - - - /** - * wrapper for an existing Iterator type to expose the address of each value yielded. - * Typically this can be used to build visitation sequences based on values living - * within a stable data structure (e.g. unmodifiable STL vector) - * @warning use of this wrapper might lead to taking the address of temporaries. - * The continued existence of the exposed storage locations must be guaranteed. - * @note bool checkable if and only if source is... - */ - template - class AddressExposingIter - : public lib::BoolCheckable > - { - typedef typename IT::pointer _Ptr; - - IT i_; ///< nested source iterator - - mutable _Ptr currPtr_; - - - void - takeAddress() - { - if (i_.isValid()) - currPtr_ = & (*i_); - else - currPtr_ = 0; - } - - - public: - typedef typename IT::pointer const* pointer; - typedef typename IT::pointer const& reference; - typedef typename IT::pointer const value_type; - - - /** AddressExposingIter is always created - * by wrapping an existing iterator. - */ - explicit - AddressExposingIter (IT srcIter) - : i_(srcIter) - { - takeAddress(); - } - - - - - - /* === lumiera forward iterator concept === */ - - /** @return address of the source iteraor's current result - * @warning exposing a reference to an internal pointer for sake of compatibility. - * Clients must not store that reference, but rather use it to initialise - * a copy. The internal pointer exposed here will be changed on increment. - */ - reference - operator*() const - { - return currPtr_; - } - - _Ptr - operator->() const - { - return currPtr_; - } - - AddressExposingIter& - operator++() - { - ++i_; - takeAddress(); - return *this; - } - - bool - isValid () const - { - return bool(i_); - } - - bool - empty () const - { - return !isValid(); - } - - - /** access the wrapped implementation iterator */ - IT const& - getBase() const - { - return i_; - } - }; - - - /// Supporting equality comparisons... - template - bool operator== (AddressExposingIter const& il, AddressExposingIter const& ir) { return il.getBase() == ir.getBase(); } - - template - bool operator!= (AddressExposingIter const& il, AddressExposingIter const& ir) { return !(il == ir); } - - - - - - /** wrapper to declare exposed values const */ template class ConstIter diff --git a/src/lib/scoped-ptrvect.hpp b/src/lib/scoped-ptrvect.hpp index 241ada393..c4373620a 100644 --- a/src/lib/scoped-ptrvect.hpp +++ b/src/lib/scoped-ptrvect.hpp @@ -48,7 +48,7 @@ #include "include/logging.h" -#include "lib/iter-adapter.hpp" +#include "lib/iter-adapter-ptr-deref.hpp" #include "lib/error.hpp" #include "lib/util.hpp" diff --git a/tests/library/iter-adapter-test.cpp b/tests/library/iter-adapter-test.cpp index fc5324b7d..2f40f3f70 100644 --- a/tests/library/iter-adapter-test.cpp +++ b/tests/library/iter-adapter-test.cpp @@ -27,6 +27,7 @@ #include "lib/util-foreach.hpp" #include "lib/iter-adapter.hpp" +#include "lib/iter-adapter-ptr-deref.hpp" #include #include @@ -139,10 +140,12 @@ namespace test{ } /** Implementation of Iteration-logic: detect iteration end. - * @note the problem here is that this implementation chooses - * to use two representations of "bottom" (end, invalid). - * The reason is, we want the default-constructed IterAdapter - * also be the "bottom" value. Thus, when we detect the + * @note the problem here is that this implementation chooses to use + * two representations of ⟂ ("bottom", end, invalid). The reason is, + * we want the default-constructed IterAdapter also be the ⟂ value. + * This is in accordance with the »Lumiera Forward Iterator« concept, + * which requires the default constructed iterator to mark the iteration + * end and to evaluate to \c bool(false). Thus, when we detect the * iteration end by internal logic (\c numberz_.end() ), we * immediately transform this into the official "bottom" */ @@ -151,11 +154,11 @@ namespace test{ checkPoint (const TestContainer* src, ITER& pos) { REQUIRE (src); - if ((pos != ITER(0)) && (pos != src->numberz_.end())) + if ((pos != ITER()) && (pos != src->numberz_.end())) return true; else { - pos = ITER(0); + pos = ITER(); return false; } } }; From ff0950fd3ba97b3eae6a89d4271cfa241d6184f7 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 4 Jul 2015 23:16:17 +0200 Subject: [PATCH 03/47] DOC: a note regarding Lumiera Forward Iterators and the range-for loop --- doc/devel/rfc/LumieraForwardIterator.txt | 23 +++++++++++++++++++++++ wiki/renderengine.html | 13 ++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/doc/devel/rfc/LumieraForwardIterator.txt b/doc/devel/rfc/LumieraForwardIterator.txt index 5bb298127..673eeb9d2 100644 --- a/doc/devel/rfc/LumieraForwardIterator.txt +++ b/doc/devel/rfc/LumieraForwardIterator.txt @@ -155,6 +155,29 @@ would require a ``deep copy'' of any underlying data structures. Ichthyostega:: 'Sa 07 Jan 2012 21:49:09 CET' ~~ +NOTE: Lumiera Forward Iterators can be made to work together with the + 'range for loop', as introduced with C++11. The preferred solution + is to define the necessary free functions `begin` and `end` for ADL. + This is best done on a per implementation base. We consider a generic + solution not worth the effort + +This is to say, these two concepts can be made to work together well. While, +at a conceptual level, they are not really compatible, and build on a +different understanding: The standard for-loop assumes ``a container'', +while our Forward Iterator Concept deals with ``abstract data sources''. +The user needs to understand the fine points of that conceptual difference: + +- if you apply the range-`for` construct on a container, you can iterate + as often as you like. Even if the iterators of that container are + implemented in compliance with the Lumiera Forward Iterator concept. +- but if you apply the range-`for` construct on _a given iterator_, + you can do so only once. There is no way to reset that iterator, + other than obtaining a new one. + +See `71167be9c9aaa` for the typical bridge implementation + +Ichthyostega:: 'Sa 04 Jul 2015 22:57:51 CEST' ~~ + //endof_comments: diff --git a/wiki/renderengine.html b/wiki/renderengine.html index cf471541e..2766ce776 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -2219,7 +2219,7 @@ Of course, we can place other ~MObjects relative to some fork (that's the main r &rarr; [[Anatomy of the high-level model|HighLevelModel]] -
+
The situation focussed by this concept is when an API needs to expose a sequence of results, values or objects, instead of just yielding a function result value. As the naive solution of passing an pointer or array creates coupling to internals, it was superseded by the ~GoF [[Iterator pattern|http://en.wikipedia.org/wiki/Iterator]]. Iteration can be implemented by convention, polymorphically or by generic programming; we use the latter approach.
 
 !Lumiera Forward Iterator concept
@@ -2237,6 +2237,17 @@ The Lumiera Forward Iterator concept is a blend of the STL iterators and iterato
 
 Another notable difference to the STL iterators is the default ctor and the {{{bool}}} conversion. The latter allows using iterators painlessly within {{{for}}} and {{{while}}} loops; a default constructed iterator is equivalent to the STL container's {{{end()}}} value &mdash; indeed any //container-like// object exposing Lumiera Forward Iteration is encouraged to provide such an {{{end()}}}-function, additionally enabling iteration by {{{std::for_each}}} (or Lumiera's even more convenient {{{util::for_each()}}}).
 
+!!!interoperation with the C++11 range-for construct
+Lumiera Forward Iterators can be made to work together with the  'range for loop', as introduced with C++11. The preferred solution is to define the necessary free functions {{{begin}}} and {{{end}}} for ADL. This is best done on a per implementation base. We consider a generic solution not worth the effort. See {{{71167be9c9aaa}}}.
+
+This is to say, these two concepts can be made to work together well. While, at a conceptual level, they are not really compatible, and build on a different understanding: The standard for-loop assumes //a container,// while our Forward Iterator Concept deals with //abstract data sources.//. 
+The user needs to understand the fine points of that conceptual difference:
+* if you apply the range-`for` construct on a container, you can iterate as often as you like. Even if the iterators of that container are  implemented in compliance with the Lumiera Forward Iterator concept.
+* but if you apply the range-`for` construct on //a given iterator, //  you can do so only once. There is no way to reset that iterator, other than obtaining a new one.
+ 
+The bridge methods are usually defined so that the {{{end}}}-function just returns a default constructed iterator, which -- by concept -- is the marker for iteration end
+
+
 !!Implementation notes
 ''iter-adapter.hpp'' provides some helper templates for building Lumiera Forward Iterators.
 * __~IterAdapter__ is the most flexible variant, intended for use by custom facilities. An ~IterAdapter maintains an internal back-link to a facilitiy exposing an iteration control API, which is accessed through free functions as extension point. This iteration control API is similar to C#, allowing to advance to the next result and to check the current iteration state.

From 50faff29a99e31cb88dffdb43178a4e29e71351a Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 3 Jul 2015 02:53:27 +0200
Subject: [PATCH 04/47] add a startsWith util function

Boost has a starts_with in the string algorithms lib,
but we do not want to pull that in everywhere.
---
 src/lib/util.hpp | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/src/lib/util.hpp b/src/lib/util.hpp
index 6c36c2959..961439894 100644
--- a/src/lib/util.hpp
+++ b/src/lib/util.hpp
@@ -142,6 +142,20 @@ namespace util {
   
   
   
+  /** check if string starts with a given prefix */
+  inline bool
+  startsWith (string const& str, string const& prefix)
+  {
+    return 0 == str.rfind(prefix, 0);
+  }
+  
+  inline bool
+  startsWith (string const& str, const char* prefix)
+  {
+    return 0 == str.rfind(prefix, 0);
+  }
+  
+  
   /** shortcut for containment test on a map */
   template 
   inline bool 

From 7fcee74960478f75322f3ff8a516c65b6a75a2ba Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 5 Jul 2015 02:44:55 +0200
Subject: [PATCH 05/47] formatting helper to join a collection into a string

Ouch!
Why does C++ lack the most basic everyday stuff?
It needn't be performant. It needn't support some fancy
higher order container. Just join the f***ing strings.

use Bosst??  -- OMG!! pulls in half the metra programming library
and tries to work on any concievable range like object. Just
somehow our Lumiera Forward Iterators aren't "range-like" enough
for boost's taste.

Thus let's code up that fucking for-loop ourselves, once and forever.
---
 src/lib/format-util.hpp              | 65 ++++++++++++++++++++++++++++
 src/proc/control/memento-tie.hpp     | 34 +++++++++------
 tests/15library.tests                |  2 +
 tests/library/format-helper-test.cpp | 65 ++++++++++++++++++++++++++--
 4 files changed, 149 insertions(+), 17 deletions(-)

diff --git a/src/lib/format-util.hpp b/src/lib/format-util.hpp
index 8ead91fab..dc7ef0e5a 100644
--- a/src/lib/format-util.hpp
+++ b/src/lib/format-util.hpp
@@ -38,10 +38,12 @@
 #define LIB_FORMAT_UTIL_H
 
 #include "lib/meta/trait.hpp"
+#include "lib/itertools.hpp"
 #include "lib/symbol.hpp"
 #include "lib/util.hpp"
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -59,6 +61,7 @@ namespace util {
   using boost::enable_if;
   using lib::meta::can_ToString;
   using lib::meta::can_lexical2string;
+  using lib::meta::can_IterForEach;
   using lib::Symbol;
   using util::isnil;
   using std::string;
@@ -143,5 +146,67 @@ namespace util {
                      : tyStr(val);
   }
   
+  namespace { // helper to build range iterator on demand
+    template
+    struct _RangeIter
+      {
+        using StlIter = typename CON::const_iterator;
+        
+        lib::RangeIter iter;
+        
+        _RangeIter(CON const& collection)
+          : iter(begin(collection), end(collection))
+          { }
+      };
+    
+    template
+    struct _RangeIter >::type>
+      {
+        IT iter;
+        
+        _RangeIter(IT&& srcIter)
+          : iter(std::forward(srcIter))
+          { }
+        
+      };
+  }
+  
+  /**
+   * enumerate a collection's contents, separated by delimiter.
+   * @param coll something that is standard-iterable
+   * @return all contents converted to string and joined into
+   *         a single string, with separators interspersed.
+   * @remarks based on the \c boost::join library function,
+   *          which in turn is based on
+   *          additionally, we use our
+   *          \link #str failsafe string conversion \endlink
+   *          which in turn invokes custom string conversion,
+   *          or lexical_cast as appropriate.
+   */
+  template
+  inline string
+  join (CON&& coll, string const& delim =", ")
+  {
+    using Coll = typename lib::meta::Strip::Type;
+    using Val =  typename Coll::value_type;
+    
+    std::function toString = [] (Val const& val) { return str(val); };
+    
+    _RangeIter range(std::forward(coll));
+    auto strings = lib::transformIterator(range.iter, toString);
+    
+    if (!strings) return "";
+    
+    std::ostringstream buffer;
+    for (string const& elm : strings)
+        buffer << elm << delim;
+    
+    // chop off last delimiter
+    size_t len = buffer.str().length();
+    ASSERT (len > delim.length());
+    return buffer.str().substr(0, len - delim.length());
+  }
+  
+  
 } // namespace util
 #endif
diff --git a/src/proc/control/memento-tie.hpp b/src/proc/control/memento-tie.hpp
index 58feede94..0222f1ef8 100644
--- a/src/proc/control/memento-tie.hpp
+++ b/src/proc/control/memento-tie.hpp
@@ -45,7 +45,6 @@
 #include "lib/meta/function-closure.hpp"
 #include "proc/control/command-signature.hpp"
 #include "lib/functor-util.hpp"
-#include "lib/format-util.hpp"
 #include "lib/util.hpp"
 
 #include 
@@ -181,19 +180,8 @@ namespace control {
           return undo_ && capture_ && isCaptured_;
         }
       
-      
-      operator std::string()  const
-        {
-          if (!undo_ || !capture_)
-            return "·noUNDO·";
-          
-          if (!isCaptured_)
-            return "";
-          
-          return "<" 
-               + util::str(memento_, "mem: ", "·memento·") 
-               + ">";
-        }
+      /** for diagnostics: include format-util.hpp */
+      operator std::string()  const;
       
       
       /// Supporting equality comparisons...
@@ -211,6 +199,24 @@ namespace control {
         }
     };
   
+
+  template
+  MementoTie::operator std::string()  const
+  {
+    if (!undo_ || !capture_)
+      return "·noUNDO·";
+    
+    if (!isCaptured_)
+      return "";
+    
+    return "<"
+#ifdef LIB_FORMAT_UTIL_H
+         + util::str(memento_, "mem: ", "·memento·")
+#else
+         + std::string("memento")
+#endif
+         + ">";
+  }
   
   
   
diff --git a/tests/15library.tests b/tests/15library.tests
index 6cd619bd3..529acf342 100644
--- a/tests/15library.tests
+++ b/tests/15library.tests
@@ -187,6 +187,8 @@ out: ^he says: hey Joe!
 out: ^the truth: 0
 out: ^just a number: 1.234.*e\+56
 out: ^12345X
+out: 0.+1.1.+2.2.+3.3.+4.4.+5.5.+6.6.+7.7.+8.8.+\-\-\+\-\-9.9
+out-lit: Nr.01(0.0), Nr.02(2.2), Nr.03(4.4), Nr.04(6.6), Nr.05(8.8), Nr.06(11.0), Nr.07(13.2), Nr.08(15.4), Nr.09(17.6), Nr.10(19.8)
 return: 0
 END
 
diff --git a/tests/library/format-helper-test.cpp b/tests/library/format-helper-test.cpp
index c625cadea..ac4258a38 100644
--- a/tests/library/format-helper-test.cpp
+++ b/tests/library/format-helper-test.cpp
@@ -23,10 +23,22 @@
 
 #include "lib/test/run.hpp"
 #include "lib/format-util.hpp"
+#include "lib/format-string.hpp"
+#include "lib/iter-adapter-stl.hpp"
 #include "lib/error.hpp"
 
 #include 
+#include 
+#include 
 
+
+using lib::transformIterator;
+using lib::iter_stl::eachElm;
+using util::_Fmt;
+
+using std::vector;
+using std::string;
+using std::to_string;
 using std::cout;
 using std::endl;
 
@@ -46,22 +58,46 @@ namespace test {
   
   
   
+  class AutoCounter
+    {
+      static uint cnt;
+      
+      uint id_;
+      double d_;
+      
+    public:
+      AutoCounter(double d)
+        : id_(++cnt)
+        , d_(d*2)
+        { }
+      
+      operator string()  const
+        {
+          return _Fmt("Nr.%02d(%3.1f)") % id_ % d_;
+        }
+    };
+  uint AutoCounter::cnt = 0;
+  
+  
+  
   /***************************************************************************//**
    * @test verifies the proper working of some string-formatting helper functions.
    *       - util::str() provides a failsafe to-String conversion, preferring
    *         an built-in conversion, falling back to just a mangled type string.
    * @see format-util.hpp
    */
-  class FormatHelper_test : public Test
+  class FormatHelper_test
+    : public Test
     {
       void
       run (Arg)
         {
           check2String();
+          checkStringJoin();
         }
       
       
-      /** @test verify a failasfe to-string conversion. */
+      /** @test verify a failsafe to-string conversion. */
       void
       check2String ()
         {
@@ -82,7 +118,30 @@ namespace test {
         }
       
       
-      
+      /** @test verify delimiter separated joining of arbitrary collections.
+       *        - the first test uses a STL container, which means we need to wrap
+       *          into a lib::RangeIter. Moreover, lexical_cast is used to convert
+       *          the double numbers into strings.
+       *        - the second test uses an inline transforming iterator to build a
+       *          series of AutoCounter objects, which provide a custom string
+       *          conversion function. Moreover, since the transforming iterator
+       *          conforms to the Lumiera Forward Iterator concept, we can just
+       *          move the rvalue into the formatting function without much ado
+       */
+      void
+      checkStringJoin()
+        {
+          vector dubious;
+          for (uint i=0; i<10; ++i)
+            dubious.push_back(1.1*i);
+          
+          std::function justCount = [](double d){ return AutoCounter(d); };
+          
+          
+          cout << join(dubious, "--+--") << endl;
+          cout << join(transformIterator(eachElm(dubious)
+                                        ,justCount)) << endl;
+        }
     };
   
   LAUNCHER (FormatHelper_test, "unit common");

From b91734b0a66c2153a60abe626a281fba2541bb7f Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 4 Jun 2015 19:26:45 +0200
Subject: [PATCH 06/47] WIP: first draft -- properties of an external symbolic
 record type

This Record type is intended to play a role in the
diff description / exchange of GUI data structures.
---
 src/lib/diff/diff-language.hpp                |   8 +-
 src/lib/diff/gen-node.hpp                     |  11 +-
 src/lib/diff/record.hpp                       |  29 ++-
 src/proc/asset/entry-id.hpp                   |   2 +-
 src/proc/asset/struct-scheme.hpp              |   2 +-
 .../generic-record-representation-test.cpp    | 206 ++++++++++++++++--
 .../diff/generic-tree-representation-test.cpp |   2 +-
 wiki/renderengine.html                        |  35 ++-
 8 files changed, 250 insertions(+), 45 deletions(-)

diff --git a/src/lib/diff/diff-language.hpp b/src/lib/diff/diff-language.hpp
index 3d5352555..d28993c97 100644
--- a/src/lib/diff/diff-language.hpp
+++ b/src/lib/diff/diff-language.hpp
@@ -121,7 +121,7 @@ namespace diff{
       using Handler = HandlerFun;
     };
   
-  template
+  template          ///< alternatively derive value and interpreter from a Handler binding
   struct InterpreterScheme>
     {
       using Val = E;
@@ -266,10 +266,10 @@ namespace diff{
   
   
   /**
-   * generic builder to apply a list diff to a given target sequence.
+   * generic builder to apply a diff description to a given target data structure.
    * The usage pattern is as follows
-   * #. construct a DiffApplicator instance, wrapping the target sequence
-   * #. feed the list diff (sequence of diff verbs) to the #consume function
+   * #. construct a DiffApplicator instance, wrapping the target data
+   * #. feed the diff (sequence of diff verbs) to the #consume function
    * #. the wrapped target sequence has been altered, to conform to the given diff
    * @note a suitable DiffApplicationStrategy will be picked, based on the type
    *       of the concrete target sequence given at construction. (Effectively
diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp
index f1f8eda9b..41245df2f 100644
--- a/src/lib/diff/gen-node.hpp
+++ b/src/lib/diff/gen-node.hpp
@@ -34,7 +34,7 @@
  ** 
  ** \par Anatomy of a GenNode
  ** 
- ** GenNode is a polymorphic value with well defined identity and type-ID.
+ ** GenNode is a polymorphic value with well defined identity and type.
  ** Each element is conceived to be »unique within context« -- as defined
  ** by the immediately visible scope within a tree like structure.
  ** Beyond this identity metadata, each GenNode carries a DataCap, which
@@ -44,7 +44,7 @@
  ** will be referred by a suitable reference representation (PlacementID).
  ** The DataCap is what creates the polymorphic nature, where the common
  ** interface is mostly limited to managemental tasks (copying of values,
- ** external representation). Besides, there is are special flavours of
+ ** external representation). Besides, there are special flavours of
  ** the DataCap to represent \em sub-collections of GenNode elements.
  ** Especially, the \ref Record type is a kind of collection suitable
  ** to represent object-like structures, since it both holds several
@@ -63,7 +63,8 @@
  ** - moreover, the elements need to be values, able to be copied and handled at will
  ** - it will be beneficial for these values to support move semantics explicitly
  ** - in addition, the tree diffing suggests a mechanism to re-gain the fully
- **   typed context, based on some kind of embedded type tag
+ **   typed context, either based on some kind of embedded type tag, or
+ **   alternatively by visitation and matching
  ** - finally, the handling of changes prompts us to support installation
  **   of a specifically typed change handling closure.
  ** 
@@ -79,6 +80,10 @@
  ** dependency management, we solve this requirement with the help of a trait type,
  ** expecting the actual usage to supply the necessary specialisations on site.
  ** 
+ ** @todo the purpose and goal of the monadic approach is not clear yet (5/2015).
+ **       To begin with, for the task of diff detection and application, it is sufficient
+ **       to get the children as traversable collection
+ ** 
  ** @see diff-index-table-test.cpp
  ** @see diff-list-generation-test.cpp
  ** @see DiffDetector
diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp
index 3c859cae5..e773c1d2b 100644
--- a/src/lib/diff/record.hpp
+++ b/src/lib/diff/record.hpp
@@ -32,9 +32,32 @@
  ** which in turn serve as the standard handle to refer to other elements, entities,
  ** attributes or references within the "backbone" of the Lumiera GUI.
  ** 
- ** @see diff-index-table-test.cpp
- ** @see diff-list-generation-test.cpp
- ** @see DiffDetector
+ ** \par design decisions
+ ** The Record type is shaped from its intended use: It serves to symbolically represent
+ ** \em objects in the "external tree description". Here, \em objects means objects for
+ ** real, i.e. with types, fields and an enclosed scope. But \em external means that we
+ ** do not work on these objects right here, we only represent them, for later referral,
+ ** \em symbolically.
+ ** 
+ ** This leads to the following decisions
+ ** - the Record entity is itself an object and thus has an inner side, privately.
+ **   The entrails of the Record can be reworked and tuned for performance
+ ** - yet the Record has an external appearance, which makes it look flat and passive.
+ **   This is to say, a Record has no visible functionality.
+ ** - the parts or \em realms within this symbolic representation are distinguished
+ **   by convention solely
+ **   
+ **   * metadata is very limited and boils down to magic attributes known by name
+ **   * children (scope contents) can be recognised by \em not bearing a name
+ ** 
+ ** Record entities are meant to be immutable. The proper way to alter a Record is
+ ** to apply a \link tree-diff.hpp diff \endlink
+ ** 
+ ** \par rationale
+ ** The underlying theme of this design is negative, dialectical: we do not want to
+ ** build yet another object system. The object model of C++ is deemed adequate.  
+ ** 
+ ** @see GenericRecordRepresentation_test
  ** 
  */
 
diff --git a/src/proc/asset/entry-id.hpp b/src/proc/asset/entry-id.hpp
index 2e3011f7c..cef31a098 100644
--- a/src/proc/asset/entry-id.hpp
+++ b/src/proc/asset/entry-id.hpp
@@ -193,7 +193,7 @@ namespace asset {
       
       /** case-1: auto generated symbolic ID */
       EntryID()
-        : BareEntryID (idi::generateSymbolID(), getTypeHash())
+        : BareEntryID (idi::generateSymbolID(), getTypeHash())        /////////////TICKET #565 : how to organise access; this is not thread safe
         { }
       
       /** case-2: explicitly specify a symbolic ID to use.
diff --git a/src/proc/asset/struct-scheme.hpp b/src/proc/asset/struct-scheme.hpp
index 35bb9a095..5730d08b6 100644
--- a/src/proc/asset/struct-scheme.hpp
+++ b/src/proc/asset/struct-scheme.hpp
@@ -156,7 +156,7 @@ namespace asset{
         static format namePattern ("%s.%03d");
         ////////////////////////////////////////////////////////////////////////////////TICKET #166 : needs to be pushed down into a *.cpp
         
-        return str(namePattern % StructTraits::namePrefix() % (++i) );
+        return str(namePattern % StructTraits::namePrefix() % (++i) );  //////////TICKET #565 : how to organise access; this ought to be thread safe (-> EntryID )
     }
     
     
diff --git a/tests/library/diff/generic-record-representation-test.cpp b/tests/library/diff/generic-record-representation-test.cpp
index 95e59876e..af036cc04 100644
--- a/tests/library/diff/generic-record-representation-test.cpp
+++ b/tests/library/diff/generic-record-representation-test.cpp
@@ -24,14 +24,19 @@
 #include "lib/test/run.hpp"
 #include "lib/test/test-helper.hpp"
 #include "lib/diff/record.hpp"
+#include "lib/util.hpp"       //////TODO necessary?
 
+#include 
 //#include 
 //#include 
-//#include 
+#include 
 
 //using std::string;
-//using std::vector;
+using util::isSameObject;
+using std::vector;
 //using std::swap;
+using std::cout;
+using std::endl;
 
 
 namespace lib {
@@ -46,6 +51,8 @@ namespace test{
     
   }//(End)Test fixture
   
+  using Seq  = vector;
+  using RecS = Record;
   
   
   
@@ -53,10 +60,9 @@ namespace test{
   
   
   
-  
-  /*****************************************************************************//**
+  /*************************************************************************************//**
    * @test Verify properties of a special collection type meant for external representation
-   *       of object-loke data.
+   *       of object-like data.
    *       
    * @see IndexTable
    * @see DiffListApplication_test
@@ -68,40 +74,198 @@ namespace test{
       run (Arg)
         {
           simpleUsage();
-          verifySnapshot();
-          sequenceIteration();
-          duplicateDetection();
+          verifyCreation();
+          verifyMutations();
           copy_and_move();
+          equality();
         }
       
       
       void
       simpleUsage()
         {
+          RecS enterprise("starship"
+                         , {"Name = USS Enterprise"
+                           ,"Registry = NCC-1701-D"
+                           ,"Class = Galaxy"
+                           ,"Owner = United Federation of Planets"
+                           ,"built=2363"
+                           }
+                         , {"Picard", "Riker", "Data", "Worf", "Troi", "Crusher", "La Forge"}
+                         );
+          
+          CHECK (enterprise.getType() == "starship");
+          CHECK (enterprise.get("Registry") == "NCC-1701-D");
+          
+          CHECK (enterprise.hasAttribute("Owner"));
+          CHECK (!enterprise.hasAttribute("owner"));
+          CHECK (!enterprise.hasAttribute("Owner "));
+          
+          CHECK (enterprise.contains("Data"));
+          CHECK (!contains (enterprise, "Woof"));
+          CHECK (util::contains (enterprise, "Worf"));
+          
+          VERIFY_ERROR (INVALID, enterprise.get("warp10"));
+          
+          cout << "enterprise = " << string(enterprise)<
 
-
+
//to symbolically represent hierarchically structured elements, without actually implementing them.//
 The purpose of this »external« description is to remove the need of a central data model to work against. We consider such a foundation data model as a good starting point, yet harmful for the evolution of any larger structure to be built. According to the subsidiarity principle, we prefer to turn the working data representation into a local concern. Which leaves us with the issue of collaboration. Any collaboration requires, as an underlying, some kind of common understanding. And any formalised, mechanical collaboration requires to represent that common point of attachment -- at least as symbolic representation. The ExternalTreeDescription is shaped to fulfil this need: //in theory,// the whole field could be represented, symbolically, as a network of hierarchically structured elements. Yet, //in practice,// all we need is to conceive the presence of such a representation, as a backdrop to work against. And we do so -- we work against that symbolic representation, by describing ''changes'' made to the structure and its elements. Thus, the description of changes, the ''diff language'', refers to and partially embodies such symbolically represented elements and relations.
 &rarr; TreeDiffFundamentals
@@ -2019,7 +2019,7 @@ This decision more or less limits the usefulness of simple values as children to
 The discriminating ID of any GenNode can serve as a name, and indeed will be used as the name of an attribute within a record.
 Now, the interesting question is: what constitues the full identity? Is it the ~ID-string? does it also include some kind of type information, so that two children with the same name, but different types would be considered different? And, moreover, we could even go as far as to make the path part of the identity, so that two otherwise identical elements would be different, when located at different positions within the graph. But since we did not rule out cyclic graphs, the latter idea would necessitate the notion of an //optimal path// --
 
-A somewhat similar question is the ordering and uniqueness of children. While attributes -- due to the usage of the attribute node's ID as name-key -- are bound to be unique within a given Record, children within the scope of a record could be required to be unique too, making the scope a set. And, of course, children could be focribly ordered, or just retaining the initial ordering, or even be completely unordered. On a second thought, it seems wise not to impose any guarantees in that regard, beyond the simple notion of retaining an initial sequence order. All these more specific ordering properties can be considered the concern of some specific kinds of objects -- which then just happen to "supply" a list of children for symbolic representation as they see fit.
+A somewhat similar question is the ordering and uniqueness of children. While attributes -- due to the usage of the attribute node's ID as name-key -- are bound to be unique within a given Record, children within the scope of a record could be required to be unique too, making the scope a set. And, of course, children could be focribly ordered, or just retain the initial ordering, or even be completely unordered. On a second thought, it seems wise not to impose any guarantees in that regard, beyond the simple notion of retaining an initial sequence order, the way a »stable« sorting algorithm does. All these more specific ordering properties can be considered the concern of some specific kinds of objects -- which then just happen to "supply" a list of children for symbolic representation as they see fit.
 
@@ -2316,9 +2316,9 @@ For this Lumiera design, we could consider making GOP just another raw media dat &rarr;see in [[Wikipedia|http://en.wikipedia.org/wiki/Group_of_pictures]]
-
+
//Abstract generic node element to build a ~DOM-like rendering of Lumiera's [[session model|HighLevelModel]].//
-GenNode elements are values, yet behave polymorphic. They are rather light-weight, have an well established identity and can be compared. They are //generic// insofar they encompass several heterogeneous ordering systems, which in themselves can not be subsumed into a single ordering hierarchy. The //purpose// of these generic nodes is to build a symbolic representation, known as [[external tree description|ExternalTreeDescription]], existing somewhere "outside", at a level where the fine points of ordering system relations do not really matter. Largely, this external description is not represented or layed down as a whole. Rather, it is used as a conceptual point of reference to describe //differences and changes.// Obviously, this means that parts of the altered structures have to appear in the description of the modifications. So, practically speaking, the prime purpose of GenNode elements is to appear as bits of information within a ''diff language'' to exchange such information of changes.
+GenNode elements are values, yet behave polymorphic. They are rather light-weight, have an well established identity and can be compared. They are //generic// insofar they encompass several heterogeneous ordering systems, which in themselves can not be subsumed into a single ordering hierarchy. The //purpose// of these generic nodes is to build a symbolic representation, known as [[external tree description|ExternalTreeDescription]], existing somewhere "outside", at a level where the fine points of ordering system relations do not really matter. Largely, this external description is not represented or layed down as a whole. Rather, it is used as a conceptual reference frame to describe //differences and changes.// Obviously, this means that parts of the altered structures have to appear in the description of the modifications. So, practically speaking, the prime purpose of GenNode elements is to appear as bits of information within a ''diff language'' to exchange such information of changes.
 
 To be more specific, within the actual model there are [[Placements|Placement]]. These refer to [[M-Objects|MObject]]. Which in turn rely on [[Assets|Asset]]. Moreover, we have some processing rules, and -- last but not least -- the "objects" encountered in the model have state, visible as attributes of atomic value type (integral, floating point, string, boolean, time, time ranges and [[quantised time entities|TimeQuant]]).
 A generic node may //represent any of these kind// -- and it may have ~GenNode children, forming a tree. Effectively all of this together makes ~GenNode a ''Monad''.
@@ -2444,7 +2444,7 @@ The workspace starts out with a single element, corresponding to the »model roo
 Speaking of implementation, this state and update mechanics relies on two crucial provisions: Lumiera's framework for [[tree diff representation|TreeDiffModel]] and the ExternalTreeDescription, which is an abstracted, ~DOM-like rendering of the relevant parts of the session; this model tree is comprised of [[generic node elements|GenNode]] acting as proxy for [[calls into|SessionInterface]] the [[session model|HighLevelModel]] proper.
 
-
+
Considerations regarding the [[structure of custom timeline widgets|GuiTimelineWidgetStructure]] highlight again the necessity of a clean separation of concerns and an "open closed design". For the purpose of updating the timeline(s) to reflect the HighLevelModel in Proc-Layer, several requirements can be identified
 * we need incremental updates: we must not start redrawing each and everything on each tiny change
 * we need recursive programming, since this is the only sane way to deal with tree like nested structures.
@@ -2479,6 +2479,9 @@ A relevant question to be settled is as to where the core of each change is cons
 * we might, at the moment of performing the update, acquire a lock from the ProcDispatcher. The update process may then effectively query down into the session datastructure proper, even through the proxy of a diffing process. The obvious downside is that GUI response might block waiting on an extended operation in Proc, especially when a new build process was started meanwhile. A remedy might be to abort the update in such cases, since its effects will be obsoleted by the build process anyway.
 * alternatively, we might incorporate a complete snapshot of all information relevant for the GUI into the GuiModel. Update messages from Proc must be complete and self contained in this case, since our goal is to avoid callbacks. Following this scheme, the first stage of any update would be a push from Proc to the GuiModel, followed by a callback pull from within the individual widgets receiving the notification later. This is the approach we choose for the Lumiera GUI.
 
+!!!information to represent and to derive
+The purpose of the GuiModel is to represent an anchor point for the structures //actually relevant for the UI.// To put that into context, the model in the session is not bound to represent matters exactly in the way to be rendered within the GUI. All we can expect is for the //build process// -- upon completion -- to generate a view of the actually altered parts, detailing the information relevant for presentation. Thus we do retain an ExternalTreeDescription of all the information received this way within the GuiModel. Whenever a completed build process sends an updated state, we use the diff framework to determine the actually relevant differences -- both for triggering the corresponding GUI widgets, and for forwarding this focussed diff information to these widgets when they call back from the UI event thread to pull the actual changes.
+
 !!!switch of typed sub-context
 When dealing with structural (tree) diffing, there is a specific twist regarding child nodes of mixed type: In the general case, we can not assume that all children of a given node are of the same kind. The classical example is (X)HTML, where a node has //attributes,// various //nested tags// and //nested text content.// The //generic node// thus must be modelled as having several collections of children -- both ordered and un-ordered collections are possible -- and the content of each such sub-collection is itself polymorphic. This constitutes a challenge for the representation of data within the tree diff format. These difficulties can be overcome as follows
 #anything, even nested "raw" content is represented //as node//
@@ -7514,7 +7517,7 @@ Thus no server and no network connection is needed. Simply open the file in your
  * see [[Homepage|http://tiddlywiki.com]], [[Wiki-Markup|http://tiddlywiki.org/wiki/TiddlyWiki_Markup]]
 
-
+
Simple time points are just like values and thus easy to change. The difficulties arise when time values are to be //quantised to an existing time grid.// The first relevant point to note is that for quantised time values, the effect of a change can be disproportional to the cause. Small changes below the threshold might be accumulated, and a tiny change might trigger a jump to the next grid point. While this might be annoying, the yet more complex questions arise when we acknowledge that the amount of the change itself might be related to a time grid.
 
 The problem with modification of quantised values highlights an inner contradiction or conflicting goals within the design
@@ -7590,7 +7593,7 @@ Rationale: allowing mutations for Time bears the danger of making ~TimeVar obsol
 * err, because MObject will include a Duration separate from the start time in the Placement, Duration needs to be mutable too
 
 !!!usage considerations
-{{red{Question 5/11}}} when moving a clip taken from 50fps media, the new position might be quantised to the 50fps grid established by the media, while the target timeline runs with 25fps, allowing for finer adjustments based on the intermediate frames present in the source material.
+{{red{Question 5/11}}} when moving a clip taken from 50fps media, the new position might be quantised to the 50fps grid established by the media, even when the target timeline runs with 25fps, allowing for finer adjustments based on the intermediate frames present in the source material.
 
 !!!design draft
 The special focus of this problem seems to lead itself to a __visitor pattern__ based implementation. Because the basic hierarchy of applicable types is fixed, but the behaviour is open ended (and not yet fully determined). A conventional implementation would scatter this behaviour over all the time entities, thus making it hard to understand and reason about. The classical ~GoF cyclic visitor solution to the contrary allows us to arrange closely related behaviour into thematically grouped visitor classes. As a plus, the concrete action can be bound dynamically, allowing for more flexibility when it comes to dealing with the intricate situations when a quantised time span (= a clip) recieves a quantised mutation (= is re-alligend to a possibly different frame grid)
@@ -7940,7 +7943,7 @@ Used this way, diff representation helps to separate structure and raw data in e
 :Chunks of raw data are attached inline to the structural diff, assuming that each element implicitly knows the kind of data to expect
 
-
+
//This page details decisions taken for implementation of Lumiera's diff handling framework//
 This topic is rather abstract, since diff handling is multi purpose within Lumiera: Diff representation is seen as a meta language and abstraction mechanism; it enables tight collaboration without the need to tie and tangle the involved implementation data structures. Used this way, diff representation reduces coupling and helps to cut down overall complexity -- so to justify the considerable amount of complexity seen within the diff framework implementation.
 
@@ -7993,6 +7996,16 @@ This design prefers the //pull// approach, with a special twist: we provide a co
 !!!representation of objects
 It should be noted, that the purpose of this whole architecture is to deal with »remote« stuff -- things we somehow need to refer and deal with, but nothing we can influence immediately, right here: every actual manipulation has to be turned into a message and sent //elsewhere.// This is the only context, where some, maybe even partial, generic and introspective object representation makes sense.
 
+{{red{open questions 6/15}}}
+* do we need to //alter// object contents -- or do we just replace?
+* to what degree is the distinction between attributes and children even relevant -- beyond the ability to address attributes by-name?
+* how do we describe an object from scratch?
+* how do we represent the break between attributes and children in this linearised description?
+** using a separator element?
+** by convention through the element names?
+** as additional metadata information sent beforehand?
+* we need an object-reference element, since we do not want to copy whole subtrees while processing a diff
+
 Within this framework, we represent //object-like// entities through a special flavour of the GenNode: Basically, an object is a flat collection of children, yet given in accordance to a distinct protocol. The relevant ''meta'' information is spelled out first, followed by the ''attributes'' and finally the ''children''. The distinction between these lies in the mode of handling. Meta information is something we need to know before we're able to deal with the actual stuff. Prominent example is the type of the object. Attributes are considered unordered, and will typically be addressed by-name. Children are an ordered collection of recursive instances of the same data structure. (Incidentally, we do not rule out the possibility that also an attribute holds a recursive subtree; only the mode of access is what makes the distinction).
 
 !!!handling of actual mutation
@@ -8064,7 +8077,7 @@ On receiving the terms of this "diff language", it is possible to gene
 i.e. a ''unified diff'' or the ''predicate notation'' used above to describe the list diffing algorithm, just by accumulating changes.
 
-
+
The TreeMutator is an intermediary to translate a generic structure pattern into heterogeneous local invocation sequences.
 
 !Motivation
@@ -8117,7 +8130,7 @@ This construction pattern can be extended to offer several optional extension ho
 * how to integrate typed children
 
 !!!working with children
-Handling tree structured object data imposes some additional constraints, in comparision to generic changes done to a flat list. One notable difference is that there are pre-existing //attributes,// which can not be added and deleted, are known by-name, not by positional order. Another point worth noting is the fact that child objects may be segregated into several collections by type. Since our goal is to provide an intermediary with the ability to map to arbitrary structures, we need to define the primitive operations necessary for implementing the structural operations represented in the form of a diff
+Handling tree structured object data imposes some additional constraints, in comparision to generic changes done to a flat list. One notable difference is that there are pre-existing //attributes,// which can not be added and deleted, are known by-name, not by positional order. Another point worth noting is the fact that child objects may be segregated into several collections by type. Since our goal is to provide an intermediary with the ability to map to arbitrary structures, we need to define the primitive operations necessary for implementing the concrete structural operations represented in the form of a diff
 * add a child
 * remove a child
 * step to the next child
@@ -8129,7 +8142,7 @@ All these basic operations are implicitly stateful, i.e. they work against an as
 To ease the repetitive part of the wiring, which is necessary for each individual application case, we can allow for some degree of //duck typing,// as far as building the TreeMutator is concerned. If there is a type, which provides the above mentioned functions for child management, these can be hooked up automatically into a suitable adapter. Otherwise, the client may supply closures, using the same definition pattern as shown for the attributes above. Here, the ID argument is optional and denotes a //type filter,// whereas the closure itself must accept a name-ID argument. The purpose of this construction is the ability to manage collections of similar children. For example
 {{{
        .addChild("Fork"), [&](string type, string id) {
-           ForkType kind = determineForkkType(type);
+           ForkType kind = determineForkType(type);
            this.forks_.push_back(MyForkImpl(kind, id);
          })
        .mutateChild("Fork"), [&](string id) {

From 96e10faa84db652e21eb9bcabef5f388277b4de3 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 5 Jun 2015 19:17:39 +0200
Subject: [PATCH 07/47] WIP: first round of stubbing for diff::Record

---
 src/lib/diff/record.hpp                       | 192 +++++++++++++++++-
 .../generic-record-representation-test.cpp    |  77 ++++---
 2 files changed, 243 insertions(+), 26 deletions(-)

diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp
index e773c1d2b..ac217c7e3 100644
--- a/src/lib/diff/record.hpp
+++ b/src/lib/diff/record.hpp
@@ -67,10 +67,16 @@
 
 
 #include "lib/error.hpp"
+#include "lib/iter-adapter.hpp"
+#include "lib/iter-adapter-stl.hpp"
+#include "lib/itertools.hpp"
+
 //#include "lib/util.hpp"
 //#include "lib/format-string.hpp"
 
-//#include 
+#include 
+#include 
+#include 
 //#include 
 
 
@@ -80,6 +86,7 @@ namespace diff{
   namespace error = lumiera::error;
   
 //using util::_Fmt;
+  using std::string;
   
   
   
@@ -87,8 +94,191 @@ namespace diff{
   template
   class Record
     {
+      using _Vec = std::vector;
+      using Attrib = std::pair;
+      using Attribs = _Vec;
+      using Children = _Vec;
+      
+      string type_;
+      Attribs attribs_;
+      Children children_;
       
     public:
+      Record()
+        : type_("NIL")
+        { }
+      
+      template
+      Record(Symbol typeID, A&& att, C&& chi)
+        : type_(typeID)
+        , attribs_(std::forward (att))
+        , children_(std::forward (chi))
+        { }
+      
+      template
+      Record(Symbol typeID, std::initializer_list const&& att
+                          , std::initializer_list const&& chi)
+        : type_(typeID)
+        , attribs_(att)
+        , children_(chi)
+        { }
+      
+      template
+      explicit
+      Record (SEQ const& con)
+        : type_("NIL")
+        {
+          auto p = std::begin(con);
+          auto e = std::end(con);
+          if (p!=e && isTypeID (*p))
+            type_ = extractTypeID(*(p++));
+          for ( ; p!=e && isAttribute(*p); ++p)
+            attribs_.push_back (*p);
+          for ( ; p!=e; ++p)
+            children_.push_back (*p);
+        }
+      
+      Record (std::initializer_list const&& ili)
+        : Record(ili)
+        { }
+      
+      // all default copy operations acceptable
+      
+      
+      operator std::string()  const
+        {
+          return "nebbich";  ////TODO
+        }
+      
+      
+      bool
+      empty()  const
+        {
+          return attribs_.empty()
+              && children_.empty();
+        }
+      
+      string
+      getType()  const
+        {
+          return type_;
+        }
+      
+      bool
+      hasAttribute (string key)  const
+        {
+          return false; ////TODO
+        }
+      
+      bool
+      contains (VAL const& ref)  const
+        {
+          return false; ////TODO
+        }
+      
+      VAL const&
+      get (string key)  const
+        {
+          return "booo"; ////TODO
+        }
+      
+      
+      /* ==== Exposing scope and contents for iteration ====== */
+      
+      using iterator  = IterAdapter;
+      using scopeIter = typename iter_stl::_SeqT<_Vec>::Range;
+      using keyIter   = TransformIter;
+      using valIter   = TransformIter;
+      
+      
+      iterator  begin () const { return iterator(this, attribs_.begin()); }
+      iterator  end ()   const { return iterator(); }
+      
+      
+      scopeIter attribs() const { return iter_stl::eachElm(attribs_); }
+      scopeIter scope()  const { return iter_stl::eachElm(children_); }
+      
+      keyIter keys()  const { return transformIterator(attribs(), extractKey); }
+      valIter vals()  const { return transformIterator(attribs(), extractVal); }
+      
+    protected: /* ==== API for the IterAdapter ==== */
+      
+      /** Implementation of Iteration-logic: pull next element. */
+      template
+      friend void
+      iterNext (const Record* src, ITER& pos)
+        {
+          ++pos;
+          checkPoint (src,pos);
+        }
+      
+      /** Implementation of Iteration-logic: detect iteration end.
+       * @remarks seamless continuation of the iteration when reaching
+       *    the end of the attribute collection. In this implementation,
+       *    we use the default constructed \c ITER() to mark iteration end.
+       */
+      template
+      friend bool
+      checkPoint (const Record* src, ITER& pos)
+        {
+          REQUIRE (src);
+          if ((pos != ITER()) && (pos != src->children_.end()))
+            return true;
+          else
+            if (pos != ITER() && (pos == src->attribs_.end()) && !src->children_.empty())
+              {
+                pos = src->children_.begin();
+                return true;
+              }
+            else
+              {
+                pos = ITER();
+                return false;
+        }     }
+      
+    private:
+      static bool
+      isAttribute (VAL const& v)
+        {
+          return false; ////TODO
+        }
+      
+      static bool
+      isTypeID (VAL const& v)
+        {
+          return false; ////TODO
+        }
+      
+      static string
+      extractTypeID (VAL const& v)
+        {
+          return "todo"; ////TODO
+        }
+      
+      static string
+      extractKey (VAL const& v)
+        {
+          return "todo"; ////TODO
+        }
+      
+      static VAL
+      extractVal (VAL const& v)
+        {
+          return VAL(); ///TODO
+        }
+      
+      
+      friend bool
+      operator== (Record const& r1, Record const& r2)
+      {
+        return false; ////TODO
+      }
+      
+      friend bool
+      operator!= (Record const& r1, Record const& r2)
+      {
+        return ! (r1 == r2);
+      }
     };
   
   
diff --git a/tests/library/diff/generic-record-representation-test.cpp b/tests/library/diff/generic-record-representation-test.cpp
index af036cc04..e4ad79e17 100644
--- a/tests/library/diff/generic-record-representation-test.cpp
+++ b/tests/library/diff/generic-record-representation-test.cpp
@@ -24,15 +24,17 @@
 #include "lib/test/run.hpp"
 #include "lib/test/test-helper.hpp"
 #include "lib/diff/record.hpp"
+#include "lib/itertools.hpp"
 #include "lib/util.hpp"       //////TODO necessary?
 
 #include 
 //#include 
-//#include 
+#include 
 #include 
 
-//using std::string;
+using std::string;
 using util::isSameObject;
+using util::isnil;
 using std::vector;
 //using std::swap;
 using std::cout;
@@ -44,16 +46,41 @@ namespace diff{
 namespace test{
   
 //  using lumiera::error::LUMIERA_ERROR_LOGIC;
+  using lumiera::error::LUMIERA_ERROR_INVALID;
   
   namespace {//Test fixture....
     
+    using Seq  = vector;
+    using RecS = Record;
+    
+    template
+    inline Seq
+    contents (IT const& it)
+    {
+      Seq collected;
+      append_all (it, collected);
+      return collected;
+    }
+    
+    inline Seq
+    contents (RecS const& rec_of_strings)
+    {
+      return contents (rec_of_strings.begin());
+    }
+    
+    template
+    inline Seq
+    strings (std::initializer_list const& con)
+    {
+      Seq collected;
+      for (auto elm : con)
+        collected.push_back(elm);
+      return collected;
+    }
     
     
   }//(End)Test fixture
   
-  using Seq  = vector;
-  using RecS = Record;
-  
   
   
   
@@ -85,13 +112,13 @@ namespace test{
       simpleUsage()
         {
           RecS enterprise("starship"
-                         , {"Name = USS Enterprise"
-                           ,"Registry = NCC-1701-D"
-                           ,"Class = Galaxy"
-                           ,"Owner = United Federation of Planets"
-                           ,"built=2363"
-                           }
-                         , {"Picard", "Riker", "Data", "Worf", "Troi", "Crusher", "La Forge"}
+                         , strings ({"Name = USS Enterprise"
+                                    ,"Registry = NCC-1701-D"
+                                    ,"Class = Galaxy"
+                                    ,"Owner = United Federation of Planets"
+                                    ,"built=2363"
+                                   })
+                         , strings ({"Picard", "Riker", "Data", "Troi", "Worf", "Crusher", "La Forge"})
                          );
           
           CHECK (enterprise.getType() == "starship");
@@ -102,15 +129,15 @@ namespace test{
           CHECK (!enterprise.hasAttribute("Owner "));
           
           CHECK (enterprise.contains("Data"));
-          CHECK (!contains (enterprise, "Woof"));
+          CHECK (!enterprise.contains("Woof"));
           CHECK (util::contains (enterprise, "Worf"));
           
           VERIFY_ERROR (INVALID, enterprise.get("warp10"));
           
           cout << "enterprise = " << string(enterprise)<
Date: Sat, 6 Jun 2015 00:11:12 +0200
Subject: [PATCH 08/47] WIP: const correctnes: Record is conceived as immutable

...and so should be all the exposed iterators.
Thanks, dear C++ compiler for spotting this subtle mismatch!
---
 src/lib/diff/record.hpp                                   | 2 +-
 tests/library/diff/generic-record-representation-test.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp
index ac217c7e3..20ff4790a 100644
--- a/src/lib/diff/record.hpp
+++ b/src/lib/diff/record.hpp
@@ -186,7 +186,7 @@ namespace diff{
       /* ==== Exposing scope and contents for iteration ====== */
       
       using iterator  = IterAdapter;
-      using scopeIter = typename iter_stl::_SeqT<_Vec>::Range;
+      using scopeIter = typename iter_stl::_SeqT::Range;
       using keyIter   = TransformIter;
       using valIter   = TransformIter;
       
diff --git a/tests/library/diff/generic-record-representation-test.cpp b/tests/library/diff/generic-record-representation-test.cpp
index e4ad79e17..549bd8ed6 100644
--- a/tests/library/diff/generic-record-representation-test.cpp
+++ b/tests/library/diff/generic-record-representation-test.cpp
@@ -190,7 +190,7 @@ namespace test{
           CHECK (!isSameObject (a.get("a"), b.get("a")));
           CHECK (!isSameObject (*a.scope(), *b.scope()));
           
-          string& c = *b.scope();
+          string const& c = *b.scope();
           CHECK ("c" == c);
           
           RecS bb;

From e664ea552fa90fa70694df5aec88c27b82bfd889 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 6 Jun 2015 01:17:42 +0200
Subject: [PATCH 09/47] stub the Record::Mutator implementation

passes compiler again
---
 src/lib/diff/record.hpp                       | 114 ++++++++++++++++--
 .../generic-record-representation-test.cpp    |   2 +-
 2 files changed, 108 insertions(+), 8 deletions(-)

diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp
index 20ff4790a..5f87da6ad 100644
--- a/src/lib/diff/record.hpp
+++ b/src/lib/diff/record.hpp
@@ -73,6 +73,7 @@
 
 //#include "lib/util.hpp"
 //#include "lib/format-string.hpp"
+#include 
 
 #include 
 #include 
@@ -182,6 +183,34 @@ namespace diff{
           return "booo"; ////TODO
         }
       
+      /**
+       * While otherwise immutable,
+       * a Record object can be remoulded
+       * with the help of a Mutator object
+       * @remarks a Mutator basically wraps a \em copy
+       *          of the original object. After performing
+       *          the desired changes, the altered copy can either
+       *          be sliced out (by conversion), or moved overwriting
+       *          an existing other Record instance (implemented as swap)
+       */
+      class Mutator;
+      
+      
+      /**
+       * copy-initialise (or convert) from the given Mutator instance.
+       * @remarks need to code this explicitly, otherwise Record's
+       *          build-from sequence templated ctor would kick in.
+       */
+      Record (Mutator const& mut)
+        : Record((Record const&) mut)
+        { }
+      Record (Mutator && mut)
+        : Record(std::move ((Record) mut))
+        { }
+      
+      friend class Mutator;
+      
+      
       
       /* ==== Exposing scope and contents for iteration ====== */
       
@@ -190,16 +219,15 @@ namespace diff{
       using keyIter   = TransformIter;
       using valIter   = TransformIter;
       
-      
-      iterator  begin () const { return iterator(this, attribs_.begin()); }
-      iterator  end ()   const { return iterator(); }
-      
+      /** default iteration exposes all data within this "object", starting with the attributes */
+      iterator  begin ()  const { return iterator(this, attribs_.begin()); }
+      iterator  end ()    const { return iterator(); }
       
       scopeIter attribs() const { return iter_stl::eachElm(attribs_); }
-      scopeIter scope()  const { return iter_stl::eachElm(children_); }
+      scopeIter scope()   const { return iter_stl::eachElm(children_); }
       
-      keyIter keys()  const { return transformIterator(attribs(), extractKey); }
-      valIter vals()  const { return transformIterator(attribs(), extractVal); }
+      keyIter   keys()    const { return transformIterator(attribs(), extractKey); }
+      valIter   vals()    const { return transformIterator(attribs(), extractVal); }
       
     protected: /* ==== API for the IterAdapter ==== */
       
@@ -255,6 +283,12 @@ namespace diff{
           return "todo"; ////TODO
         }
       
+      static VAL
+      buildTypeAttribute (string const& typeID)
+        {
+          return VAL(); ///TODO
+        }
+      
       static string
       extractKey (VAL const& v)
         {
@@ -283,6 +317,72 @@ namespace diff{
   
   
   
+  template
+  class Record::Mutator
+    : boost::noncopyable
+    {
+      using Rec = Record;
+      
+      Rec record_;
+      
+    public:
+      explicit
+      Mutator (Rec const& startingPoint)
+        : record_(startingPoint)
+        { }
+      
+      explicit
+      Mutator (Rec && startingPoint)
+        : record_(std::move (startingPoint))
+        { }
+      
+      operator Rec&()
+        {
+          return record_;
+        }
+      
+      void
+      replace (Rec& existingInstance)  noexcept
+        {
+          std::swap (existingInstance, record_);
+        }
+      
+      
+      /* === functions to alter contents === */
+      
+      void
+      setType (string const& newTypeID)
+        {
+          set ("type", Rec::buildTypeAttribute (newTypeID));
+          record_.type_ = newTypeID;
+        }
+      
+      void
+      set (string const& key, VAL const& newValue)
+        {
+          ///////////TODO;
+        }
+      
+      void
+      appendChild (VAL const& newChild)
+        {
+          record_.children_.push_back (newChild);
+        }
+      
+      void
+      prependChild (VAL const& newChild)
+        {
+          record_.children_.insert (record_.children_.begin(), newChild);
+        }
+      
+      bool
+      empty()  const
+        {
+          return record_.empty();
+        }
+    };
+  
+  
   
 }} // namespace lib::diff
 #endif /*LIB_DIFF_GEN_NODE_H*/
diff --git a/tests/library/diff/generic-record-representation-test.cpp b/tests/library/diff/generic-record-representation-test.cpp
index 549bd8ed6..13f0da01f 100644
--- a/tests/library/diff/generic-record-representation-test.cpp
+++ b/tests/library/diff/generic-record-representation-test.cpp
@@ -273,7 +273,7 @@ namespace test{
           mut.appendChild("a");
           mut.set("a", "1");
           
-          RecS aa(mut);
+          RecS aa(std::move(mut));
           CHECK (a != aa);
           CHECK ("u" == aa.getType());
           CHECK (Seq({"type=u", "a=1", "a"}) == contents(aa));

From cecb5db97270efc8567aefce1c33966d6554829f Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 6 Jun 2015 02:40:18 +0200
Subject: [PATCH 10/47] settle on an approach for handling attributes

Initially, I considered to build an index table like
collection of ordered attributes. But since our actual
use case is Record, this was ruled out in favour
of just a vector, where the keys are embedded
right within the nameID-Field of GenNode.

A decisive factor was the observation, that this design
is basically forced to encode the attribute keys somehow
into the attribute values, because otherwise the whole
collection like initialisation and iteration would break
down. Thus, a fully generic implementation is not possible,
and a pseudo generic implementation just for the purpose of
writing unit tests would be overkill.

Basically this decision means that Record requires an
explicit specialisation to implement the attribute-key
binding for each value type to use.
---
 src/lib/diff/gen-node.hpp |  44 +++++++++++++
 src/lib/diff/record.hpp   | 129 +++++++++++++++++++++++++++-----------
 2 files changed, 135 insertions(+), 38 deletions(-)

diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp
index 41245df2f..cfce16e56 100644
--- a/src/lib/diff/gen-node.hpp
+++ b/src/lib/diff/gen-node.hpp
@@ -157,6 +157,50 @@ namespace diff{
   
   
   
+  /* === Specialisation for handling of attributes in Record === */
+  
+  template<>
+  inline bool
+  Record::isAttribute (GenNode const& v)
+  {
+    return false; ////TODO
+  }
+  
+  template<>
+  inline bool
+  Record::isTypeID (GenNode const& v)
+  {
+    return false; ////TODO
+  }
+  
+  template<>
+  inline string
+  Record::extractTypeID (GenNode const& v)
+  {
+    return "todo"; ////TODO
+  }
+  
+  template<>
+  inline GenNode
+  Record::buildTypeAttribute (string const& typeID)
+  {
+    return GenNode(); ///TODO
+  }
+  
+  template<>
+  inline string
+  Record::extractKey (GenNode const& v)
+  {
+    return "todo"; ////TODO
+  }
+  
+  template<>
+  inline GenNode
+  Record::extractVal (GenNode const& v)
+  {
+    return GenNode(); ///TODO
+  }
+  
   
 }} // namespace lib::diff
 #endif /*LIB_DIFF_GEN_NODE_H*/
diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp
index 5f87da6ad..3b6f2bb9c 100644
--- a/src/lib/diff/record.hpp
+++ b/src/lib/diff/record.hpp
@@ -55,7 +55,25 @@
  ** 
  ** \par rationale
  ** The underlying theme of this design is negative, dialectical: we do not want to
- ** build yet another object system. The object model of C++ is deemed adequate.  
+ ** build yet another object system. The object model of C++ is deemed adequate.
+ ** 
+ ** @remarks
+ **  - the implementation is focused on the intended primary use case,
+ **    which is to exchange diff messages drawn against a symbolic representation
+ **    of a typed object tree. Especially, we assume that there is only a small
+ **    number of attributes (so linear search for access by key is adequate).
+ **  - moreover, we assume that the value type allows to somehow to embed
+ **    the key of each attribute; the implementation needs an explicit
+ **    specialisation of the binding functions for each value type.
+ **  - this header defines a specialisation for VAL = std::string --
+ **    while the most relevant specialisation for GenNode is provided
+ **    alongside with this special, monadic value type.
+ **  - an alternative implementation approach would have been to use a
+ **    dedicated helper type to represent the collection of attributes.
+ **    This type might then be specialised, e.g. to utilise an index table
+ **    for key-value lookup. However, in the light of the intended usage
+ **    of Record entities as tree nodes within a GenNode monad, such a
+ **    more elaborate approach was deemed unnecessary for the time being.
  ** 
  ** @see GenericRecordRepresentation_test
  ** 
@@ -78,7 +96,7 @@
 #include 
 #include 
 #include 
-//#include 
+
 
 
 namespace lib {
@@ -91,7 +109,24 @@ namespace diff{
   
   
   
-  /** object-like record of data */
+  /**
+   * object-like record of data.
+   * For symbolic representation of "objects".
+   * A Record holds both \em attributes (key-value data)
+   * plus a list of \em enclosed children, which are conceived
+   * to be within the "scope" of this Record. Optionally, a \em typeID
+   * (metadata) may be defined. Otherwise, this typeID defaults to \c "NIL".
+   * The representation of attributes depends on the actual value type, which
+   * somehow need the ability to encode the keys within the value data.
+   * By default, a specialisation is given for string, using the \c "key = val"
+   * syntax. Yet the most relevant use case is \c Record -- using the
+   * embedded name-ID of the GenNode elements as key for attributes.
+   * 
+   * Recode elements meant to be immutable; they can be created from a
+   * defining collection. However, we provide a #Mutator mechanism to allow
+   * for rebuilding and mutating symbolic data structures based on Records
+   * and GenNode. Especially, Lumiera's diff framework relies on this.
+   */
   template
   class Record
     {
@@ -265,41 +300,13 @@ namespace diff{
         }     }
       
     private:
-      static bool
-      isAttribute (VAL const& v)
-        {
-          return false; ////TODO
-        }
-      
-      static bool
-      isTypeID (VAL const& v)
-        {
-          return false; ////TODO
-        }
-      
-      static string
-      extractTypeID (VAL const& v)
-        {
-          return "todo"; ////TODO
-        }
-      
-      static VAL
-      buildTypeAttribute (string const& typeID)
-        {
-          return VAL(); ///TODO
-        }
-      
-      static string
-      extractKey (VAL const& v)
-        {
-          return "todo"; ////TODO
-        }
-      
-      static VAL
-      extractVal (VAL const& v)
-        {
-          return VAL(); ///TODO
-        }
+      /* === abstract attribute handling : needs specialisation === */
+      static bool   isAttribute (VAL const& v);
+      static bool   isTypeID (VAL const& v);
+      static string extractTypeID (VAL const& v);
+      static VAL    buildTypeAttribute (string const& typeID);
+      static string extractKey (VAL const& v);
+      static VAL    extractVal (VAL const& v);
       
       
       friend bool
@@ -384,5 +391,51 @@ namespace diff{
   
   
   
+  /* === Specialisations to define the handling of attributes === */
+  
+  template<>
+  inline bool
+  Record::isAttribute (string const& v)
+  {
+    return false; ////TODO
+  }
+  
+  template<>
+  inline bool
+  Record::isTypeID (string const& v)
+  {
+    return false; ////TODO
+  }
+  
+  template<>
+  inline string
+  Record::extractTypeID (string const& v)
+  {
+    return "todo"; ////TODO
+  }
+  
+  template<>
+  inline string
+  Record::buildTypeAttribute (string const& typeID)
+  {
+    return string(); ///TODO
+  }
+  
+  template<>
+  inline string
+  Record::extractKey (string const& v)
+  {
+    return "todo"; ////TODO
+  }
+  
+  template<>
+  inline string
+  Record::extractVal (string const& v)
+  {
+    return string(); ///TODO
+  }
+  
+  
+  
 }} // namespace lib::diff
 #endif /*LIB_DIFF_GEN_NODE_H*/

From 8e274165949ff36ca0807cfea6b1c02d2529e689 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 8 Jun 2015 01:58:39 +0200
Subject: [PATCH 11/47] planning towards a tree diff language

before engaging into the implementation of lib::Record,
I prefer to conduct a round of planning, to get a clearer
view about the requirements we'll meet when extending
our existing list diff to tree structures
---
 src/lib/diff/gen-node.hpp | 36 +++++++++++++++++++-----------------
 wiki/renderengine.html    | 23 +++++++++++++++++------
 2 files changed, 36 insertions(+), 23 deletions(-)

diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp
index cfce16e56..a821db633 100644
--- a/src/lib/diff/gen-node.hpp
+++ b/src/lib/diff/gen-node.hpp
@@ -24,12 +24,12 @@
 /** @file gen-node.hpp
  ** Generic building block for tree shaped (meta)data structures.
  ** A representation built from GenNode elements is intended to support
- ** introspection of data structures and exchange of mutations in the
- ** form of \link diff-language.hpp diff messages. \endlink
+ ** (limited) introspection of data structures and exchange of mutations
+ ** in the form of \link diff-language.hpp diff messages. \endlink
  ** 
  ** Despite of the name, GenNode is \em not meant to be an universal
  ** data representation; rather it is limited to embody a fixed hard
- ** wired set of data elements, able to stand-in for attributes
+ ** wired set of data types, able to stand-in for attributes
  ** and sub scope contents of the lumiera high-level data model.
  ** 
  ** \par Anatomy of a GenNode
@@ -44,13 +44,13 @@
  ** will be referred by a suitable reference representation (PlacementID).
  ** The DataCap is what creates the polymorphic nature, where the common
  ** interface is mostly limited to managemental tasks (copying of values,
- ** external representation). Besides, there are special flavours of
- ** the DataCap to represent \em sub-collections of GenNode elements.
- ** Especially, the \ref Record type is a kind of collection suitable
- ** to represent object-like structures, since it both holds several
- ** \am attributes referable by-name, and a (ordered) collection
- ** of elements treated as children within the scope of the
- ** given record.
+ ** external representation).
+ ** 
+ ** To represent object-like structures and for building trees, a special
+ ** kind of data type is placed into the DataCap. This type, Record
+ ** is recursive and has the ability to hold both a a set of attributes
+ ** addressable by-name and an (ordered) collection of elements treated
+ ** as children within the scope of the given record.
  ** 
  ** \par Requirements
  ** 
@@ -122,6 +122,7 @@ namespace diff{
   
   class GenNode;
   
+  using Rec = Record;
   using DataValues = meta::Types
+                                ,Rec
                                 >;
   
   
@@ -161,42 +163,42 @@ namespace diff{
   
   template<>
   inline bool
-  Record::isAttribute (GenNode const& v)
+  Rec::isAttribute (GenNode const& v)
   {
     return false; ////TODO
   }
   
   template<>
   inline bool
-  Record::isTypeID (GenNode const& v)
+  Rec::isTypeID (GenNode const& v)
   {
     return false; ////TODO
   }
   
   template<>
   inline string
-  Record::extractTypeID (GenNode const& v)
+  Rec::extractTypeID (GenNode const& v)
   {
     return "todo"; ////TODO
   }
   
   template<>
   inline GenNode
-  Record::buildTypeAttribute (string const& typeID)
+  Rec::buildTypeAttribute (string const& typeID)
   {
     return GenNode(); ///TODO
   }
   
   template<>
   inline string
-  Record::extractKey (GenNode const& v)
+  Rec::extractKey (GenNode const& v)
   {
     return "todo"; ////TODO
   }
   
   template<>
   inline GenNode
-  Record::extractVal (GenNode const& v)
+  Rec::extractVal (GenNode const& v)
   {
     return GenNode(); ///TODO
   }
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index d34f89592..23fa71198 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -8018,7 +8018,7 @@ Within the context of GuiModelUpdate, we discern two distinct situations necessi
 the second case is what poses the real challenge in terms of writing well organised code. Since in that case, the receiver side has to translate generic diff verbs into operations on hard wired language level data structures -- structures, we can not control, predict or limit beforhand. We deal with this situation by introducing a specific intermediary, the &rarr; TreeMutator.
 
-
+
for the purpose of handling updates in the GUI timeline display efficiently, we need to determine and represent //structural differences//
 This leads to what could be considered the very opposite of data-centric programming. Instead of embody »the truth« into a central data model with predefined layout, we base our achitecture on a set of actors and their collaboration. In the mentioned example this would be the high-level view in the Session, the Builder, the UI-Bus and the presentation elements within the timeline view. Underlying to each such collaboration is a shared conception of data. There is no need to //actually represent that data// -- it can be conceived to exist in a more descriptive, declarative [[external tree description (ETD)|ExternalTreeDescription]]. In fact, what we //do represent// is a ''diff'' against such an external rendering.
 
@@ -8065,13 +8065,24 @@ Thus, for our specific usage scenario, the foremost relevant question is //how t
 __Implementation note__:The representation chosen here uses terms of constant size for the individual diff steps; in most cases, the argument is redundant and can be used for verification when applying the diff -- with the exception of the {{{ins}}} term, where it actually encodes additional information. Especially the {{{find}}}-representation is a compromise, since we encode as "search for the term a~~5~~ and insert it at curent position". The more obvious rendering -- "push term a~~4~~ back by +1 steps" -- requires an additional integer argument not neccesary for any of the other diff verbs, defeating a fixed size value implementation.
 
 !!!extension to tree changes
-Basically we could send messages for recursive descent right after each {{{pick}}} token -- yet, while minimal, such a representation would be unreadable, and requires a dedicated stack storage on both sides. Thus we arrange for the //recursive treatment of children// to be sent //postfix,// after the messages for the current node. Recursive descent is indicated by explicit (and slightly redundant) //bracketing tokens://
-*{{{open}}}(node-ID)  : recurse into the designated node, which must be present already as result of the preceding changes
-*{{{close}}}(node-ID)  : close the current node context and return one step up; the node-ID is given for verification, but can be used to restore the working position at parent level
-In addition, we might consider to introduce up/down folding primitives
-*{{{fold}}}(//num//, node-ID) : pick the next //num// elements and fold them down into a new child with given node-ID
+Diff description and diff handling can be applied to tree-like data structures as well. Some usages of textual comparison (e.g. diffing of programming language texts) are effectively working on tree structures -- yet they do not build on the structure of the diffed data explicitly. But if we represent the data structures symbolically, the change form text diffing to data structure diffing is marginal. The only relevant change is to handle embedded recursive diff descriptions of the child nodes. As it stands, each node or "object" can be represented as a list of properties plus the attachment of child nodes. This list can be treated with the methods developed for a stream of text tokes.
+
+Basically the transition from text diffing to changes on data structures is achieved by exchanging the //type of the tokens.// Instead of words, or lines of text, we now use //data elements.// To do so, we introduce a symbolic ExternalTreeDescription of tree-like core data structures. The elementary token element used in this tree diff, the GenNode, embodies either simple plain data elements (numbers, strings, booleans, id-hashes, time values) -- or it describes a //recursive data element,// given as {{{Record<GenNode>}}}. Such a recursive data element describes object-like entities as a sequence of metadata, named attributes and ordered child-nodes -- it is handled in two phases: the first step is to treat the presence and ordering of child data elements, insertions and deletes. The second phase opens for each structurally changed child data element a recursive bracketing construct, as indicated  by explicit (and slightly redundant) //bracketing tokens://
+*{{{mut}}}(node-ID)  : recurse into the designated node, which must be present already as result of the preceding changes. The following diff tokens describe //mutations// of the child
+*{{{emu}}}(node-ID)  : close the current node context and return one step up; the node-ID is given for verification, but can be used to restore the working position at parent level
+In addition, in a future extension, we might consider to introduce up/down folding primitives
+*{{{fold}}}(node-ID) : pick the following elements and fold them down into a new child with given node-ID. The downfolding continues until the next {{{emu}}} token
 *{{{lift}}}(node-ID) : remove the next child node, which must be node-ID, and insert its children at current position
 
+Since the bracketing construct for mutation of child structures bears the ID of the parent, a certain degree of leeway is introduced. In theory, we could always open such a bracketing construct right after the {{{pick}}} token accepting the parent -- yet, while minimal, such a strictly depth-first representation would be hard to read -- so we allow to group the recursive treatement of children //post-fix,// after the messages for the current node. In a similar vein, we introduce another token to describe a //short-cut://
+*{{{after}}}(node-ID) : fast-forward through the sequence of elements at current level until the position after the designated element.
+To complement this language construct, we define some special, magical (meta) element-~IDs
+*{{{_CHILD_}}} : marks an //unnamed// ID. Mostly, the implementation exploits this specific marker to distinguish between nodes which are (named) attributes of an object, and real children
+*{{{_THIS_}}} : can be used to refer to the immediately preceding element without knowing its name. Typically used to open a {{{mut(_THIS_)}}} ... {{{emu(_THIS_)}}} bracket to populate a newly inserted object
+*{{{_ATTRIBS_}}} : can be used to jump {{{after(_ATTRIBS_)}}} when mutating the contents of an object. So the following diff verbs will immediately start working on the children
+*{{{_END_}}} : likewise can be used to jump {{{after(_END_)}}} to start appending new elements without caring for the existing current content.
+All these additional language constructs aren't strictly necessary, but widen the usability of the langauge, also to cover the description of incomplete or fuzzy diffs.
+
 !!!deriving conventional representations
 On receiving the terms of this "diff language", it is possible to generate the well known and more conventional diff representations,
 i.e. a ''unified diff'' or the ''predicate notation'' used above to describe the list diffing algorithm, just by accumulating changes.

From bee4d11b3485b9d024306cf417fe2ea2f910cbf6 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 8 Jun 2015 02:51:53 +0200
Subject: [PATCH 12/47] WIP: draft some basic properties of a GenNode

- can build from the supported value types
- is optionally named
- is copyable value, but only assignable within one payload type
- is recursive, for object / tree representation
---
 tests/library/diff/gen-node-basic-test.cpp | 42 ++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/tests/library/diff/gen-node-basic-test.cpp b/tests/library/diff/gen-node-basic-test.cpp
index 4bd1883eb..81d00dbd1 100644
--- a/tests/library/diff/gen-node-basic-test.cpp
+++ b/tests/library/diff/gen-node-basic-test.cpp
@@ -25,14 +25,18 @@
 #include "lib/test/test-helper.hpp"
 #include "lib/diff/gen-node.hpp"
 #include "lib/diff/record.hpp"
+#include "lib/util.hpp"
 
 //#include 
 //#include 
 //#include 
+#include 
 
+using util::contains;
 //using std::string;
 //using std::vector;
 //using std::swap;
+using std::fabs;
 
 
 namespace lib {
@@ -40,6 +44,7 @@ namespace diff{
 namespace test{
   
 //  using lumiera::error::LUMIERA_ERROR_LOGIC;
+  using error::LUMIERA_ERROR_WRONG_TYPE;
   
   namespace {//Test fixture....
     
@@ -79,6 +84,43 @@ namespace test{
       void
       simpleUsage()
         {
+          // can build from the supported value types
+          GenNode n1(42);
+          CHECK (42 == n1.data.get());
+          CHECK (!n1.isNamed());
+          CHECK (contains (n1.idi.getSym(), "_CHILD_"));
+          
+          // can optionally be named
+          GenNode n2("π", 3.14159265358979323846264338328);
+          CHECK (fabs (3.14159265 - n2.data.get) < 1e-5 );
+          CHECK (n2.isNamed());
+          CHECK ("π" == n2.idi.getSym());
+          
+          // is a copyable value
+          GenNode n11(n1);
+          CHECK (n1 == n11);
+          CHECK (42 == n11.data.get());
+          
+          // is assignable with compatible payload value
+          n11.data = 24;
+          CHECK (n1 != n11);
+          CHECK (24 == n11.data.get());
+          CHECK (42 == n1.data.get());
+          
+          // is assignable within the same kind of value
+          n1 = n11;
+          CHECK (n1 == n11);
+          
+          // but assignment may not alter payload type
+          VERIFY_ERROR (WRONG_TYPE, n1 = n2 );
+          
+          // can build recursive data structures
+          GenNode n3(Rec({"spam", GenNode("ham", "eggs")}));
+          CHECK ("spam" == n3.data.get().getType());
+          CHECK ("eggs" == n3.data.get().get("ham").data.get());
+          CHECK ("ham"  == n3.data.get().get("ham").idi.getSym());
+          CHECK (n3.data.get().get("ham").isNamed());
+          CHECK (!n3.isNamed());
         }
       
       

From c70ad01ad3a842d410a87f43da3568324e4710a9 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 12 Jun 2015 23:23:23 +0200
Subject: [PATCH 13/47] resolve some conflicts in the design of the "External
 Tree Representation"

for the purpose of working out the inner logic, I frequently use the
help of a mindmap -- so why not commiting this alongside? For sure,
it is preliminary and the worked out concepts will be cast in code
and documented on the website. Yet the thought-process leading to
these decisions might be of some interest, at least for myself.
---
 wiki/renderengine.html   |  16 +--
 wiki/thinkPad.ichthyo.mm | 239 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 247 insertions(+), 8 deletions(-)
 create mode 100644 wiki/thinkPad.ichthyo.mm

diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 23fa71198..a99b9c12f 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1970,7 +1970,7 @@ It would thus be desirable to have a fixed-sized allocation, able to hold the pl
 !!!Storage
 Explicit placements are value objects and stored at the respective usage site, most notably the [[Segmentation]]. They are //not// attached to the placement index in the session, nor do they bear any referential or indexing semantics. The only dynamic side effect of an explicit placement is to keep the reference count of the corresponding MObject up and thus keep it alive and  accessible.
-
+
//to symbolically represent hierarchically structured elements, without actually implementing them.//
 The purpose of this »external« description is to remove the need of a central data model to work against. We consider such a foundation data model as a good starting point, yet harmful for the evolution of any larger structure to be built. According to the subsidiarity principle, we prefer to turn the working data representation into a local concern. Which leaves us with the issue of collaboration. Any collaboration requires, as an underlying, some kind of common understanding. And any formalised, mechanical collaboration requires to represent that common point of attachment -- at least as symbolic representation. The ExternalTreeDescription is shaped to fulfil this need: //in theory,// the whole field could be represented, symbolically, as a network of hierarchically structured elements. Yet, //in practice,// all we need is to conceive the presence of such a representation, as a backdrop to work against. And we do so -- we work against that symbolic representation, by describing ''changes'' made to the structure and its elements. Thus, the description of changes, the ''diff language'', refers to and partially embodies such symbolically represented elements and relations.
 &rarr; TreeDiffFundamentals
@@ -1985,9 +1985,9 @@ For practical purposes, we have to introduce some distinctions and limitations.
 * to the contrary, a Record has an associated, symbolic type-ID, plus it can potentially be associated with and thus relate to further elements, with the relation originating at the Record.
 * and we distinguish two different kinds of relations possibly originating from a Record:
 ** ''attributes'' are known by-name; they can be addressed through this name-ID as a key, while the value is again a generic node, possibly even another record.
-** ''children'' to the contrary can only be ennumerated; they are considered to be within (and form) the ''scope'' of the given Record (object).
+** ''children'' to the contrary can only be enumerated; they are considered to be within (and form) the ''scope'' of the given Record (object).
 
-And there is a further limitation: Tthe domain of possible data is fixed, even hard wired. Implementation-wise, this turns the data within the generic node into a »Variant« (typesafe union). Basically, this opens two different ways to //access// the data within a given GenNode: either you know the type to expect beforehand (and the validity of this assumption is checked on each access; please recall, all of this is meant for symbolic representation, not for implementation of high performance computing). Or we offer the ability for //generic access// through a ''variant visitor'' (double dispatch). The latter includes the option not to handle all possible content types (making the variant visitor a //partial function// -- as in any non exhaustive pattern match).
+And there is a further limitation: The domain of possible data is fixed, even hard wired. Implementation-wise, this turns the data within the generic node into a »Variant« (typesafe union). Basically, this opens two different ways to //access// the data within a given GenNode: either you know the type to expect beforehand (and the validity of this assumption is checked on each access; please recall, all of this is meant for symbolic representation, not for implementation of high performance computing). Or we offer the ability for //generic access// through a ''variant visitor'' (double dispatch). The latter includes the option not to handle all possible content types (making the variant visitor a //partial function// -- as in any non exhaustive pattern match).
 
 Basically, you can expect to encounter the following kinds of data
 *{{{int}}}, {{{int64_t}}}, {{{short}}}, {{{char}}}
@@ -2010,16 +2010,16 @@ Operationally, a modad can be constructed to wrap-up and embody a given element.
 Now, the interesting question is: //what does "join" mean?// --
 *will it drill down?
 *will it lift the contents of generated monads into the parent level, or attach them as new subtrees?
-*will the function get to see {{{Record}}} elements, or will it imediately see the "contents", the attributes or the children?
+*will the function get to see {{{Record}}} elements, or will it immediately see the "contents", the attributes or the children?
 
 !!!names, identity and typing
-It was a design decision that the GenNode shall not embodiy a readable type field, just a type selector within the variant to hold the actual data elements.
-This decision more or less limits the usefulness of simple values as children to those cases, where all children are of uniform type, or where we agree to deal with all children through variant visitation solely. Of course, we can still use simple values as attributes, since those are known and addressed by name. And we can use filtereing by type to limit access to some children of type record, since every record does indeed embody a symbolic type name. It must be that way, since otherwise, records would be pretty much useless as representation for any object like entity.
+It was a design decision that the GenNode shall not embody a readable type field, just a type selector within the variant to hold the actual data elements.
+This decision more or less limits the usefulness of simple values as children to those cases, where all children are of uniform type, or where we agree to deal with all children through variant visitation solely. Of course, we can still use simple values as //attributes,// since those are known and addressed by name. And we can use filtering by type to limit access to some children of type {{{Record}}}, since every record does indeed embody a symbolic type name, an attribute named {{{"type"}}}. It must be that way, since otherwise, records would be pretty much useless as representation for any object like entity.
 
 The discriminating ID of any GenNode can serve as a name, and indeed will be used as the name of an attribute within a record.
-Now, the interesting question is: what constitues the full identity? Is it the ~ID-string? does it also include some kind of type information, so that two children with the same name, but different types would be considered different? And, moreover, we could even go as far as to make the path part of the identity, so that two otherwise identical elements would be different, when located at different positions within the graph. But since we did not rule out cyclic graphs, the latter idea would necessitate the notion of an //optimal path// --
+Now, the interesting question is: what constitutes the full identity? Is it the ~ID-string? does it also include some kind of type information, so that two children with the same name, but different types would be considered different? And, moreover, we could even go as far as to make the path part of the identity, so that two otherwise identical elements would be different, when located at different positions within the graph. But since we did not rule out cyclic graphs, the latter idea would necessitate the notion of an //optimal path// --
 
-A somewhat similar question is the ordering and uniqueness of children. While attributes -- due to the usage of the attribute node's ID as name-key -- are bound to be unique within a given Record, children within the scope of a record could be required to be unique too, making the scope a set. And, of course, children could be focribly ordered, or just retain the initial ordering, or even be completely unordered. On a second thought, it seems wise not to impose any guarantees in that regard, beyond the simple notion of retaining an initial sequence order, the way a »stable« sorting algorithm does. All these more specific ordering properties can be considered the concern of some specific kinds of objects -- which then just happen to "supply" a list of children for symbolic representation as they see fit.
+A somewhat similar question is the ordering and uniqueness of children. While attributes -- due to the usage of the attribute node's ID as name-key -- are bound to be unique within a given Record, children within the scope of a record could be required to be unique too, making the scope a set. And, of course, children could be forcibly ordered, or just retain the initial ordering, or even be completely unordered. On a second thought, it seems wise not to impose any guarantees in that regard, beyond the simple notion of retaining an initial sequence order, the way a »stable« sorting algorithm does. All these more specific ordering properties can be considered the concern of some specific kinds of objects -- which then just happen to "supply" a list of children for symbolic representation as they see fit.
 
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm new file mode 100644 index 000000000..7073b3232 --- /dev/null +++ b/wiki/thinkPad.ichthyo.mm @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ scheidet aus, wegen Wertsemantik +

+ + +
+ +
+ + + + + + + +

+ mit speziellem Ref-Typ +

+

+ -- im DataCap +

+ + +
+
+ + + + + + + + + +
+
+
+ + + + + + + + + + + + + + +

+ heißt: in der Diff-Verarbeitung wird dieser spezielle check verwendet +

+ +
+ + + + + + + + + + + + + + + +

+ heißt: wird direkt von standard-equality so behandelt +

+ +
+ + + + + + +

+ brauche speziellen Builder, +

+

+ der das so fabriziert +

+ +
+
+ + + + + + +

+ bekomme einen +

+

+ "ungenutzten" DataCap +

+ +
+ + + + + + + + + +

+ Idee: Ref-GenNode +

+ +
+ + + + + + + +

+ als Ref erkennbar +

+

+ (Prädikat) +

+ +
+
+ + + + + + +

+ hash-identische +

+

+ Ziel-ID ableitbar +

+ +
+
+ + + + + + +
+
+ + + + + + + + + +

+ Verarbeiten +

+

+ von Teilbäumen +

+ +
+ +
+
+
+
+
+
+
+
+
+
From b81419ad639a74ec9a9ea54f0521e73f1d1def33 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 14 Jun 2015 02:52:11 +0200 Subject: [PATCH 14/47] WIP: decide to implement the record ref as simple referenc wrapper --- src/lib/diff/record.hpp | 43 +++++++++++++++++++ .../generic-record-representation-test.cpp | 33 ++++++++++++++ wiki/thinkPad.ichthyo.mm | 39 +++++++++++------ 3 files changed, 103 insertions(+), 12 deletions(-) diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp index 3b6f2bb9c..0868fda5c 100644 --- a/src/lib/diff/record.hpp +++ b/src/lib/diff/record.hpp @@ -391,6 +391,49 @@ namespace diff{ + /** + * wrapped record reference. + * A helper for lib::GenNode and the diff representation. + * RecordRef is copyable and assignable, but like a reference + * can not be rebound. It can be used to refer to a subtree + * within the diff representation, without the need to copy. + */ + template + class RecordRef + { + using Target = Record; + + Target* record_; + + public: + RecordRef(Target& o) noexcept + : record_(&o) + { } + + RecordRef(Target&&) = delete; + + // standard copy operations acceptable + + + operator Target&() const + { + if (!record_) + throw error::Logic("attempt to dereference an unbound record reference" + ,error::LUMIERA_ERROR_BOTTOM_VALUE); + return *record_; + } + + Target* + get() const noexcept + { + return record_; + } + }; + + + + + /* === Specialisations to define the handling of attributes === */ template<> diff --git a/tests/library/diff/generic-record-representation-test.cpp b/tests/library/diff/generic-record-representation-test.cpp index 13f0da01f..c20488515 100644 --- a/tests/library/diff/generic-record-representation-test.cpp +++ b/tests/library/diff/generic-record-representation-test.cpp @@ -47,6 +47,7 @@ namespace test{ // using lumiera::error::LUMIERA_ERROR_LOGIC; using lumiera::error::LUMIERA_ERROR_INVALID; + using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE; namespace {//Test fixture.... @@ -105,6 +106,7 @@ namespace test{ verifyMutations(); copy_and_move(); equality(); + wrapRef(); } @@ -294,6 +296,37 @@ namespace test{ CHECK (Seq({"type=u", "a=α", "a=β", "⟂", "a"}) == contents(a)); CHECK (Seq({"type=u", "a=1", "a"}) == contents(aa)); } + + + void + wrapRef() + { + RecS oo({"type = 🌰", "☿ = mercury", "♀ = venus", "♁ = earth", "♂ = mars", "♃ = jupiter", "♄ = saturn"}); + + RecordRef empty; + CHECK (bool(empty) == false); + CHECK (nullptr == empty.get()); + VERIFY_ERROR (BOTTOM_VALUE, RecS& (empty)); + + RecordRef ref(oo); + CHECK (ref); + CHECK (ref.get() == &oo); + + RecS& oor = ref; + CHECK ("🌰" == oor.getType()); + CHECK (oor.get("♄") == "saturn"); + + // are copyable and assignable + RecordRef r2 = ref; + CHECK (r2); + CHECK (r2.get() == ref.get()); + CHECK (!isSameObject (r2, ref)); + + empty = std::move(r2); + CHECK (empty); + CHECK (!r2); + CHECK (nullptr == r2.get()); + } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 7073b3232..671867b0f 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -42,10 +42,10 @@ - - + + - + @@ -59,7 +59,8 @@ - + + @@ -73,8 +74,7 @@ -- im DataCap

- -
+
@@ -111,9 +111,23 @@ - - - + + + + + + + + + + +

+ m.E. die einzig saubere Desgin-Variante! +

+ + +
+
@@ -156,10 +170,10 @@

- - + + - + @@ -170,6 +184,7 @@

+ From 1810d0069048d08584f1712118b0af008a7036d3 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 14 Jun 2015 02:58:43 +0200 Subject: [PATCH 15/47] WIP: but with a notable difference to std::ref ..it can be default created, which represents the "bottom", invalid state --- src/lib/diff/record.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp index 0868fda5c..3f4d6f306 100644 --- a/src/lib/diff/record.hpp +++ b/src/lib/diff/record.hpp @@ -397,6 +397,10 @@ namespace diff{ * RecordRef is copyable and assignable, but like a reference * can not be rebound. It can be used to refer to a subtree * within the diff representation, without the need to copy. + * @remarks this is almost identical to std::ref, with the + * notable difference that it can be default-created + * into "bottom" state; this also implies to have + * a NULL check on dereferentiation. */ template class RecordRef @@ -406,15 +410,27 @@ namespace diff{ Target* record_; public: + /** by default create an + * invalid ("bottom") reference */ + RecordRef() noexcept + : record_(nullptr) + { } + + /** create a reference bound to + * the given target; can not be rebound */ RecordRef(Target& o) noexcept : record_(&o) { } + /** prevent moving into black hole */ RecordRef(Target&&) = delete; // standard copy operations acceptable + /** target is accessed by cast + * @throws error::Logic on bottom reference + */ operator Target&() const { if (!record_) From f79f4cd82fea436ab1808e789279a44be3d9955f Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 14 Jun 2015 22:34:22 +0200 Subject: [PATCH 16/47] WIP: fix -- revert back I had added this variation just to check compilation and forgot to revert ist. Of course, we do *not* want to move the inwards of our Mutator in the test. Rather, we want to draw a copy from the mutated state --- tests/library/diff/generic-record-representation-test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/library/diff/generic-record-representation-test.cpp b/tests/library/diff/generic-record-representation-test.cpp index c20488515..3ae9245a5 100644 --- a/tests/library/diff/generic-record-representation-test.cpp +++ b/tests/library/diff/generic-record-representation-test.cpp @@ -275,7 +275,7 @@ namespace test{ mut.appendChild("a"); mut.set("a", "1"); - RecS aa(std::move(mut)); + RecS aa(mut); CHECK (a != aa); CHECK ("u" == aa.getType()); CHECK (Seq({"type=u", "a=1", "a"}) == contents(aa)); From 248fbef9b4ea6f1a41cd8bd920f8983d98510d99 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 16 Jun 2015 04:27:37 +0200 Subject: [PATCH 17/47] WIP: draft a DSL for simplified definition of literal records This is just a draft for now -- kindof a by-catch, since it is chep to build that DSL on top of the Rec::Mutator. This DSL could be of value later, when it comes to define some configuration data inline, in a copact and clear fashion, without the need to use a bridge to/from JSON --- tests/library/diff/gen-node-basic-test.cpp | 84 +++++++++++++++++++++- wiki/thinkPad.ichthyo.mm | 21 +++++- 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/tests/library/diff/gen-node-basic-test.cpp b/tests/library/diff/gen-node-basic-test.cpp index 81d00dbd1..18b511802 100644 --- a/tests/library/diff/gen-node-basic-test.cpp +++ b/tests/library/diff/gen-node-basic-test.cpp @@ -63,6 +63,18 @@ namespace test{ /*****************************************************************************//** * @test Verify properties of a special collection type shaped for * external representation of object-like data. + * - GenNode elements can be created "right away", picking up + * the given type, assumed that the payload is one of the + * supported basic types. + * - optionally, GenNode elements can be named + * - unnamed elements get a marker ID plus unique number extension + * - object-like elements can be represented by using a diff:Record + * as payload. Obviously, the resulting data structure type is recursive. + * - a shortcut is provided to simplify defining empty baseline objects + * - there is a special notation to create "id references", which can + * can be used to stand-in for an "object" (Record). This shortcut + * notation is relevant for the tree diff language -- used within + * Lumiera as "External Tree Description" of object networks. * * @see IndexTable * @see DiffListApplication_test @@ -74,7 +86,8 @@ namespace test{ run (Arg) { simpleUsage(); - verifySnapshot(); + objectShortcut(); + symbolReference(); sequenceIteration(); duplicateDetection(); copy_and_move(); @@ -115,7 +128,7 @@ namespace test{ VERIFY_ERROR (WRONG_TYPE, n1 = n2 ); // can build recursive data structures - GenNode n3(Rec({"spam", GenNode("ham", "eggs")})); + GenNode n3(Rec({GenNode("type", "spam"), GenNode("ham", "eggs")})); CHECK ("spam" == n3.data.get().getType()); CHECK ("eggs" == n3.data.get().get("ham").data.get()); CHECK ("ham" == n3.data.get().get("ham").idi.getSym()); @@ -125,7 +138,72 @@ namespace test{ void - verifySnapshot() + objectShortcut() + { + auto o0 = Rec().genNode(); + auto o1 = Rec().genNode("νόμος"); + auto o2 = Rec().type("spam").genNode(); + auto o3 = Rec().attrib("Ψ", int64_t(42), "π", 3.14159265358979323846264338328).genNode("νόμος"); + + CHECK (!o0.isNamed()); + CHECK (isnil(o0.data.get())); + CHECK ("NIL" == o0.data.get().getType()); + + CHECK (o1.isNamed()); + CHECK ("νόμος" == o1.idi.getSym()); + CHECK (isnil(o1.data.get())); + + CHECK (!o2.isNamed()); + CHECK ("spam" == o0.data.get().getType()); + CHECK (isnil(o2.data.get())); + + CHECK (o3.isNamed()); + CHECK ("νόμος" == o3.idi.getSym()); + CHECK ("NIL" == o3.data.get().getType()); + CHECK (GenNode("Ψ", int64_t(42)) == o3.data.get().get("Ψ")); + CHECK (42L == o3.data.get().get("Ψ").data.get); + CHECK (1e-7 > fabs (3.14159265 - o3.data.get().get("π").data.get)); + + LuidH luid; + auto o4 = Rec::Mutator(o2) // ...use GenNode o2 as starting point + .appendChild(GenNode("τ", Time(1,2,3,4))) // a named node with Time value + .scope('*' // a char node + ,"★" // a string node + ,luid // a hash value (LUID) + ,TimeSpan(Time::ZERO, FSecs(23,25)) // a time span + ,Rec().type("ham").scope("eggs").genNode()) // a spam object + .genNode("baked beans"); // ---> finish into named node + + CHECK (o4.isNamed()); + CHECK ("baked beans" == o4.idi.getSym()); + CHECK ("spam" == o4.data.get().getType()); // this was "inherited" from o2 + + auto scope = o4.data.get().scope(); + CHECK (!isnil(scope)); + CHECK (GenNode("τ", Time(1,2,3,4)) == *scope); + ++scope; + CHECK (GenNode(char('*')) == *scope); + ++scope; + CHECK ("★" == scope->data.get()); + ++scope; + CHECK (luid == scope->data.get()); + ++scope; + CHECK (Time(0.92,0) == scope->data.get().end()); + ++scope; + auto spam = *scope; + CHECK (!++scope); + CHECK ("ham" == spam.getType()); + CHECK (spam.contains (GenNode("eggs"))); + + // but while o4 was based on o2, + // adding all the additional contents didn't mutate o2 + CHECK (isnil(o2.data.get())); + } + + + + void + symbolReference() { } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 671867b0f..c31af429b 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -34,7 +34,26 @@ - + + + + + + + + + +

+ Zweck: kompaktes Anschreiben +

+

+ von literalen Daten +

+ + +
+
+
From de08a4d3c6770a5debc4bc37853a65bd73ac1c29 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 20 Jun 2015 04:20:48 +0200 Subject: [PATCH 18/47] WIP: draft GenNode symbolic object references these speical reference-flavours of a GenNode are built to stand-in for a full fledged "object" GenNode. The purpose is to be able to handle sub-trees of objects efficiently in comparisions and processing. --- tests/library/diff/gen-node-basic-test.cpp | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/library/diff/gen-node-basic-test.cpp b/tests/library/diff/gen-node-basic-test.cpp index 18b511802..58ed90248 100644 --- a/tests/library/diff/gen-node-basic-test.cpp +++ b/tests/library/diff/gen-node-basic-test.cpp @@ -25,6 +25,7 @@ #include "lib/test/test-helper.hpp" #include "lib/diff/gen-node.hpp" #include "lib/diff/record.hpp" +#include "lib/time/timevalue.hpp" #include "lib/util.hpp" //#include @@ -33,6 +34,8 @@ #include using util::contains; +using lib::time::Time; +using lib::time::TimeSpan; //using std::string; //using std::vector; //using std::swap; @@ -45,6 +48,7 @@ namespace test{ // using lumiera::error::LUMIERA_ERROR_LOGIC; using error::LUMIERA_ERROR_WRONG_TYPE; + using error::LUMIERA_ERROR_BOTTOM_VALUE; namespace {//Test fixture.... @@ -102,12 +106,14 @@ namespace test{ CHECK (42 == n1.data.get()); CHECK (!n1.isNamed()); CHECK (contains (n1.idi.getSym(), "_CHILD_")); + CHECK (contains (name(n1), "_CHILD_")); // can optionally be named GenNode n2("π", 3.14159265358979323846264338328); CHECK (fabs (3.14159265 - n2.data.get) < 1e-5 ); CHECK (n2.isNamed()); CHECK ("π" == n2.idi.getSym()); + CHECK ("π" == name(n2)); // is a copyable value GenNode n11(n1); @@ -165,6 +171,7 @@ namespace test{ CHECK (1e-7 > fabs (3.14159265 - o3.data.get().get("π").data.get)); LuidH luid; + //Demonstration: object builder is based on the mutator mechanism for Records... auto o4 = Rec::Mutator(o2) // ...use GenNode o2 as starting point .appendChild(GenNode("τ", Time(1,2,3,4))) // a named node with Time value .scope('*' // a char node @@ -205,6 +212,43 @@ namespace test{ void symbolReference() { + GenNode ham = Rec().type("spam").attrib("τ", Time(23,42)).genNode("egg bacon sausage and spam"); + + GenNode::ID hamID(ham); + CHECK (hamID == ham.idi); + CHECK (hamID.getSym() == ham.idi.getSym()); + CHECK (hamID.getHash() == ham.idi.getHash()); + CHECK (contains (string(hamID), "spam")); // Lovely spam! + + GenNode ref1 = Ref("egg bacon sausage and spam"); + GenNode ref2 = Ref(ham); + + CHECK (ref1.idi == ham.idi); + CHECK (ref2.idi == ham.idi); + + // can stand-in for the original Record... + CHECK (isSameObject (ham, ref2.data.get())); + VERIFY_ERROR (BOTTOM_VALUE, ref1.data.get()); + + RecordRef rr1 = ref1.data.get(); + RecordRef rr2 = ref2.data.get(); + + CHECK ( isnil(rr1)); + CHECK (!isnil(rr2)); + Rec& ham_ref = rr2; + CHECK (isSameObject(ham, ham_ref)); + CHECK (isSameObject(&ham, rr2.get())); + + // pre-defined special ref-tokens + CHECK ("_END_" == name(Ref::END)); + CHECK ("_THIS_" == name(Ref::THIS)); + CHECK ("_CHILD_" == name(Ref::CHILD)); + CHECK ("_ATTRIBS_" == name(Ref::ATTRIBS)); + + CHECK (isnil (Ref::END.data.get())); + CHECK (isnil (Ref::THIS.data.get())); + CHECK (isnil (Ref::CHILD.data.get())); + CHECK (isnil (Ref::ATTRIBS.data.get())); } From 7ea4f739bdec6377603bda79c00898a770683a9a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 27 Jun 2015 19:33:49 +0200 Subject: [PATCH 19/47] introduce a new header for #984 --- src/lib/idi/genfunc.hpp | 71 +++++++++++++++++++++++++++++++++++++ src/proc/asset/entry-id.hpp | 7 ++++ wiki/thinkPad.ichthyo.mm | 17 +++++---- 3 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 src/lib/idi/genfunc.hpp diff --git a/src/lib/idi/genfunc.hpp b/src/lib/idi/genfunc.hpp new file mode 100644 index 000000000..d88c4b306 --- /dev/null +++ b/src/lib/idi/genfunc.hpp @@ -0,0 +1,71 @@ +/* + GENFUNC.hpp - generic identification functions + + Copyright (C) Lumiera.org + 2015, Hermann Vosseler + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file genfunc.hpp + ** Generic function to build identification schemes. + ** These template functions are meant as common extension point. + ** The purpose is to streamline and disentangle the various identification schemes + ** in use at various places within Lumiera. We strive to cover all the common basic + ** usage situations through these functions + ** - build a symbolic ID + ** - build a classification record + ** - create a readable yet unique ID + ** - render an ID in human readable form + ** - derive a hash function + ** + ** @see EntryID + ** + */ + + + +#ifndef LIB_IDI_GENFUNC_H +#define LIB_IDI_GENFUNC_H + +#include "lib/hash-value.h" +//#include "lib/hash-standard.hpp" + +#include + + +namespace lib { +namespace idi { + + namespace { // integration helpers... + } //(End)integration helpers... + + + + /********************************************************//** + * A Mixin to add a private ID type to the target class, + * together with storage to hold an instance of this ID, + * getter and setter, and a templated version of the ID type + * which can be used to pass specific subclass type info. + */ + + + + + +}} // namespace lib::idi +#endif /*LIB_IDI_GENFUNC_H*/ diff --git a/src/proc/asset/entry-id.hpp b/src/proc/asset/entry-id.hpp index cef31a098..a352a8d4e 100644 --- a/src/proc/asset/entry-id.hpp +++ b/src/proc/asset/entry-id.hpp @@ -62,6 +62,13 @@ namespace asset { + /** + * Identification Schemes. + * Collection of commonly used mechanisms to build identification records, + * unique identifiers, registration numbers and hashes. These are used as glue + * and thin abstraction to link various subsystems or to allow interoperation + * of registration facilities + */ namespace idi { using lib::hash::LuidH; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index c31af429b..d3566b380 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -31,6 +31,14 @@ + + + + + + + + @@ -50,8 +58,7 @@ von literalen Daten

- -
+
@@ -74,8 +81,7 @@ scheidet aus, wegen Wertsemantik

- - +
@@ -144,8 +150,7 @@ m.E. die einzig saubere Desgin-Variante!

- - +
From fc488f3b562125d5318f44886821f8dbdbb1a9da Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 29 Jun 2015 02:31:22 +0200 Subject: [PATCH 20/47] extract a basic set of generic ID functions for #984 using the struct-scheme.hpp and the requirements for EntryID as a guideline. The goal is to move EntryID over into the support lib, which means we need to get rid of all direct proc::asset dependencies. Thus, these generic ID functions shall form a baseline implementation, while asset::Struct may provide the previously used implementation through specialisation -- so the behaviour of EntryID will not change for the structural assets, but we'll get a more sane and readable default implementation for all other types. --- src/lib/idi/genfunc.cpp | 76 +++++++++++++++++++++++++++++++++++++ src/lib/idi/genfunc.hpp | 76 ++++++++++++++++++++++++++++++++++--- src/lib/symbol-impl.cpp | 3 +- src/lib/typed-counter.hpp | 2 +- src/proc/asset/entry-id.hpp | 1 + 5 files changed, 151 insertions(+), 7 deletions(-) create mode 100644 src/lib/idi/genfunc.cpp diff --git a/src/lib/idi/genfunc.cpp b/src/lib/idi/genfunc.cpp new file mode 100644 index 000000000..76ca64e7b --- /dev/null +++ b/src/lib/idi/genfunc.cpp @@ -0,0 +1,76 @@ +/* + GenFunc - generic identification functions (raw) + + Copyright (C) Lumiera.org + 2015, Hermann Vosseler + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/idi/genfunc.hpp" +#include "lib/format-string.hpp" +#include "lib/util.hpp" + +#include +#include + + +using util::_Fmt; +using std::string; + + +namespace lib { +namespace test{ // see test-helper.cpp + std::string demangleCxx (lib::Literal rawName); +} +namespace idi { + + + namespace { // generic entry points / integration helpers... + + using lib::test::demangleCxx; + + string + demangled_innermost_component (const char* rawName) + { + string typeStr = demangleCxx (rawName); + size_t pos = typeStr.rfind("::"); + if (pos != string::npos) + typeStr = typeStr.substr(pos+2); + return typeStr; + } + + string + demangled_sanitised_name (const char* rawName) + { + return util::sanitise (test::demangleCxx (rawName)); + } + + + string + instance_formatter (string const& prefix, long instanceNr) + { + return _Fmt("%s.%03d") + % prefix % instanceNr; + } + + } //(End)integration helpers... + + + + +}} // namespace lib::test diff --git a/src/lib/idi/genfunc.hpp b/src/lib/idi/genfunc.hpp index d88c4b306..53f760674 100644 --- a/src/lib/idi/genfunc.hpp +++ b/src/lib/idi/genfunc.hpp @@ -43,27 +43,93 @@ #define LIB_IDI_GENFUNC_H #include "lib/hash-value.h" +#include "lib/symbol.hpp" +#include "lib/typed-counter.hpp" //#include "lib/hash-standard.hpp" +#include #include namespace lib { namespace idi { + using lib::HashVal; + using std::string; + namespace { // integration helpers... + string demangled_innermost_component (const char* rawName); + string demangled_sanitised_name (const char* rawName); + + string instance_formatter (string const& prefix, long instanceNr); + } //(End)integration helpers... - /********************************************************//** - * A Mixin to add a private ID type to the target class, - * together with storage to hold an instance of this ID, - * getter and setter, and a templated version of the ID type - * which can be used to pass specific subclass type info. + /** Short readable type identifier, not necessarily unique or complete. + * @return the innermost component of the demangled C++ type name. + * Usually, this is the bare name without any namespaces. */ + template + inline string + typeSymbol() + { + return demangled_innermost_component (typeid(TY).name()); + } + + /** Complete unique type identifier + * @return complete demangled C++ type name, additionally + * passed through our ID sanitiser function, i.e. + * one word, no whitespace, only minimal punctuation + */ + template + inline string + typeFullID() + { + return demangled_sanitised_name (typeid(TY).name()); + } + + template + inline string + categoryFolder() + { + return typeSymbol(); + } + + template + inline string + namePrefix() + { + return typeSymbol(); + } + /** build a per-type unique identifier. + * @return a type based prefix, followed by an instance number + * @note we use the short prefix without namespace, not necessarily unique + * @warning this operation is not exactly cheap; it acquires a lock + * for the counter and, after increasing and dropping the lock, + * it builds and uses a boost::format instance. + */ + template + inline string + generateSymbolicID() + { + static TypedCounter instanceCounter; + return instance_formatter (namePrefix(), instanceCounter.inc()); + } + + /** + * @return a boost hash value, based on the full (mangled) C++ type name + */ + template + inline HashVal + getTypeHash() + { + Literal rawTypeName (typeid(TY).name()); + return hash_value (rawTypeName); + } diff --git a/src/lib/symbol-impl.cpp b/src/lib/symbol-impl.cpp index b9aade749..3762179c4 100644 --- a/src/lib/symbol-impl.cpp +++ b/src/lib/symbol-impl.cpp @@ -68,7 +68,8 @@ namespace lib { and should be usable both with \c std::tr1 and \c . It is implemented similar as the boost::hash specialisation for std::string */ - size_t hash_value (Literal sym) + size_t + hash_value (Literal sym) { size_t hash=0; if (sym) diff --git a/src/lib/typed-counter.hpp b/src/lib/typed-counter.hpp index ce62ab6d2..88e14fc07 100644 --- a/src/lib/typed-counter.hpp +++ b/src/lib/typed-counter.hpp @@ -30,7 +30,7 @@ ** setup allows to bridge between metaprogramming and (runtime) dispatcher tables. ** ** Each such series of type-id-slots is associated to a distinct usage context. - ** Those usage contexts are discerned by the template parameter \c XY. Each of + ** Those usage contexts are discerned by the template parameter \c CX. Each of ** these usage contexts uses a separate numbering scheme on his own, i.e. every ** new type encountered at runtime gets the next higher ID number (slot). ** @warning the actual ID numbers depend on the sequence of first encountering diff --git a/src/proc/asset/entry-id.hpp b/src/proc/asset/entry-id.hpp index a352a8d4e..d4bc7b6db 100644 --- a/src/proc/asset/entry-id.hpp +++ b/src/proc/asset/entry-id.hpp @@ -46,6 +46,7 @@ #include "proc/asset.hpp" #include "proc/asset/struct-scheme.hpp" #include "lib/hash-indexed.hpp" +#include "lib/idi/genfunc.hpp" #include "lib/util.hpp" #include From dccc41f15668e94e36d544521bf15effb32c0f3b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 30 Jun 2015 02:31:36 +0200 Subject: [PATCH 21/47] EntryID(#865): switch ID generation to the newly defined generic ID functions ...first step to get rid of the proc::asset dependency --- src/lib/idi/genfunc.cpp | 2 +- src/lib/idi/genfunc.hpp | 8 ++++---- src/proc/asset/entry-id.hpp | 25 ++++++++++++------------- src/proc/asset/struct-scheme.hpp | 10 ---------- 4 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/lib/idi/genfunc.cpp b/src/lib/idi/genfunc.cpp index 76ca64e7b..bfcf7415f 100644 --- a/src/lib/idi/genfunc.cpp +++ b/src/lib/idi/genfunc.cpp @@ -40,7 +40,7 @@ namespace test{ // see test-helper.cpp namespace idi { - namespace { // generic entry points / integration helpers... + namespace format { // generic entry points / integration helpers... using lib::test::demangleCxx; diff --git a/src/lib/idi/genfunc.hpp b/src/lib/idi/genfunc.hpp index 53f760674..657f34d7c 100644 --- a/src/lib/idi/genfunc.hpp +++ b/src/lib/idi/genfunc.hpp @@ -57,7 +57,7 @@ namespace idi { using lib::HashVal; using std::string; - namespace { // integration helpers... + namespace format { // integration helpers... string demangled_innermost_component (const char* rawName); string demangled_sanitised_name (const char* rawName); @@ -75,7 +75,7 @@ namespace idi { inline string typeSymbol() { - return demangled_innermost_component (typeid(TY).name()); + return format::demangled_innermost_component (typeid(TY).name()); } /** Complete unique type identifier @@ -87,7 +87,7 @@ namespace idi { inline string typeFullID() { - return demangled_sanitised_name (typeid(TY).name()); + return format::demangled_sanitised_name (typeid(TY).name()); } template @@ -117,7 +117,7 @@ namespace idi { generateSymbolicID() { static TypedCounter instanceCounter; - return instance_formatter (namePrefix(), instanceCounter.inc()); + return format::instance_formatter (namePrefix(), instanceCounter.inc()); } /** diff --git a/src/proc/asset/entry-id.hpp b/src/proc/asset/entry-id.hpp index d4bc7b6db..d22e0d3a8 100644 --- a/src/proc/asset/entry-id.hpp +++ b/src/proc/asset/entry-id.hpp @@ -61,6 +61,8 @@ namespace asset { using std::string; using std::ostream; + using lib::idi::generateSymbolicID; + using lib::idi::getTypeHash; /** @@ -72,9 +74,12 @@ namespace asset { */ namespace idi { - using lib::hash::LuidH; - using lib::HashVal; + } + using lib::hash::LuidH; + using lib::HashVal; + + namespace { /** build up a hash value, packaged as LUID. * @param sym symbolic ID-string to be hashed @@ -131,9 +136,9 @@ namespace asset { * encoded into a hash seed. Thus even the same symbolicID * generates differing hash-IDs for different type parameters */ - BareEntryID (string const& symbolID, idi::HashVal seed =0) + BareEntryID (string const& symbolID, HashVal seed =0) : symbol_(util::sanitise(symbolID)) - , hash_(idi::buildHash (symbol_, seed)) + , hash_(buildHash (symbol_, seed)) { } public: @@ -201,7 +206,7 @@ namespace asset { /** case-1: auto generated symbolic ID */ EntryID() - : BareEntryID (idi::generateSymbolID(), getTypeHash()) /////////////TICKET #565 : how to organise access; this is not thread safe + : BareEntryID (generateSymbolicID(), getTypeHash()) { } /** case-2: explicitly specify a symbolic ID to use. @@ -210,7 +215,7 @@ namespace asset { */ explicit EntryID (string const& symbolID) - : BareEntryID (symbolID, getTypeHash()) + : BareEntryID (symbolID, getTypeHash()) { } @@ -228,12 +233,6 @@ namespace asset { return Asset::Ident (this->getSym(), cat); } - static idi::HashVal - getTypeHash() - { - return hash_value (Category (STRUCT, idi::StructTraits::catFolder())); - } - /** @return true if the upcast would yield exactly the same * tuple (symbol,type) as was used on original definition @@ -243,7 +242,7 @@ namespace asset { static bool canRecast (BareEntryID const& bID) { - return bID.getHash() == idi::buildHash (bID.getSym(), getTypeHash()); + return bID.getHash() == buildHash (bID.getSym(), getTypeHash()); } static EntryID diff --git a/src/proc/asset/struct-scheme.hpp b/src/proc/asset/struct-scheme.hpp index 5730d08b6..34b2bc581 100644 --- a/src/proc/asset/struct-scheme.hpp +++ b/src/proc/asset/struct-scheme.hpp @@ -148,16 +148,6 @@ namespace asset{ - template - inline string - generateSymbolID() - { - static uint i=0; - static format namePattern ("%s.%03d"); - ////////////////////////////////////////////////////////////////////////////////TICKET #166 : needs to be pushed down into a *.cpp - - return str(namePattern % StructTraits::namePrefix() % (++i) ); //////////TICKET #565 : how to organise access; this ought to be thread safe (-> EntryID ) - } }}} // namespace asset::idi From 7285c6f4d5431fc65ed671bd319cbfd4cfcd95ff Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 30 Jun 2015 03:06:17 +0200 Subject: [PATCH 22/47] reverse dependency order of Asset::Ident and EntryID --- src/proc/asset/entry-id.hpp | 23 +++++--------------- src/proc/asset/meta/time-grid.cpp | 4 ++-- src/proc/asset/struct-scheme.hpp | 26 +++++++++++++++++------ src/proc/mobject/session/generator-mo.hpp | 2 -- src/proc/streamtype.hpp | 8 ++++++- tests/core/proc/asset/entry-id-test.cpp | 11 +++++----- 6 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/proc/asset/entry-id.hpp b/src/proc/asset/entry-id.hpp index d22e0d3a8..02b8f49d9 100644 --- a/src/proc/asset/entry-id.hpp +++ b/src/proc/asset/entry-id.hpp @@ -43,8 +43,7 @@ #define ASSET_ENTRY_ID_H -#include "proc/asset.hpp" -#include "proc/asset/struct-scheme.hpp" +#include "lib/error.hpp" #include "lib/hash-indexed.hpp" #include "lib/idi/genfunc.hpp" #include "lib/util.hpp" @@ -58,11 +57,14 @@ namespace proc { namespace asset { + namespace error = lumiera::error; + using std::string; using std::ostream; using lib::idi::generateSymbolicID; using lib::idi::getTypeHash; + using lib::idi::typeSymbol; /** @@ -219,21 +221,6 @@ namespace asset { { } - /** generate an Asset identification tuple - * based on this EntryID's symbolic ID and type information. - * The remaining fields are filled in with hardwired defaults. - * @note there is a twist, as this asset identity tuple generates - * a different hash as the EntryID. It would be desirable - * to make those two addressing systems interchangeable. /////////////TICKET #739 - */ - Asset::Ident - getIdent() const - { - Category cat (STRUCT, idi::StructTraits::catFolder()); - return Asset::Ident (this->getSym(), cat); - } - - /** @return true if the upcast would yield exactly the same * tuple (symbol,type) as was used on original definition * of an ID, based on the given BareEntryID. Implemented @@ -258,7 +245,7 @@ namespace asset { operator string () const { - return "ID<"+idi::StructTraits::idSymbol()+">-"+EntryID::getSym(); + return "ID<"+typeSymbol()+">-"+EntryID::getSym(); } friend ostream& operator<< (ostream& os, EntryID const& id) { return os << string(id); } diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp index 8966b4383..12771ec67 100644 --- a/src/proc/asset/meta/time-grid.cpp +++ b/src/proc/asset/meta/time-grid.cpp @@ -22,6 +22,7 @@ #include "proc/asset/meta/time-grid.hpp" +#include "proc/asset/struct-scheme.hpp" #include "proc/asset/entry-id.hpp" #include "proc/assetmanager.hpp" #include "lib/time/quantiser.hpp" @@ -36,7 +37,6 @@ using util::_Fmt; using util::cStr; using util::isnil; -using boost::str; using std::string; @@ -50,7 +50,7 @@ namespace meta { /** */ TimeGrid::TimeGrid (EntryID const& nameID) - : Meta (nameID.getIdent()) + : Meta (idi::getAssetIdent (nameID)) { } diff --git a/src/proc/asset/struct-scheme.hpp b/src/proc/asset/struct-scheme.hpp index 34b2bc581..1f015a36e 100644 --- a/src/proc/asset/struct-scheme.hpp +++ b/src/proc/asset/struct-scheme.hpp @@ -37,6 +37,8 @@ #include "lib/symbol.hpp" +#include "proc/asset.hpp" +#include "proc/asset/entry-id.hpp" #include @@ -45,11 +47,9 @@ using boost::format; -namespace lumiera { - class StreamType; -} - namespace proc { + class StreamType; + namespace mobject { namespace session { @@ -105,7 +105,7 @@ namespace asset{ static Symbol catFolder() { return "pipes";} static Symbol idSymbol() { return "pipe"; } }; - template<> struct StructTraits + template<> struct StructTraits { static Symbol namePrefix() { return "type"; } static Symbol catFolder() { return "stream-types";} @@ -147,7 +147,21 @@ namespace asset{ - + /** generate an Asset identification tuple + * based on this EntryID's symbolic ID and type information. + * The remaining fields are filled in with hardwired defaults. + * @note there is a twist, as this asset identity tuple generates + * a different hash as the EntryID. It would be desirable + * to make those two addressing systems interchangeable. /////////////TICKET #739 + */ + template + inline Asset::Ident + getAssetIdent (EntryID const& entryID) + { + Category cat (STRUCT, idi::StructTraits::catFolder()); + return Asset::Ident (entryID.getSym(), cat); + } + }}} // namespace asset::idi diff --git a/src/proc/mobject/session/generator-mo.hpp b/src/proc/mobject/session/generator-mo.hpp index c0c64a4f9..ba72e0e9b 100644 --- a/src/proc/mobject/session/generator-mo.hpp +++ b/src/proc/mobject/session/generator-mo.hpp @@ -83,8 +83,6 @@ namespace session { DEFINE_PROCESSABLE_BY (builder::BuilderTool); }; - - typedef Placement PClipMO; diff --git a/src/proc/streamtype.hpp b/src/proc/streamtype.hpp index d42fbf6cd..296bc79b3 100644 --- a/src/proc/streamtype.hpp +++ b/src/proc/streamtype.hpp @@ -191,4 +191,10 @@ namespace proc { } // namespace proc -#endif + + +namespace lumiera { + using proc::StreamType; +} + +#endif /*PROC_STREAMTYPE_H*/ diff --git a/tests/core/proc/asset/entry-id-test.cpp b/tests/core/proc/asset/entry-id-test.cpp index 12bfda4ce..e9ceadfb8 100644 --- a/tests/core/proc/asset/entry-id-test.cpp +++ b/tests/core/proc/asset/entry-id-test.cpp @@ -25,6 +25,7 @@ #include "lib/test/test-helper.hpp" #include "proc/asset/entry-id.hpp" +#include "proc/asset/struct-scheme.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/mobject/session/fork.hpp" #include "lib/meta/trait-special.hpp" @@ -133,13 +134,13 @@ namespace test { checkBasicProperties () { ForkID tID(" test ⚡ ☠ ☭ ⚡ track "); - CHECK (tID.getIdent() == Asset::Ident("test_track", Category(STRUCT,"forks"), "lumi", 0)); + CHECK (idi::getAssetIdent(tID) == Asset::Ident("test_track", Category(STRUCT,"forks"), "lumi", 0)); CHECK (tID.getHash() == ForkID("☢ test ☢ track ☢").getHash()); - CHECK (tID.getSym() == tID.getIdent().name); - CHECK (ForkID().getIdent().category == Category (STRUCT,"forks")); - CHECK (ClipID().getIdent().category == Category (STRUCT,"clips")); + CHECK (tID.getSym() == idi::getAssetIdent(tID).name); + CHECK (idi::getAssetIdent(ForkID()).category == Category (STRUCT,"forks")); + CHECK (idi::getAssetIdent(ClipID()).category == Category (STRUCT,"clips")); ClipID cID2,cID3; CHECK (cID2.getSym() < cID3.getSym()); @@ -153,7 +154,7 @@ namespace test { tID = arbitrary; CHECK (tID.getHash() == arbitrary.getHash()); CHECK (tID.getSym() == arbitrary.getSym()); - CHECK (tID.getIdent()== arbitrary.getIdent()); + CHECK (idi::getAssetIdent(tID)== idi::getAssetIdent(arbitrary)); } cout << showSizeof() << endl; From f88236319fe75b16504a594f9b38ba993463c933 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 2 Jul 2015 19:13:50 +0200 Subject: [PATCH 23/47] relocate EntryID to library namespace --- src/proc/asset/entry-id.hpp | 34 ++++++++----------- src/proc/asset/meta.cpp | 1 + src/proc/asset/meta.hpp | 4 +-- src/proc/asset/meta/time-grid.cpp | 9 +++-- src/proc/asset/meta/time-grid.hpp | 6 ++-- src/proc/asset/struct-factory-impl.hpp | 1 + src/proc/asset/struct-scheme.hpp | 2 +- src/proc/mobject/session/fork.hpp | 14 +------- src/proc/mobject/session/mobjectfactory.hpp | 2 +- src/proc/streamtype.hpp | 2 +- tests/core/proc/asset/entry-id-test.cpp | 33 ++++++++++-------- .../proc/asset/meta/time-grid-basics-test.cpp | 1 - tests/core/proc/asset/typed-id-test.cpp | 14 ++++---- 13 files changed, 56 insertions(+), 67 deletions(-) diff --git a/src/proc/asset/entry-id.hpp b/src/proc/asset/entry-id.hpp index 02b8f49d9..39ee5d466 100644 --- a/src/proc/asset/entry-id.hpp +++ b/src/proc/asset/entry-id.hpp @@ -39,8 +39,8 @@ */ -#ifndef ASSET_ENTRY_ID_H -#define ASSET_ENTRY_ID_H +#ifndef LIB_IDI_ENTRY_ID_H +#define LIB_IDI_ENTRY_ID_H #include "lib/error.hpp" @@ -54,8 +54,16 @@ #include -namespace proc { -namespace asset { +namespace lib { + + /** + * Identification Schemes. + * Collection of commonly used mechanisms to build identification records, + * unique identifiers, registration numbers and hashes. These are used as glue + * and thin abstraction to link various subsystems or to allow interoperation + * of registration facilities + */ +namespace idi { namespace error = lumiera::error; @@ -65,19 +73,6 @@ namespace asset { using lib::idi::generateSymbolicID; using lib::idi::getTypeHash; using lib::idi::typeSymbol; - - - /** - * Identification Schemes. - * Collection of commonly used mechanisms to build identification records, - * unique identifiers, registration numbers and hashes. These are used as glue - * and thin abstraction to link various subsystems or to allow interoperation - * of registration facilities - */ - namespace idi { - - - } using lib::hash::LuidH; using lib::HashVal; @@ -126,7 +121,6 @@ namespace asset { class BareEntryID : public boost::equality_comparable { - typedef lib::hash::LuidH LuidH; string symbol_; LuidH hash_; @@ -281,5 +275,5 @@ namespace asset { -}} // namespace proc::asset -#endif +}} // namespace lib::idi +#endif /*LIB_IDI_ENTRY_ID_H*/ diff --git a/src/proc/asset/meta.cpp b/src/proc/asset/meta.cpp index 1b0bbaa13..a62cdfd5e 100644 --- a/src/proc/asset/meta.cpp +++ b/src/proc/asset/meta.cpp @@ -25,6 +25,7 @@ #include "proc/asset/meta.hpp" #include "lib/util.hpp" +using lib::idi::EntryID; namespace proc { namespace asset { diff --git a/src/proc/asset/meta.hpp b/src/proc/asset/meta.hpp index 4b719e6e1..fbd6d1937 100644 --- a/src/proc/asset/meta.hpp +++ b/src/proc/asset/meta.hpp @@ -153,10 +153,10 @@ namespace asset { typedef P PType; template - meta::Builder operator() (EntryID elementIdentity); + meta::Builder operator() (lib::idi::EntryID elementIdentity); template - meta::Builder operator() (meta::Descriptor const& prototype, EntryID elementIdentity); + meta::Builder operator() (meta::Descriptor const& prototype, lib::idi::EntryID elementIdentity); }; diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp index 12771ec67..107ee0765 100644 --- a/src/proc/asset/meta/time-grid.cpp +++ b/src/proc/asset/meta/time-grid.cpp @@ -23,7 +23,6 @@ #include "proc/asset/meta/time-grid.hpp" #include "proc/asset/struct-scheme.hpp" -#include "proc/asset/entry-id.hpp" #include "proc/assetmanager.hpp" #include "lib/time/quantiser.hpp" #include "lib/time/timevalue.hpp" @@ -49,7 +48,7 @@ namespace meta { /** */ - TimeGrid::TimeGrid (EntryID const& nameID) + TimeGrid::TimeGrid (GridID const& nameID) : Meta (idi::getAssetIdent (nameID)) { } @@ -108,12 +107,12 @@ namespace meta { { public: - SimpleTimeGrid (Time start, Duration frameDuration, EntryID const& name) + SimpleTimeGrid (Time start, Duration frameDuration, GridID const& name) : TimeGrid (name) , FixedFrameQuantiser(frameDuration,start) { } - SimpleTimeGrid (Time start, FrameRate frames_per_second, EntryID const& name) + SimpleTimeGrid (Time start, FrameRate frames_per_second, GridID const& name) : TimeGrid (name) , FixedFrameQuantiser(frames_per_second,start) { } @@ -149,7 +148,7 @@ namespace meta { _Fmt gridIdFormat("grid(%f_%d)"); id_ = string(gridIdFormat % fps_ % _raw(origin_)); } - EntryID nameID (id_); + GridID nameID (id_); return publishWrapped (*new SimpleTimeGrid(origin_, fps_, nameID)); } diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp index 5dd754865..b52faea7f 100644 --- a/src/proc/asset/meta/time-grid.hpp +++ b/src/proc/asset/meta/time-grid.hpp @@ -48,6 +48,7 @@ #define ASSET_META_TIME_GRID_H #include "proc/asset/meta.hpp" +#include "proc/asset/entry-id.hpp" #include "lib/time/grid.hpp" #include "lib/time/timevalue.hpp" #include "lib/symbol.hpp" @@ -67,7 +68,8 @@ namespace meta { class TimeGrid; - typedef P PGrid; + using PGrid = lib::P; + using GridID = lib::idi::EntryID; /** @@ -91,7 +93,7 @@ namespace meta { static PGrid build (Symbol gridID, FrameRate frames_per_second, Time origin); protected: - TimeGrid (EntryID const&); + TimeGrid (GridID const&); }; diff --git a/src/proc/asset/struct-factory-impl.hpp b/src/proc/asset/struct-factory-impl.hpp index 5eccf8fe1..1f4d574c1 100644 --- a/src/proc/asset/struct-factory-impl.hpp +++ b/src/proc/asset/struct-factory-impl.hpp @@ -78,6 +78,7 @@ namespace asset { using proc::mobject::session::Scope; using proc::mobject::session::match_specificFork; using proc::mobject::session::RBinding; + using proc::mobject::session::ForkID; using proc::mobject::session::RFork; using proc::mobject::session::Fork; diff --git a/src/proc/asset/struct-scheme.hpp b/src/proc/asset/struct-scheme.hpp index 1f015a36e..ed2184cb8 100644 --- a/src/proc/asset/struct-scheme.hpp +++ b/src/proc/asset/struct-scheme.hpp @@ -156,7 +156,7 @@ namespace asset{ */ template inline Asset::Ident - getAssetIdent (EntryID const& entryID) + getAssetIdent (lib::idi::EntryID const& entryID) { Category cat (STRUCT, idi::StructTraits::catFolder()); return Asset::Ident (entryID.getSym(), cat); diff --git a/src/proc/mobject/session/fork.hpp b/src/proc/mobject/session/fork.hpp index 358b74f86..aeff9b1f8 100644 --- a/src/proc/mobject/session/fork.hpp +++ b/src/proc/mobject/session/fork.hpp @@ -34,25 +34,13 @@ namespace proc { namespace mobject { namespace session { //////////////////////////////////////////////////////TICKET #637 - using lib::P; using lib::time::Time; class Fork; - typedef P PFork; + using ForkID = lib::idi::EntryID; -}} - -namespace asset { //////////////////////////////////////////////////////TICKET #637 - typedef EntryID ForkID; -} - - -namespace mobject { -namespace session { - - using asset::ForkID; //////////////////////////////////////////////////////TICKET #646 diff --git a/src/proc/mobject/session/mobjectfactory.hpp b/src/proc/mobject/session/mobjectfactory.hpp index b10d4fdc4..721316330 100644 --- a/src/proc/mobject/session/mobjectfactory.hpp +++ b/src/proc/mobject/session/mobjectfactory.hpp @@ -70,7 +70,7 @@ namespace session { Placement operator() (lumiera::query::DefsManager&); Placement operator() (asset::Clip const&, asset::Media const&); Placement operator() (asset::Clip const&, vector); - Placement operator() (asset::EntryID const&); + Placement operator() (lib::idi::EntryID const&); Placement operator() (asset::Effect const&); Placement
::ptrFromThis () { return static_pointer_cast,Asset> @@ -111,8 +111,8 @@ namespace test { template TestAsset::TestAsset (PAsset& pRef); template TestAsset::TestAsset (PAsset& pRef); - template P > TestAsset::ptrFromThis (); - template P > TestAsset::ptrFromThis (); + template lib::P > TestAsset::ptrFromThis (); + template lib::P > TestAsset::ptrFromThis (); diff --git a/tests/core/proc/asset/testasset.hpp b/tests/core/proc/asset/testasset.hpp index 188178bfd..0b4c73f5d 100644 --- a/tests/core/proc/asset/testasset.hpp +++ b/tests/core/proc/asset/testasset.hpp @@ -53,7 +53,7 @@ namespace test { static void deleter (TestAsset* aa) { delete aa; } public: - typedef P > PA; + typedef lib::P > PA; static PA create () { return (new TestAsset )->ptrFromThis(); } static PA create (PAsset& pRef) { return (new TestAsset (pRef))->ptrFromThis(); } From 16cc7e608c672dcd7c6b6f8e565c280e36d3fcd4 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 2 Jul 2015 19:24:44 +0200 Subject: [PATCH 25/47] EntryID(#865): move into the support library does no longer depend on the asset subsystem --- src/{proc/asset => lib/idi}/entry-id.hpp | 0 src/proc/asset/meta.hpp | 2 +- src/proc/asset/meta/time-grid.hpp | 2 +- src/proc/asset/struct-scheme.hpp | 2 +- src/proc/mobject/session/fork.hpp | 2 +- src/proc/mobject/session/mobjectfactory.hpp | 2 +- src/proc/streamtype.hpp | 2 +- tests/core/proc/asset/typed-id-test.cpp | 2 +- tests/{core/proc/asset => library}/entry-id-test.cpp | 4 ++-- 9 files changed, 9 insertions(+), 9 deletions(-) rename src/{proc/asset => lib/idi}/entry-id.hpp (100%) rename tests/{core/proc/asset => library}/entry-id-test.cpp (99%) diff --git a/src/proc/asset/entry-id.hpp b/src/lib/idi/entry-id.hpp similarity index 100% rename from src/proc/asset/entry-id.hpp rename to src/lib/idi/entry-id.hpp diff --git a/src/proc/asset/meta.hpp b/src/proc/asset/meta.hpp index 0af580a18..b7518b0ed 100644 --- a/src/proc/asset/meta.hpp +++ b/src/proc/asset/meta.hpp @@ -60,7 +60,7 @@ #define ASSET_META_H #include "proc/asset.hpp" -#include "proc/asset/entry-id.hpp" +#include "lib/idi/entry-id.hpp" #include diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp index d45fe78bc..2cf593edb 100644 --- a/src/proc/asset/meta/time-grid.hpp +++ b/src/proc/asset/meta/time-grid.hpp @@ -48,7 +48,7 @@ #define ASSET_META_TIME_GRID_H #include "proc/asset/meta.hpp" -#include "proc/asset/entry-id.hpp" +#include "lib/idi/entry-id.hpp" #include "lib/time/grid.hpp" #include "lib/time/timevalue.hpp" #include "lib/symbol.hpp" diff --git a/src/proc/asset/struct-scheme.hpp b/src/proc/asset/struct-scheme.hpp index ed2184cb8..350f1d090 100644 --- a/src/proc/asset/struct-scheme.hpp +++ b/src/proc/asset/struct-scheme.hpp @@ -38,7 +38,7 @@ #include "lib/symbol.hpp" #include "proc/asset.hpp" -#include "proc/asset/entry-id.hpp" +#include "lib/idi/entry-id.hpp" #include diff --git a/src/proc/mobject/session/fork.hpp b/src/proc/mobject/session/fork.hpp index aeff9b1f8..31eec9444 100644 --- a/src/proc/mobject/session/fork.hpp +++ b/src/proc/mobject/session/fork.hpp @@ -25,7 +25,7 @@ #define MOBJECT_SESSION_FORK_H #include "proc/mobject/session/meta.hpp" -#include "proc/asset/entry-id.hpp" +#include "lib/idi/entry-id.hpp" #include "lib/time/timevalue.hpp" diff --git a/src/proc/mobject/session/mobjectfactory.hpp b/src/proc/mobject/session/mobjectfactory.hpp index a207dd7f9..86a50b6f4 100644 --- a/src/proc/mobject/session/mobjectfactory.hpp +++ b/src/proc/mobject/session/mobjectfactory.hpp @@ -25,7 +25,7 @@ #define PROC_MOBJECT_SESSION_MOBJECTFACTORY_H #include "proc/mobject/mobject.hpp" -#include "proc/asset/entry-id.hpp" +#include "lib/idi/entry-id.hpp" #include "lib/symbol.hpp" diff --git a/src/proc/streamtype.hpp b/src/proc/streamtype.hpp index 195e502b9..e357bd694 100644 --- a/src/proc/streamtype.hpp +++ b/src/proc/streamtype.hpp @@ -37,7 +37,7 @@ #include "lib/symbol.hpp" //#include "common/query.hpp" -#include "proc/asset/entry-id.hpp" +#include "lib/idi/entry-id.hpp" #include diff --git a/tests/core/proc/asset/typed-id-test.cpp b/tests/core/proc/asset/typed-id-test.cpp index 09ed647cd..65e54cdc8 100644 --- a/tests/core/proc/asset/typed-id-test.cpp +++ b/tests/core/proc/asset/typed-id-test.cpp @@ -25,7 +25,7 @@ #include "lib/test/test-helper.hpp" #include "proc/asset/typed-id.hpp" -#include "proc/asset/entry-id.hpp" +#include "lib/idi/entry-id.hpp" #include "lib/p.hpp" #include "proc/assetmanager.hpp" #include "proc/asset/inventory.hpp" diff --git a/tests/core/proc/asset/entry-id-test.cpp b/tests/library/entry-id-test.cpp similarity index 99% rename from tests/core/proc/asset/entry-id-test.cpp rename to tests/library/entry-id-test.cpp index a144ed23c..c846b0744 100644 --- a/tests/core/proc/asset/entry-id-test.cpp +++ b/tests/library/entry-id-test.cpp @@ -24,7 +24,7 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" -#include "proc/asset/entry-id.hpp" +#include "lib/idi/entry-id.hpp" #include "proc/asset/struct-scheme.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/mobject/session/fork.hpp" @@ -293,7 +293,7 @@ namespace test{ /** Register this test class... */ - LAUNCHER (EntryID_test, "unit asset"); + LAUNCHER (EntryID_test, "unit common"); }}} // namespace lib::idi::test From 9b694044ebcb80ea933d7bfb1e88e64f235283af Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 3 Jul 2015 00:33:02 +0200 Subject: [PATCH 26/47] clean-up: rename variable in Test formerly 'track' now 'fork' --- tests/library/entry-id-test.cpp | 42 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/library/entry-id-test.cpp b/tests/library/entry-id-test.cpp index c846b0744..670f1f861 100644 --- a/tests/library/entry-id-test.cpp +++ b/tests/library/entry-id-test.cpp @@ -205,49 +205,49 @@ namespace test{ void checkErasure () { - ForkID tID("suspicious"); + ForkID fID("suspicious"); ClipID cID("suspicious"); - CHECK (tID.getHash() != cID.getHash()); - CHECK (tID.getSym() == cID.getSym()); + CHECK (fID.getHash() != cID.getHash()); + CHECK (fID.getSym() == cID.getSym()); - BareEntryID bIDt (tID); + BareEntryID bIDf (fID); BareEntryID bIDc (cID); - CHECK (bIDt != bIDc); - CHECK (bIDt.getHash() != bIDc.getHash()); - CHECK (bIDt.getSym() == bIDc.getSym()); + CHECK (bIDf != bIDc); + CHECK (bIDf.getHash() != bIDc.getHash()); + CHECK (bIDf.getSym() == bIDc.getSym()); CHECK ("suspicious" == bIDc.getSym()); using proc::mobject::session::Fork; using proc::mobject::session::Clip; - ForkID tIDnew = bIDt.recast(); + ForkID tIDnew = bIDf.recast(); ClipID cIDnew = bIDc.recast(); - CHECK (tIDnew == tID); + CHECK (tIDnew == fID); CHECK (cIDnew == cID); - VERIFY_ERROR (WRONG_TYPE, bIDt.recast()); + VERIFY_ERROR (WRONG_TYPE, bIDf.recast()); VERIFY_ERROR (WRONG_TYPE, bIDc.recast()); VERIFY_ERROR (WRONG_TYPE, bIDc.recast()); - VERIFY_ERROR (WRONG_TYPE, bIDt.recast()); + VERIFY_ERROR (WRONG_TYPE, bIDf.recast()); - CHECK (tID == ForkID::recast (bIDt)); // equivalent static API on typed subclass + CHECK (fID == ForkID::recast (bIDf)); // equivalent static API on typed subclass VERIFY_ERROR (WRONG_TYPE, ForkID::recast(bIDc)); - VERIFY_ERROR (WRONG_TYPE, ClipID::recast(bIDt)); + VERIFY_ERROR (WRONG_TYPE, ClipID::recast(bIDf)); VERIFY_ERROR (WRONG_TYPE, DummyID::recast(bIDc)); - VERIFY_ERROR (WRONG_TYPE, DummyID::recast(bIDt)); + VERIFY_ERROR (WRONG_TYPE, DummyID::recast(bIDf)); // mixed equality comparisons (based on the hash) - BareEntryID bIDt_copy (bIDt); - CHECK (bIDt == bIDt_copy); - CHECK (!isSameObject (bIDt, bIDt_copy)); + BareEntryID bIDt_copy (bIDf); + CHECK (bIDf == bIDt_copy); + CHECK (!isSameObject (bIDf, bIDt_copy)); - CHECK (tID != bIDc); + CHECK (fID != bIDc); CHECK (cID != bIDt_copy); - CHECK (tID == bIDt_copy); + CHECK (fID == bIDt_copy); - CHECK (bIDt == ForkID ("suspicious")); - CHECK (bIDt != ClipID ("suspicious")); + CHECK (bIDf == ForkID ("suspicious")); + CHECK (bIDf != ClipID ("suspicious")); CHECK (bIDc == ClipID ("suspicious")); CHECK (ForkID ("suspicious") != ClipID ("suspicious")); } From 9d42b58aae9a9eb096b0c8a54c622331e01c20e4 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 3 Jul 2015 00:45:02 +0200 Subject: [PATCH 27/47] EntryID implementation changes for #956 - move the santitise operation up into EntryID's ctor - turn the recast() operation into a real in-place cast these changes should be transparent to the existing usages of EntryID (within the asset framework), but allow for use as attribute name holder in GenNode, since we're now able to feed existing name/ID values directly into the ctor of BareEntryID, without any spurious santitise operation. --- src/lib/idi/entry-id.hpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/lib/idi/entry-id.hpp b/src/lib/idi/entry-id.hpp index 39ee5d466..edca5c30f 100644 --- a/src/lib/idi/entry-id.hpp +++ b/src/lib/idi/entry-id.hpp @@ -133,7 +133,7 @@ namespace idi { * generates differing hash-IDs for different type parameters */ BareEntryID (string const& symbolID, HashVal seed =0) - : symbol_(util::sanitise(symbolID)) + : symbol_(symbolID) , hash_(buildHash (symbol_, seed)) { } @@ -169,7 +169,7 @@ namespace idi { template - EntryID recast() const; + EntryID const& recast() const; }; @@ -211,7 +211,7 @@ namespace idi { */ explicit EntryID (string const& symbolID) - : BareEntryID (symbolID, getTypeHash()) + : BareEntryID (util::sanitise(symbolID), getTypeHash()) { } @@ -226,14 +226,14 @@ namespace idi { return bID.getHash() == buildHash (bID.getSym(), getTypeHash()); } - static EntryID + static EntryID const& recast (BareEntryID const& bID) { if (!canRecast(bID)) throw error::Logic ("unable to recast EntryID: desired type " "doesn't match original definition" , error::LUMIERA_ERROR_WRONG_TYPE); - return EntryID (bID.getSym()); + return static_cast (bID); } @@ -268,7 +268,8 @@ namespace idi { * Exception if it doesn't match the stored hash. */ template - EntryID BareEntryID::recast() const + EntryID const& + BareEntryID::recast() const { return EntryID::recast(*this); } From 150fdea7a0ad63897f8359e565a178794f34fa9b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 15 Aug 2015 05:31:50 +0200 Subject: [PATCH 28/47] improve spread of the hash function used for EntryID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit basically this is the well known problem #587 Just it became more pressing with the Upgrade to Jessie and Boost 1.55 So I've pulled off the well known "Knuth trick" to spread the input data more evenly within the hash domain. And voilà: now we're able to use 100000 number suffixes without collision --- src/lib/idi/entry-id.hpp | 22 +++++++++--- src/lib/idi/genfunc.hpp | 2 ++ tests/library/entry-id-test.cpp | 11 ++++-- tests/library/hash-generator-test.cpp | 52 +++++++++++++++++++++++++-- 4 files changed, 77 insertions(+), 10 deletions(-) diff --git a/src/lib/idi/entry-id.hpp b/src/lib/idi/entry-id.hpp index edca5c30f..4579fc438 100644 --- a/src/lib/idi/entry-id.hpp +++ b/src/lib/idi/entry-id.hpp @@ -77,6 +77,14 @@ namespace idi { using lib::HashVal; namespace { + /** lousy old tinkerer's trick: + * hash values with poor distribution can be improved + * by spreading the input with something close to the golden ratio. + * Additionally, the scaling factor (for hashing) should be prime. + * 2^32 * (√5-1)/2 = 2654435769.49723 + */ + const size_t KNUTH_MAGIC = 2654435761; + /** build up a hash value, packaged as LUID. * @param sym symbolic ID-string to be hashed @@ -92,16 +100,22 @@ namespace idi { * conjunction with LUID. How to create a LuidH instance, if not generating * a new random value. How to make EntryID and asset::Ident interchangeable, /////////TICKET #739 * which would require both to yield the same hash values.... - * @warning there is a weakness in boost::hash for strings of running numbers, causing - * collisions already for a small set with less than 100000 entries. - * To ameliorate the problem, we hash the symbol twice /////////TICKET #865 + * @warning there is a weakness in boost::hash for strings of running numbers, + * causing collisions already for a small set with less than 100000 entries. + * To ameliorate the problem, we hash in the trailing digits, and + * spread them by the #KNUTH_MAGIC /////////TICKET #865 * @warning this code isn't portable and breaks if sizeof(size_t) < sizeof(void*) + * @see HashGenerator_test#verify_Knuth_workaround */ inline LuidH buildHash (string const& sym, HashVal seed =0) { + size_t l = sym.length(); + if (l > 1) boost::hash_combine(seed, KNUTH_MAGIC * sym[l-1]); + if (l > 2) boost::hash_combine(seed, KNUTH_MAGIC * sym[l-2]); + if (l > 3) boost::hash_combine(seed, KNUTH_MAGIC * sym[l-3]); ////////////////////////TICKET #865 + boost::hash_combine(seed, sym); - boost::hash_combine(seed, sym); ////////////////////////TICKET #865 lumiera_uid tmpLUID; lumiera_uid_set_ptr (&tmpLUID, reinterpret_cast (seed)); return reinterpret_cast (tmpLUID); diff --git a/src/lib/idi/genfunc.hpp b/src/lib/idi/genfunc.hpp index 657f34d7c..45cc4f434 100644 --- a/src/lib/idi/genfunc.hpp +++ b/src/lib/idi/genfunc.hpp @@ -108,6 +108,8 @@ namespace idi { /** build a per-type unique identifier. * @return a type based prefix, followed by an instance number * @note we use the short prefix without namespace, not necessarily unique + * @todo consequently the generated IDs might clash for two distinct types, + * which generate the same \c namePrefix(). Is this a problem? * @warning this operation is not exactly cheap; it acquires a lock * for the counter and, after increasing and dropping the lock, * it builds and uses a boost::format instance. diff --git a/tests/library/entry-id-test.cpp b/tests/library/entry-id-test.cpp index 670f1f861..354849640 100644 --- a/tests/library/entry-id-test.cpp +++ b/tests/library/entry-id-test.cpp @@ -258,20 +258,25 @@ namespace test{ typedef std::unordered_map Hashtable; /** @test build a hashtable, using EntryID as key, - * thereby using the embedded hash-ID */ + * thereby using the embedded hash-ID + * @note there is a known weakness of the boost::hash + * when used on IDs with a running number suffix. /////TICKET #587 + * We use a trick to spread the numbers better. + * @see HashGenerator_test#verify_Knuth_workaround + */ void buildHashtable () { Hashtable tab; - for (uint i=0; i<10000; ++i) //////////////////TICKET #865 hash collisions for 100000 entries + for (uint i=0; i<100000; ++i) { DummyID dummy; tab[dummy] = string(dummy); } CHECK (and_all (tab, verifyEntry)); - CHECK (10000 == tab.size()); + CHECK (100000 == tab.size()); } diff --git a/tests/library/hash-generator-test.cpp b/tests/library/hash-generator-test.cpp index 1d35d9e8b..930c60824 100644 --- a/tests/library/hash-generator-test.cpp +++ b/tests/library/hash-generator-test.cpp @@ -58,9 +58,14 @@ namespace test{ run (Arg) { demonstrate_boost_hash_weakness(); + verify_Knuth_workaround(); } + typedef boost::hash BoostStringHasher; + typedef std::map StringsTable; + + /** @test demonstrate a serious weakness of boost::hash for strings. * When hashing just the plain string representation of integers, * we get collisions already with small numbers below 100000. @@ -73,9 +78,6 @@ namespace test{ void demonstrate_boost_hash_weakness () { - typedef boost::hash BoostStringHasher; - typedef std::map StringsTable; - BoostStringHasher hashFunction; StringsTable hashValues; string prefix = "Entry."; @@ -103,6 +105,50 @@ namespace test{ CHECK (0 < collisions, "boost::hash for strings is expected to produce collisions"); } + + + /** @test verify a well-known pragmatic trick to help with unevenly spaced hash values. + * The boost::hash function is known to perform poorly on strings with common prefix + * plus running number. The mentioned trick (attributed to Donald Knuth) is spread the + * input numbers by something below the full domain, best close to the golden ratio; + * bonus points if this number is also a prime. An additional factor of 2 does not hurt + * (so in case of 64bit platform). + * + * In our case, it is sufficient to apply this trick to the trailing two digits; + * without this trick, we get the first collisions after about 20000 running numbers. + * @see BareEntryID + */ + void + verify_Knuth_workaround() + { + StringsTable hashValues; + string prefix = "Entry."; + const size_t seed = rand(); + + const size_t KNUTH_MAGIC = 2654435761; + + uint collisions(0); + for (uint i=0; i<100000; ++i) + { + string candidate = prefix + lexical_cast (i); + size_t l = candidate.length(); + size_t hashVal = seed; + + boost::hash_combine(hashVal, KNUTH_MAGIC * candidate[l-1]); + boost::hash_combine(hashVal, KNUTH_MAGIC * candidate[l-2]); + boost::hash_combine(hashVal, candidate); + + if (contains (hashValues, hashVal)) + { + ++collisions; + string other = hashValues[hashVal]; + cout << "Hash collision between " << i << " and " << other < Date: Sat, 15 Aug 2015 05:40:14 +0200 Subject: [PATCH 29/47] EntryID : fix test definition to account for the changed ID format The format of the string representation of EntryID was altered with the switch to our 'generic ID functions' in 1898d9 --- tests/41asset.tests | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/41asset.tests b/tests/41asset.tests index fe1e581d3..6daeaa629 100644 --- a/tests/41asset.tests +++ b/tests/41asset.tests @@ -34,14 +34,14 @@ END TEST "plain entry-ID" EntryID_test <-..proc.asset.+Dummy.\.001 -out: ID<..proc.asset.+Dummy.>-strange -out: ID<..proc.asset.+Dummy.>-..proc.asset.+Dummy.\.002 -out: ID-fork\.001 -out: ID-fork\.002 -out: ID-special -out: sizeof\( ..proc.asset.+EntryID.+mobject.+session.+Fork.+ \) = -out: sizeof\( ..proc.asset.+BareEntryID. \) = +out-lit: ID-Dummy.001 +out-lit: ID-strange +out-lit: ID-Dummy.002 +out-lit: ID-Fork.001 +out-lit: ID-Fork.002 +out-lit: ID-special +out: sizeof\( ..lib.idi.EntryID.+mobject.+session.+Fork.+ \) = +out: sizeof\( ..lib.idi.+BareEntryID. \) = return: 0 END From f15266e43555ba46f823a7b8757c6c7a38905242 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 3 Jul 2015 04:13:16 +0200 Subject: [PATCH 30/47] GenNode(#956): define the ctors implies decision on the ID representation --- src/lib/diff/gen-node.hpp | 86 ++++++++++++++++++++-- src/lib/variant.hpp | 6 ++ tests/library/diff/gen-node-basic-test.cpp | 13 ++-- wiki/thinkPad.ichthyo.mm | 8 +- 4 files changed, 99 insertions(+), 14 deletions(-) diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index a821db633..09ad59421 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -101,6 +101,7 @@ #include "lib/variant.hpp" #include "lib/time/timevalue.hpp" #include "lib/diff/record.hpp" +#include "lib/idi/entry-id.hpp" //#include "lib/util.hpp" //#include "lib/format-string.hpp" @@ -148,13 +149,88 @@ namespace diff{ DataCap(X&& x) : Variant(std::forward(x)) { } + + DataCap(DataCap const& o) =default; + DataCap(DataCap&& o) =default; + DataCap(DataCap& o) + : DataCap((DataCap const&)o) + { } }; /** generic data element node within a tree */ - class GenNode + struct GenNode { - public: + class ID + : public idi::BareEntryID + { + friend class GenNode; + + template + ID (X*, string const& symbolicID) + : idi::BareEntryID (symbolicID, + idi::getTypeHash()) + { } + + public: + ID (GenNode const& node) + : ID(node.idi) + { } + + // standard copy operations acceptable + }; + + ID idi; + DataCap data; + + template + GenNode(X&& val) + : idi(&val, buildChildID()) + , data(std::forward(val)) + { } + + template + GenNode(string const& symbolicID, X&& val) + : idi(&val, symbolicID) + , data(std::forward(val)) + { } + + GenNode(string const& symbolicID, const char* text) + : GenNode(symbolicID, string(text)) + { } + + GenNode(const char* text) + : GenNode(string(text)) + { } + + GenNode(GenNode const& o) =default; + GenNode(GenNode&& o) =default; + GenNode(GenNode& o) + : GenNode((GenNode const&)o) + { } + + // default copy assignable + + + bool + isNamed() const + { + return util::startsWith (idi.getSym(), "_CHILD_"); + } + + friend string + name (GenNode const& node) + { + return node.idi.getSym(); + } + + private: + template + static string + buildChildID() + { + return "_CHILD_" + idi::generateSymbolicID(); + } }; @@ -186,21 +262,21 @@ namespace diff{ inline GenNode Rec::buildTypeAttribute (string const& typeID) { - return GenNode(); ///TODO + return GenNode("type", typeID); } template<> inline string Rec::extractKey (GenNode const& v) { - return "todo"; ////TODO + return v.idi.getSym(); } template<> inline GenNode Rec::extractVal (GenNode const& v) { - return GenNode(); ///TODO + return GenNode(v); ///TODO } diff --git a/src/lib/variant.hpp b/src/lib/variant.hpp index 938fd2203..2ffb00143 100644 --- a/src/lib/variant.hpp +++ b/src/lib/variant.hpp @@ -111,6 +111,12 @@ namespace lib { using Type = X; }; + template + struct CanBuildFrom> + { + using Type = X; + }; + template struct CanBuildFrom> { diff --git a/tests/library/diff/gen-node-basic-test.cpp b/tests/library/diff/gen-node-basic-test.cpp index 58ed90248..161b47854 100644 --- a/tests/library/diff/gen-node-basic-test.cpp +++ b/tests/library/diff/gen-node-basic-test.cpp @@ -33,7 +33,9 @@ //#include #include +using util::isnil; using util::contains; +using util::isSameObject; using lib::time::Time; using lib::time::TimeSpan; //using std::string; @@ -93,7 +95,6 @@ namespace test{ objectShortcut(); symbolReference(); sequenceIteration(); - duplicateDetection(); copy_and_move(); } @@ -110,7 +111,7 @@ namespace test{ // can optionally be named GenNode n2("π", 3.14159265358979323846264338328); - CHECK (fabs (3.14159265 - n2.data.get) < 1e-5 ); + CHECK (fabs (3.14159265 - n2.data.get()) < 1e-5 ); CHECK (n2.isNamed()); CHECK ("π" == n2.idi.getSym()); CHECK ("π" == name(n2)); @@ -255,18 +256,14 @@ namespace test{ void sequenceIteration() { - } - - - void - duplicateDetection() - { + UNIMPLEMENTED ("wtf"); } void copy_and_move() { + UNIMPLEMENTED ("need to check that?"); } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index d3566b380..8357203dc 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -35,7 +35,13 @@ - + + + + + + + From 8c78af2adcf3fe5fc103016ebc82c6c18c51a563 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 3 Jul 2015 15:42:25 +0200 Subject: [PATCH 31/47] bool conversion for record references (see also #477) I decided to allow for an 'unbound' reference to allow default construction of elements involving record references. I am aware of the implications, but I place the focus on the value nature of GenNode elements; the RecordRef was introduced only as a means to cary out diff comparisons and similar computations. --- src/lib/diff/record.hpp | 6 ++++++ tests/library/diff/generic-record-representation-test.cpp | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp index 3f4d6f306..5a4ab6a3b 100644 --- a/src/lib/diff/record.hpp +++ b/src/lib/diff/record.hpp @@ -428,6 +428,12 @@ namespace diff{ // standard copy operations acceptable + explicit + operator bool() const + { + return bool(record_); + } + /** target is accessed by cast * @throws error::Logic on bottom reference */ diff --git a/tests/library/diff/generic-record-representation-test.cpp b/tests/library/diff/generic-record-representation-test.cpp index 3ae9245a5..d6603335c 100644 --- a/tests/library/diff/generic-record-representation-test.cpp +++ b/tests/library/diff/generic-record-representation-test.cpp @@ -306,7 +306,7 @@ namespace test{ RecordRef empty; CHECK (bool(empty) == false); CHECK (nullptr == empty.get()); - VERIFY_ERROR (BOTTOM_VALUE, RecS& (empty)); + VERIFY_ERROR (BOTTOM_VALUE, empty.operator RecS&() ); RecordRef ref(oo); CHECK (ref); From 0cec3490fe62e9130af677089e0234de4f37b2fc Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 3 Jul 2015 18:08:28 +0200 Subject: [PATCH 32/47] WIP: Forwarding ctor shadows standard copy operations (#963) unsuccssful attempt to come up with a generic remedy. Aborted this attempt and stashed it away as TICKET #963 --- src/lib/diff/gen-node.hpp | 44 +++++++++++++++++++++++++++------------ src/lib/variant.hpp | 7 +++++-- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index 09ad59421..2f6f08aa7 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -121,6 +121,33 @@ namespace diff{ using std::string; + namespace {//////TODO this is a prototype, to be factored out + + template + struct ShaddowCopyCtor + { + operator bool() const { return true; } + }; + + template + struct ShaddowCopyCtor + { + // no bool conversion -> substitution fails. + }; + template + struct ShaddowCopyCtor + { + // no bool conversion -> substitution fails. + }; + template + struct ShaddowCopyCtor + { + // no bool conversion -> substitution fails. + }; + + }//(End) copy shaddowing solution + + class GenNode; using Rec = Record; @@ -146,15 +173,11 @@ namespace diff{ { public: template - DataCap(X&& x) + DataCap(X&& x, bool = ShaddowCopyCtor()) : Variant(std::forward(x)) { } - DataCap(DataCap const& o) =default; - DataCap(DataCap&& o) =default; - DataCap(DataCap& o) - : DataCap((DataCap const&)o) - { } + ////////////////////////TICKET #963 Forwarding shadows copy operations -- generic solution?? }; @@ -183,8 +206,9 @@ namespace diff{ ID idi; DataCap data; + template - GenNode(X&& val) + GenNode(X&& val, bool = ShaddowCopyCtor()) : idi(&val, buildChildID()) , data(std::forward(val)) { } @@ -203,12 +227,6 @@ namespace diff{ : GenNode(string(text)) { } - GenNode(GenNode const& o) =default; - GenNode(GenNode&& o) =default; - GenNode(GenNode& o) - : GenNode((GenNode const&)o) - { } - // default copy assignable diff --git a/src/lib/variant.hpp b/src/lib/variant.hpp index 2ffb00143..6a6640e7b 100644 --- a/src/lib/variant.hpp +++ b/src/lib/variant.hpp @@ -142,12 +142,11 @@ namespace lib { = meta::InstantiateForEach; - /////TODO: - is it possible directly to forward the constructor invocation to the object within the buffer? Beware of unverifiable generic solutions! - }//(End) implementation helpers + /** * Typesafe union record. * A Variant element may carry an embedded value of any of the predefined types. @@ -160,6 +159,10 @@ namespace lib { * in question, but type mismatch will provoke an exception at runtime. * Generic access is possible using a visitor. * @warning not threadsafe + * @todo we need to define all copy operations explicitly, due to the + * templated one-arg ctor to wrap the actual values. + * There might be a generic solution for that ////////////////////////TICKET #963 Forwarding shadows copy operations -- generic solution?? + * But -- Beware of unverifiable generic solutions! */ template class Variant From 1fa7a4a43769e1bc0571d52f2c022409653ea745 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 3 Jul 2015 18:16:54 +0200 Subject: [PATCH 33/47] WIP: define the full set of default copy operations explicitly on a second thought, this "workaround" does not look so bad, due to the C++11 feature to request the default implementation explicitly. Maybe we'll never need a generic solution for these cases --- src/lib/diff/gen-node.hpp | 50 ++++++++++++++++----------------------- wiki/thinkPad.ichthyo.mm | 34 +++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 31 deletions(-) diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index 2f6f08aa7..6c5e81b49 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -121,33 +121,6 @@ namespace diff{ using std::string; - namespace {//////TODO this is a prototype, to be factored out - - template - struct ShaddowCopyCtor - { - operator bool() const { return true; } - }; - - template - struct ShaddowCopyCtor - { - // no bool conversion -> substitution fails. - }; - template - struct ShaddowCopyCtor - { - // no bool conversion -> substitution fails. - }; - template - struct ShaddowCopyCtor - { - // no bool conversion -> substitution fails. - }; - - }//(End) copy shaddowing solution - - class GenNode; using Rec = Record; @@ -173,11 +146,19 @@ namespace diff{ { public: template - DataCap(X&& x, bool = ShaddowCopyCtor()) + DataCap(X&& x) : Variant(std::forward(x)) { } ////////////////////////TICKET #963 Forwarding shadows copy operations -- generic solution?? + DataCap(DataCap const&) =default; + DataCap(DataCap&&) =default; + DataCap(DataCap& o) + : DataCap((DataCap const&)o) + { } + + DataCap& operator= (DataCap const&) =default; + DataCap& operator= (DataCap&&) =default; }; @@ -208,7 +189,7 @@ namespace diff{ template - GenNode(X&& val, bool = ShaddowCopyCtor()) + GenNode(X&& val) : idi(&val, buildChildID()) , data(std::forward(val)) { } @@ -227,7 +208,16 @@ namespace diff{ : GenNode(string(text)) { } - // default copy assignable + ////////////////////////TICKET #963 Forwarding shadows copy operations -- generic solution?? + GenNode(GenNode const&) =default; + GenNode(GenNode&&) =default; + GenNode(GenNode& o) + : GenNode((GenNode const&)o) + { } + + GenNode& operator= (GenNode const&) =default; + GenNode& operator= (GenNode&&) =default; + bool diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 8357203dc..0714b3e3a 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -46,7 +46,39 @@ - + + + + + + + + + + + + + + +

+ ich hatte damals beim Variant und zugehörigen Buffer die Sorge, +

+

+ daß ich die Implikationen einer generischen Lösung nicht durchdringen kann. +

+

+ Und ich wollte keine Zeit auf einen exzessiven Unit-Test verwenden +

+ + +
+
+
+ + + + +
From da148e9758cac90338f5169b4f70b2bcf0a95f5c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 3 Jul 2015 18:21:46 +0200 Subject: [PATCH 34/47] WIP: equality comparisons for GenNode forwarding equality to the embedded EntryID Basically, two GenNodes are equal when they have the same "identity" Ironically, this is the usual twist with database entities --- src/lib/diff/gen-node.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index 6c5e81b49..947fcfc4b 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -184,6 +184,9 @@ namespace diff{ // standard copy operations acceptable }; + + //------GenNode Data fields--- + ID idi; DataCap data; @@ -232,6 +235,19 @@ namespace diff{ return node.idi.getSym(); } + friend bool + operator== (GenNode const& n1, GenNode const& n2) + { + return n1.idi == n2.idi; + } + + friend bool + operator!= (GenNode const& n1, GenNode const& n2) + { + return n1.idi != n2.idi; + } + + private: template static string From 8e990fc04d424a3975f82f1c5d30ba80580da998 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 3 Jul 2015 19:18:49 +0200 Subject: [PATCH 35/47] WIP: simple implementation / stubbing especially I've now decided how to handle const-ness: We're open to all forms of const-ness, the actual usage decides. const GenNode will only expose a const& to the data values still TODO is the object builder notation for diff::Record --- src/lib/diff/gen-node.hpp | 8 +- src/lib/variant.hpp | 6 + tests/library/diff/gen-node-basic-test.cpp | 2 + .../diff/generic-tree-mutator-test.cpp | 14 --- wiki/renderengine.html | 14 ++- wiki/thinkPad.ichthyo.mm | 116 ++++++++++++++++-- 6 files changed, 131 insertions(+), 29 deletions(-) diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index 947fcfc4b..594f0881d 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -104,8 +104,7 @@ #include "lib/idi/entry-id.hpp" //#include "lib/util.hpp" //#include "lib/format-string.hpp" - -#include "lib/format-util.hpp" +//#include "lib/format-util.hpp" #include "lib/variant.hpp" #include "lib/util.hpp" @@ -182,6 +181,11 @@ namespace diff{ { } // standard copy operations acceptable + + operator string() const + { + return "ID(\""+getSym()+"\")"; + } }; diff --git a/src/lib/variant.hpp b/src/lib/variant.hpp index 6a6640e7b..35a9d40e0 100644 --- a/src/lib/variant.hpp +++ b/src/lib/variant.hpp @@ -404,6 +404,12 @@ namespace lib { return buff().access(); } + template + X const& + get() const + { + return unConst(this)->get(); + } void accept (Visitor& visitor) diff --git a/tests/library/diff/gen-node-basic-test.cpp b/tests/library/diff/gen-node-basic-test.cpp index 161b47854..cd561579b 100644 --- a/tests/library/diff/gen-node-basic-test.cpp +++ b/tests/library/diff/gen-node-basic-test.cpp @@ -36,6 +36,8 @@ using util::isnil; using util::contains; using util::isSameObject; +using lib::hash::LuidH; +using lib::time::FSecs; using lib::time::Time; using lib::time::TimeSpan; //using std::string; diff --git a/tests/library/diff/generic-tree-mutator-test.cpp b/tests/library/diff/generic-tree-mutator-test.cpp index e06406196..f6b10803e 100644 --- a/tests/library/diff/generic-tree-mutator-test.cpp +++ b/tests/library/diff/generic-tree-mutator-test.cpp @@ -79,9 +79,7 @@ namespace test{ run (Arg) { simpleAttributeBinding(); - verifySnapshot(); sequenceIteration(); - duplicateDetection(); copy_and_move(); } @@ -113,24 +111,12 @@ namespace test{ } - void - verifySnapshot() - { - } - - void sequenceIteration() { } - void - duplicateDetection() - { - } - - void copy_and_move() { diff --git a/wiki/renderengine.html b/wiki/renderengine.html index a99b9c12f..c9d87bc26 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -2316,13 +2316,17 @@ For this Lumiera design, we could consider making GOP just another raw media dat &rarr;see in [[Wikipedia|http://en.wikipedia.org/wiki/Group_of_pictures]]
-
+
//Abstract generic node element to build a ~DOM-like rendering of Lumiera's [[session model|HighLevelModel]].//
 GenNode elements are values, yet behave polymorphic. They are rather light-weight, have an well established identity and can be compared. They are //generic// insofar they encompass several heterogeneous ordering systems, which in themselves can not be subsumed into a single ordering hierarchy. The //purpose// of these generic nodes is to build a symbolic representation, known as [[external tree description|ExternalTreeDescription]], existing somewhere "outside", at a level where the fine points of ordering system relations do not really matter. Largely, this external description is not represented or layed down as a whole. Rather, it is used as a conceptual reference frame to describe //differences and changes.// Obviously, this means that parts of the altered structures have to appear in the description of the modifications. So, practically speaking, the prime purpose of GenNode elements is to appear as bits of information within a ''diff language'' to exchange such information of changes.
 
 To be more specific, within the actual model there are [[Placements|Placement]]. These refer to [[M-Objects|MObject]]. Which in turn rely on [[Assets|Asset]]. Moreover, we have some processing rules, and -- last but not least -- the "objects" encountered in the model have state, visible as attributes of atomic value type (integral, floating point, string, boolean, time, time ranges and [[quantised time entities|TimeQuant]]).
 A generic node may //represent any of these kind// -- and it may have ~GenNode children, forming a tree. Effectively all of this together makes ~GenNode a ''Monad''.
 
+GenNode elements are conceived as values, and can thus be treated as mutable or immutable; it is up to the user to express this intent through const correctness.
+Especially the nested structures, i.e. a GenNode holding an embedded {{{diff::Record}}}, are by default immutable, but expose a object builder API for remoulding. This again places the actual decision about mutations into the usage context, since the remoulded Record has to be assigned explicitly.
+
+
 !to reflect or not reflect
 When dealing with this external model representation, indeed there are some rather global concerns which lend themselves to a generic programming style. Simply because, otherwise, we'd end up explicating and thereby duplicating the structure of the model all over the code. Frequently, such a situation is quoted as the reason to demand introspection facilities on any data structure. We doubt this is a valid conclusion. Since introspection allows to accept just //any element// -- followed by an open-ended //reaction on the received type// -- we might arrive at the impression that our code reaches a maximum of flexibility and "openness". Unfortunately, this turns out to be a self-deception, since code to do any meaningful operation needs pre-established knowledge about the meaning of the data to be processed. More so, when, as in any hierarchical data organisation, the relevant meaning is attached to the structure itself, so consequently this pre-established knowledge tends to be scattered over several, superficially "open" handler functions. What looks open and flexible at first sight is in fact littered with obscure and scattered, non obvious additional presumptions.
 This observation from coding practice gets us to the conclusion, that we do not really want to support the full notion of data and type introspection. We //do want// some kind of passive matching on structure, where the receiver explicitly has to supply structural presuppositions. In a fully functional language with a correspondingly rich type system, a partial function (pattern match) would be the solution of choice. Under the given circumstances, we're able to emulate this pattern based on our variant visitor -- which basically calls a different virtual function for each of the types possibly to be encountered "within" a ~GenNode.
@@ -7943,7 +7947,7 @@ Used this way, diff representation helps to separate structure and raw data in e :Chunks of raw data are attached inline to the structural diff, assuming that each element implicitly knows the kind of data to expect
-
+
//This page details decisions taken for implementation of Lumiera's diff handling framework//
 This topic is rather abstract, since diff handling is multi purpose within Lumiera: Diff representation is seen as a meta language and abstraction mechanism; it enables tight collaboration without the need to tie and tangle the involved implementation data structures. Used this way, diff representation reduces coupling and helps to cut down overall complexity -- so to justify the considerable amount of complexity seen within the diff framework implementation.
 
@@ -7999,13 +8003,15 @@ It should be noted, that the purpose of this whole architecture is to deal with
 {{red{open questions 6/15}}}
 * do we need to //alter// object contents -- or do we just replace?
 * to what degree is the distinction between attributes and children even relevant -- beyond the ability to address attributes by-name?
-* how do we describe an object from scratch?
+* how do we describe an object from scratch? &larr;''object builder''
 * how do we represent the break between attributes and children in this linearised description?
 ** using a separator element?
-** by convention through the element names?
+** by convention through the element names? &larr; ''This''
 ** as additional metadata information sent beforehand?
 * we need an object-reference element, since we do not want to copy whole subtrees while processing a diff
 
+"Objects" can be spelled out literally in code. We care to make the respective ctor syntax expressive enough. For nested objects, i.e. values of type{{{diff::Record}}}, a dedicated object builder notation is provided, (because this is the point, where the syntax gets convoluted
+
 Within this framework, we represent //object-like// entities through a special flavour of the GenNode: Basically, an object is a flat collection of children, yet given in accordance to a distinct protocol. The relevant ''meta'' information is spelled out first, followed by the ''attributes'' and finally the ''children''. The distinction between these lies in the mode of handling. Meta information is something we need to know before we're able to deal with the actual stuff. Prominent example is the type of the object. Attributes are considered unordered, and will typically be addressed by-name. Children are an ordered collection of recursive instances of the same data structure. (Incidentally, we do not rule out the possibility that also an attribute holds a recursive subtree; only the mode of access is what makes the distinction).
 
 !!!handling of actual mutation
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index 0714b3e3a..8e782dbcd 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -29,23 +29,86 @@
 
 
 
-
-
+
+
 
 
 
-
+
+
+
 
 
 
 
 
-
-
+
+
+
+
+
 
 
 
-
+
+
+
+
+
+
+
+
+  
+    
+  
+  
+    

+ d.h. der usage context entscheidet, ob wir einen Wert, +

+

+ eine Referenz oder einen konstanten Wert verwenden +

+ + +
+ + + + + + + + + + + +

+ Record selber ist immuable +

+

+ aber hat eine Builder-Mechanik +

+ + +
+
+
+
+ + + + + + +

+ eigentlich fehlte nur die get()-Operation +

+ + +
+ +
+
@@ -73,12 +136,47 @@ + + + + + + +

+ generische Lösung verschoben #963 +

+ + +
+ + +
+ + + + + + +

+ C++11 erlaubt =default +

+ + +
+ +
- + + + + + + +
@@ -152,8 +250,8 @@ - - + + From d92878876a94babbd3ae90696d9af2cf0bb72a92 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 4 Jul 2015 03:39:53 +0200 Subject: [PATCH 36/47] WIP: attempt to define the object builder invocation chain TODO still unresolved issues with the bootstrap. Looks like we shall not initiate from the basic Rec(), but reather require an explicit construction. --- src/lib/diff/gen-node.hpp | 33 +++++++++++++++ src/lib/diff/record.hpp | 84 ++++++++++++++++++++++++++++++++++----- wiki/thinkPad.ichthyo.mm | 72 ++++++++++++++++++++++++++++++++- 3 files changed, 179 insertions(+), 10 deletions(-) diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index 594f0881d..fece1c643 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -263,6 +263,39 @@ namespace diff{ + /* === Specialisation to add fluent GenNode builder API to Record === */ + + template<> + inline GenNode&& + Rec::Mutator::genNode() + { + UNIMPLEMENTED("wrap newly built Record into a new GenNode instance"); + } + + template<> + inline GenNode&& + Rec::Mutator::genNode(string const& symbolicID) + { + UNIMPLEMENTED("wrap newly built Record into a new named GenNode instance"); + } + + template<> + template + inline Rec::Mutator& + Rec::Mutator::attrib (ARGS&& ...args) + { + UNIMPLEMENTED("split sequence of arguments into key-value pairs and use these to populate the attributes collection"); + } + + template<> + template + inline Rec::Mutator& + Rec::Mutator::scope (ARGS&& ...args) + { + UNIMPLEMENTED("split sequence of arguments and build GenNode instances from them, to populate scope collection"); + } + + /* === Specialisation for handling of attributes in Record === */ template<> diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp index 5a4ab6a3b..a33d36b83 100644 --- a/src/lib/diff/record.hpp +++ b/src/lib/diff/record.hpp @@ -131,7 +131,6 @@ namespace diff{ class Record { using _Vec = std::vector; - using Attrib = std::pair; using Attribs = _Vec; using Children = _Vec; @@ -247,6 +246,44 @@ namespace diff{ + /* ==== extension point for fluent builder API ====== */ + + // to initiate and open builder chain: + Mutator + type (string const& typeID) + { + return Mutator(*this).type(typeID); + } + + template + Mutator + attrib (ARGS&& ...args) + { + return Mutator(*this).attrib(std::forward(args)...); + } + + template + Mutator + scope (ARGS&& ...args) + { + return Mutator(*this).scope(std::forward(args)...); + } + + // to close and finish builder chain (needs specialisation) + VAL&& + genNode() + { + return Mutator(*this).genNode(); + } + + VAL&& + genNode(string const& symbolicID) + { + return Mutator(*this).genNode(symbolicID); + } + + + /* ==== Exposing scope and contents for iteration ====== */ using iterator = IterAdapter; @@ -354,6 +391,12 @@ namespace diff{ std::swap (existingInstance, record_); } + bool + empty() const + { + return record_.empty(); + } + /* === functions to alter contents === */ @@ -364,29 +407,52 @@ namespace diff{ record_.type_ = newTypeID; } - void + Mutator& + type (string const& typeID) + { + setType (typeID); + return *this; + } + + Mutator& set (string const& key, VAL const& newValue) { ///////////TODO; + return *this; } - void + Mutator& appendChild (VAL const& newChild) { record_.children_.push_back (newChild); + return *this; } - void + Mutator& prependChild (VAL const& newChild) { record_.children_.insert (record_.children_.begin(), newChild); + return *this; } - bool - empty() const - { - return record_.empty(); - } + + /* === extension point for building specific value types === */ + /* + * the following builder functions are to be specialised + * to create a Record holding specific value types, + * especially for building a tree like structure + * with GenNode holding a Record + */ + + VAL&& genNode(); + VAL&& genNode(string const& symbolicID); + + template + Mutator& attrib (ARGS&& ...); + + template + Mutator& scope (ARGS&& ...); + }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 8e782dbcd..517c951c3 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -196,8 +196,78 @@ + + + + + + +

+ Object builder +

+ + +
+ + + + + + + +

+ Problem ist, wir definieren den Typ Record generisch, +

+

+ verwenden dann aber nur die spezialisierung Record<GenNode> +

+

+ Und die Builder-Funktionen brauchen eigentlich spezielles Wissen über den zu konstruierenden Zieltyp +

+ + +
+
- + + + + + + +

+ Ergebnis move +

+

+ pro / contra +

+ + +
+ + + + + +

+ Move ist gefährlich +

+

+ aber auch deutlich effizienter, +

+

+ denn wir müssen sonst das ganze erzeugte Ergebnis einmal kopieren. +

+

+ Nicht sicher, ob der Optimiser das hinbekommt +

+ + +
+
+
+
+ From d14c502ea9b5e7d4b72b5972244843c1eb1dbb78 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 4 Jul 2015 14:55:36 +0200 Subject: [PATCH 37/47] WIP: decision about the builder sequence after sleeping a night over this, it seems obvios that we do not want to start the build proces "implicitly", starting from a Record. Rather, we always want the user to plant a dedicated Mutator object, which then can remain noncopyable and is passed by reference through the whole builder chain. Movin innards of *this object* are moved away a the end of the chain does not pose much risk. --- src/lib/diff/gen-node.hpp | 14 ++++++++ src/lib/diff/record.hpp | 42 +++------------------- tests/library/diff/gen-node-basic-test.cpp | 34 +++++++++--------- 3 files changed, 35 insertions(+), 55 deletions(-) diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index fece1c643..8daca99eb 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -123,6 +123,7 @@ namespace diff{ class GenNode; using Rec = Record; + using MakeRec = Rec::Mutator; using DataValues = meta::Types - Mutator - attrib (ARGS&& ...args) - { - return Mutator(*this).attrib(std::forward(args)...); - } - - template - Mutator - scope (ARGS&& ...args) - { - return Mutator(*this).scope(std::forward(args)...); - } - - // to close and finish builder chain (needs specialisation) - VAL&& - genNode() - { - return Mutator(*this).genNode(); - } - - VAL&& - genNode(string const& symbolicID) - { - return Mutator(*this).genNode(symbolicID); - } - - - /* ==== Exposing scope and contents for iteration ====== */ using iterator = IterAdapter; @@ -370,6 +332,10 @@ namespace diff{ Rec record_; public: + Mutator() + : record_() + { } + explicit Mutator (Rec const& startingPoint) : record_(startingPoint) diff --git a/tests/library/diff/gen-node-basic-test.cpp b/tests/library/diff/gen-node-basic-test.cpp index cd561579b..8a4127e0d 100644 --- a/tests/library/diff/gen-node-basic-test.cpp +++ b/tests/library/diff/gen-node-basic-test.cpp @@ -149,10 +149,10 @@ namespace test{ void objectShortcut() { - auto o0 = Rec().genNode(); - auto o1 = Rec().genNode("νόμος"); - auto o2 = Rec().type("spam").genNode(); - auto o3 = Rec().attrib("Ψ", int64_t(42), "π", 3.14159265358979323846264338328).genNode("νόμος"); + auto o0 = MakeRec().genNode(); + auto o1 = MakeRec().genNode("νόμος"); + auto o2 = MakeRec().type("spam").genNode(); + auto o3 = MakeRec().attrib("Ψ", int64_t(42), "π", 3.14159265358979323846264338328).genNode("λόγος"); CHECK (!o0.isNamed()); CHECK (isnil(o0.data.get())); @@ -167,22 +167,22 @@ namespace test{ CHECK (isnil(o2.data.get())); CHECK (o3.isNamed()); - CHECK ("νόμος" == o3.idi.getSym()); + CHECK ("λόγος" == o3.idi.getSym()); CHECK ("NIL" == o3.data.get().getType()); CHECK (GenNode("Ψ", int64_t(42)) == o3.data.get().get("Ψ")); - CHECK (42L == o3.data.get().get("Ψ").data.get); - CHECK (1e-7 > fabs (3.14159265 - o3.data.get().get("π").data.get)); + CHECK (42L == o3.data.get().get("Ψ").data.get()); + CHECK (1e-7 > fabs (3.14159265 - o3.data.get().get("π").data.get())); LuidH luid; //Demonstration: object builder is based on the mutator mechanism for Records... - auto o4 = Rec::Mutator(o2) // ...use GenNode o2 as starting point - .appendChild(GenNode("τ", Time(1,2,3,4))) // a named node with Time value - .scope('*' // a char node - ,"★" // a string node - ,luid // a hash value (LUID) - ,TimeSpan(Time::ZERO, FSecs(23,25)) // a time span - ,Rec().type("ham").scope("eggs").genNode()) // a spam object - .genNode("baked beans"); // ---> finish into named node + auto o4 = Rec::Mutator(o2.data.get()) // ...use GenNode o2 as starting point + .appendChild(GenNode("τ", Time(1,2,3,4))) // a named node with Time value + .scope('*' // a char node + ,"★" // a string node + ,luid // a hash value (LUID) + ,TimeSpan(Time::ZERO, FSecs(23,25)) // a time span + ,MakeRec().type("ham").scope("eggs").genNode()) // a spam object + .genNode("baked beans"); // ---> finish into named node CHECK (o4.isNamed()); CHECK ("baked beans" == o4.idi.getSym()); @@ -202,7 +202,7 @@ namespace test{ ++scope; auto spam = *scope; CHECK (!++scope); - CHECK ("ham" == spam.getType()); + CHECK ("ham" == spam.data.get().getType()); CHECK (spam.contains (GenNode("eggs"))); // but while o4 was based on o2, @@ -215,7 +215,7 @@ namespace test{ void symbolReference() { - GenNode ham = Rec().type("spam").attrib("τ", Time(23,42)).genNode("egg bacon sausage and spam"); + GenNode ham = MakeRec().type("spam").attrib("τ", Time(23,42)).genNode("egg bacon sausage and spam"); GenNode::ID hamID(ham); CHECK (hamID == ham.idi); From ee6d044e33dbef1a1a5784109971d3dc14f2b7f7 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 4 Jul 2015 15:22:04 +0200 Subject: [PATCH 38/47] WIP: implement the node builder API --- src/lib/diff/gen-node.hpp | 24 ++++-------------------- src/lib/diff/record.hpp | 31 +++++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index 8daca99eb..6fd239cad 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -281,32 +281,16 @@ namespace diff{ template<> inline GenNode&& - Rec::Mutator::genNode() + MakeRec::genNode() { - UNIMPLEMENTED("wrap newly built Record into a new GenNode instance"); + return std::move (GenNode(std::move(record_))); } template<> inline GenNode&& - Rec::Mutator::genNode(string const& symbolicID) + MakeRec::genNode(string const& symbolicID) { - UNIMPLEMENTED("wrap newly built Record into a new named GenNode instance"); - } - - template<> - template - inline Rec::Mutator& - Rec::Mutator::attrib (ARGS&& ...args) - { - UNIMPLEMENTED("split sequence of arguments into key-value pairs and use these to populate the attributes collection"); - } - - template<> - template - inline Rec::Mutator& - Rec::Mutator::scope (ARGS&& ...args) - { - UNIMPLEMENTED("split sequence of arguments and build GenNode instances from them, to populate scope collection"); + return std::move (GenNode(symbolicID, std::move(record_))); } diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp index b9faa6e47..2e1fe7714 100644 --- a/src/lib/diff/record.hpp +++ b/src/lib/diff/record.hpp @@ -413,11 +413,34 @@ namespace diff{ VAL&& genNode(); VAL&& genNode(string const& symbolicID); - template - Mutator& attrib (ARGS&& ...); + template + Mutator& attrib (string const& key, X const& initialiser, ARGS&& ...args) + { + set (key, VAL(initialiser)); + return attrib (std::forward(args)...); + } + + template + Mutator& + attrib (string const& key, X const& initialiser) + { + set (key, VAL(initialiser)); + return *this; + } - template - Mutator& scope (ARGS&& ...); + template + Mutator& scope (X const& initialiser, ARGS&& ...args) + { + appendChild (VAL(initialiser)); + return scope (std::forward(args)...); + } + + template + Mutator& scope (X const& initialiser) + { + appendChild (VAL(initialiser)); + return *this; + } }; From 5b0d58518e2f773f7d4d4672317b33d94548c0ce Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 4 Jul 2015 20:50:18 +0200 Subject: [PATCH 39/47] WIP: stub GenNode ref --- src/lib/diff/diff.cpp | 10 +++++ src/lib/diff/gen-node.hpp | 50 ++++++++++++++++++++++ src/lib/diff/record.hpp | 6 +++ tests/library/diff/gen-node-basic-test.cpp | 16 +++---- wiki/renderengine.html | 4 +- wiki/thinkPad.ichthyo.mm | 48 +++++++++++++++++++-- 6 files changed, 120 insertions(+), 14 deletions(-) diff --git a/src/lib/diff/diff.cpp b/src/lib/diff/diff.cpp index 7bc2d54fb..eadc4d8b0 100644 --- a/src/lib/diff/diff.cpp +++ b/src/lib/diff/diff.cpp @@ -33,6 +33,7 @@ #include "lib/error.hpp" #include "lib/diff/diff-language.hpp" +#include "lib/diff/gen-node.hpp" namespace lib { @@ -40,5 +41,14 @@ namespace diff{ LUMIERA_ERROR_DEFINE(DIFF_CONFLICT, "Collision in diff application: contents of target not as expected."); + /* symbolic marker ID references + * used the tree diff language + * to mark specific scopes + */ + Ref Ref::END ("_END_"); + Ref Ref::THIS ("_THIS_"); + Ref Ref::CHILD ("_CHILD_"); + Ref Ref::ATTRIBS("_ATTRIBS_"); + }} // namespace lib::diff diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index 6fd239cad..cf395d6ac 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -123,6 +123,7 @@ namespace diff{ class GenNode; using Rec = Record; + using RecRef = RecordRef; using MakeRec = Rec::Mutator; using DataValues = meta::Types; @@ -265,6 +267,21 @@ namespace diff{ return n1.idi != n2.idi; } + + protected: + /** @internal for dedicated builder subclasses */ + GenNode (ID&& id, DataCap&& d) + : idi(std::move(id)) + , data(std::move(d)) + { } + + template + static GenNode::ID + fabricateRefID (string const& symbolicID) + { + X* typeID; + return ID(typeID, symbolicID); + } private: template @@ -276,6 +293,39 @@ namespace diff{ }; + /** + * Constructor for a specially crafted 'ref GenNode'. + * The identity record of the generated object will be prepared + * such as to be identical to a regular GenNode with Record payload. + * @note slicing in usage is intentional + */ + struct Ref + : GenNode + { + /** create an empty ID stand-in. + * @note the purpose is to create a symbolic reference by name + */ + explicit + Ref(string const& symbolicID) + : GenNode(fabricateRefID (symbolicID) + , DataCap(RecRef())) // note: places NIL into the reference part + { } + + /** build reference to a Record, using the original ID + * @throw error::Logic when oNode does not hold a Record + */ + Ref(GenNode& oNode) + : GenNode(ID(oNode) + , DataCap(RecRef(oNode.data.get()))) + { } + + static Ref END; ///< symbolic ID ref "_END_" + static Ref THIS; ///< symbolic ID ref "_THIS_" + static Ref CHILD; ///< symbolic ID ref "_CHILD_" + static Ref ATTRIBS; ///< symbolic ID ref "_ATTRIBS_" + }; + + /* === Specialisation to add fluent GenNode builder API to Record === */ diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp index 2e1fe7714..07471ac63 100644 --- a/src/lib/diff/record.hpp +++ b/src/lib/diff/record.hpp @@ -485,6 +485,12 @@ namespace diff{ explicit operator bool() const + { + return empty(); + } + + bool + empty() const { return bool(record_); } diff --git a/tests/library/diff/gen-node-basic-test.cpp b/tests/library/diff/gen-node-basic-test.cpp index 8a4127e0d..cac1344a4 100644 --- a/tests/library/diff/gen-node-basic-test.cpp +++ b/tests/library/diff/gen-node-basic-test.cpp @@ -223,8 +223,8 @@ namespace test{ CHECK (hamID.getHash() == ham.idi.getHash()); CHECK (contains (string(hamID), "spam")); // Lovely spam! - GenNode ref1 = Ref("egg bacon sausage and spam"); - GenNode ref2 = Ref(ham); + Ref ref1("egg bacon sausage and spam"); + Ref ref2(ham); CHECK (ref1.idi == ham.idi); CHECK (ref2.idi == ham.idi); @@ -233,8 +233,8 @@ namespace test{ CHECK (isSameObject (ham, ref2.data.get())); VERIFY_ERROR (BOTTOM_VALUE, ref1.data.get()); - RecordRef rr1 = ref1.data.get(); - RecordRef rr2 = ref2.data.get(); + RecRef rr1 = ref1.data.get(); + RecRef rr2 = ref2.data.get(); CHECK ( isnil(rr1)); CHECK (!isnil(rr2)); @@ -248,10 +248,10 @@ namespace test{ CHECK ("_CHILD_" == name(Ref::CHILD)); CHECK ("_ATTRIBS_" == name(Ref::ATTRIBS)); - CHECK (isnil (Ref::END.data.get())); - CHECK (isnil (Ref::THIS.data.get())); - CHECK (isnil (Ref::CHILD.data.get())); - CHECK (isnil (Ref::ATTRIBS.data.get())); + CHECK (isnil (Ref::END.data.get())); + CHECK (isnil (Ref::THIS.data.get())); + CHECK (isnil (Ref::CHILD.data.get())); + CHECK (isnil (Ref::ATTRIBS.data.get())); } diff --git a/wiki/renderengine.html b/wiki/renderengine.html index c9d87bc26..65a3b8f0f 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -7947,7 +7947,7 @@ Used this way, diff representation helps to separate structure and raw data in e :Chunks of raw data are attached inline to the structural diff, assuming that each element implicitly knows the kind of data to expect
-
+
//This page details decisions taken for implementation of Lumiera's diff handling framework//
 This topic is rather abstract, since diff handling is multi purpose within Lumiera: Diff representation is seen as a meta language and abstraction mechanism; it enables tight collaboration without the need to tie and tangle the involved implementation data structures. Used this way, diff representation reduces coupling and helps to cut down overall complexity -- so to justify the considerable amount of complexity seen within the diff framework implementation.
 
@@ -8010,7 +8010,7 @@ It should be noted, that the purpose of this whole architecture is to deal with
 ** as additional metadata information sent beforehand?
 * we need an object-reference element, since we do not want to copy whole subtrees while processing a diff
 
-"Objects" can be spelled out literally in code. We care to make the respective ctor syntax expressive enough. For nested objects, i.e. values of type{{{diff::Record}}}, a dedicated object builder notation is provided, (because this is the point, where the syntax gets convoluted
+"Objects" can be spelled out literally in code. We care to make the respective ctor syntax expressive enough. For nested objects, i.e. values of type {{{diff::Record}}}, a dedicated object builder notation is provided, because this is the point, where the syntax gets convoluted
 
 Within this framework, we represent //object-like// entities through a special flavour of the GenNode: Basically, an object is a flat collection of children, yet given in accordance to a distinct protocol. The relevant ''meta'' information is spelled out first, followed by the ''attributes'' and finally the ''children''. The distinction between these lies in the mode of handling. Meta information is something we need to know before we're able to deal with the actual stuff. Prominent example is the type of the object. Attributes are considered unordered, and will typically be addressed by-name. Children are an ordered collection of recursive instances of the same data structure. (Incidentally, we do not rule out the possibility that also an attribute holds a recursive subtree; only the mode of access is what makes the distinction).
 
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index 517c951c3..3e1c1ae36 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -325,8 +325,8 @@
 
 
 
-
-
+
+
 
 
   
@@ -345,8 +345,10 @@
 
 
 
-
-
+
+
+
+
 
   
     
@@ -357,6 +359,41 @@
     

+ +
+ + + + + + + + + + + + +

+ gemeint ist: +

+
    +
  • + man kann alternativ auch eine RecordRef direkt in eine elementare GenNode packen +
  • +
  • + diese verhält sich dann nicht transparent, denn sie hat eine andere Identität als ihr Ziel +
  • +
  • + das kann aber als spezielles Ausdrucksmittel genutzt werden +
  • +
+ + +
+
+ + + @@ -473,6 +510,9 @@
+ + +
From 7f51a01631c5ae8c37a232c1210276002cf5f99a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 4 Jul 2015 21:09:44 +0200 Subject: [PATCH 40/47] clean-up some library and linkage problems the object VTable is typically emitted when the compiler encounters the first non-static non-inline function of the class or a derived class. Sometimes this happens within the wrong library and so the compiler needs a nudge to emit those infrastructure functions. But in most cases this works out of the box and need no further magic incanctations, which might have a downside. Especially because also a non-inline dtor does incur a call overhead, whereas an inline dtor can be trivially elided. --- src/lib/time/timecode.cpp | 3 --- src/lib/time/timecode.hpp | 2 +- src/proc/engine/engine-service.cpp | 2 -- src/proc/engine/engine-service.hpp | 2 +- tests/{library => core/proc/asset}/entry-id-test.cpp | 0 5 files changed, 2 insertions(+), 7 deletions(-) rename tests/{library => core/proc/asset}/entry-id-test.cpp (100%) diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 0a7a629e5..27ffc0eae 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -48,9 +48,6 @@ namespace lib { namespace time { - TCode::~TCode() { } // emit VTable here.... - - namespace format { /* ================= Timecode implementation details ======== */ LUMIERA_ERROR_DEFINE (INVALID_TIMECODE, "timecode format error, illegal value encountered"); diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index f81df7c7f..8c93aa966 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -52,7 +52,7 @@ namespace time { { public: - virtual ~TCode(); + virtual ~TCode() { } operator string() const { return show(); } string describe() const { return string(tcID()); } diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp index 45e7a24e6..c936e4904 100644 --- a/src/proc/engine/engine-service.cpp +++ b/src/proc/engine/engine-service.cpp @@ -181,8 +181,6 @@ namespace engine{ /* ===== Quality-of-Service ===== */ - EngineService::Quality::~Quality() { } // emit vtables here... - enum CalcType { PLAYBACK, RENDER, diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp index bc98a6a28..2d7a40707 100644 --- a/src/proc/engine/engine-service.hpp +++ b/src/proc/engine/engine-service.hpp @@ -121,7 +121,7 @@ namespace engine{ class Quality { public: - virtual ~Quality(); ///< this is an Interface + virtual ~Quality() { }; ///< this is an Interface }; diff --git a/tests/library/entry-id-test.cpp b/tests/core/proc/asset/entry-id-test.cpp similarity index 100% rename from tests/library/entry-id-test.cpp rename to tests/core/proc/asset/entry-id-test.cpp From bfb7bbd2f5fa8aa09d2b22e26ced9f94d5520126 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 5 Jul 2015 03:17:39 +0200 Subject: [PATCH 41/47] implement Record: operator string() for diagnostics --- src/lib/diff/gen-node.hpp | 11 +- src/lib/diff/record.hpp | 48 ++++++-- .../generic-record-representation-test.cpp | 1 + wiki/thinkPad.ichthyo.mm | 112 ++++++++++++++---- 4 files changed, 141 insertions(+), 31 deletions(-) diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index cf395d6ac..f902b9895 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -237,9 +237,9 @@ namespace diff{ } bool - contains (GenNode const&) const + contains (GenNode const& elm) const { - UNIMPLEMENTED("containment check"); + return contains (elm.idi); } bool @@ -388,6 +388,13 @@ namespace diff{ return GenNode(v); ///TODO } + template<> + inline string + Rec::renderAttribute (GenNode const& a) + { + return "notyet = todo"; ////TODO + } + }} // namespace lib::diff #endif /*LIB_DIFF_GEN_NODE_H*/ diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp index 07471ac63..44c48ac7f 100644 --- a/src/lib/diff/record.hpp +++ b/src/lib/diff/record.hpp @@ -88,8 +88,8 @@ #include "lib/iter-adapter.hpp" #include "lib/iter-adapter-stl.hpp" #include "lib/itertools.hpp" +#include "lib/util.hpp" -//#include "lib/util.hpp" //#include "lib/format-string.hpp" #include @@ -122,10 +122,10 @@ namespace diff{ * syntax. Yet the most relevant use case is \c Record -- using the * embedded name-ID of the GenNode elements as key for attributes. * - * Recode elements meant to be immutable; they can be created from a + * Record elements are meant to be immutable; they can be created from a * defining collection. However, we provide a #Mutator mechanism to allow * for rebuilding and mutating symbolic data structures based on Records - * and GenNode. Especially, Lumiera's diff framework relies on this. + * and GenNode. Essentially, Lumiera's diff framework relies on this. */ template class Record @@ -180,10 +180,8 @@ namespace diff{ // all default copy operations acceptable - operator std::string() const - { - return "nebbich"; ////TODO - } + /** for diagnostic purpose, include format-util.hpp */ + operator std::string() const; bool @@ -206,9 +204,9 @@ namespace diff{ } bool - contains (VAL const& ref) const + contains (VAL const& val) const { - return false; ////TODO + return util::contains (children_, val); } VAL const& @@ -304,6 +302,7 @@ namespace diff{ static bool isTypeID (VAL const& v); static string extractTypeID (VAL const& v); static VAL buildTypeAttribute (string const& typeID); + static string renderAttribute (VAL const& a); static string extractKey (VAL const& v); static VAL extractVal (VAL const& v); @@ -561,6 +560,37 @@ namespace diff{ return string(); ///TODO } + template<> + inline string + Record::renderAttribute (string const& attrib) + { + return extractKey(attrib) + " = " + string(extractVal(attrib)); + } + + + + + + /* === Diagnostics === */ + + template + Record::operator std::string() const + { +#ifndef LIB_FORMAT_UTIL_H + return "Record(...)"; +#else + using util::join; + using lib::transformIterator; + + return "Rec(" + + join (transformIterator (this->attribs(), renderAttribute)) + + "|{" + + join (this->scope()) + + "})" + ; +#endif + } + }} // namespace lib::diff diff --git a/tests/library/diff/generic-record-representation-test.cpp b/tests/library/diff/generic-record-representation-test.cpp index d6603335c..1e0f83509 100644 --- a/tests/library/diff/generic-record-representation-test.cpp +++ b/tests/library/diff/generic-record-representation-test.cpp @@ -23,6 +23,7 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" +#include "lib/format-util.hpp" #include "lib/diff/record.hpp" #include "lib/itertools.hpp" #include "lib/util.hpp" //////TODO necessary? diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 3e1c1ae36..d2fe9bbb6 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -52,7 +52,7 @@ - + @@ -112,8 +112,23 @@ - + + + + + + +

+ erledigt... ähm vertagt +

+

+ aber nicht wirklich; der workaround könnte schon die Lösung sein #963 +

+ + +
+ @@ -178,8 +193,10 @@ - - + + + + @@ -196,7 +213,7 @@ - + @@ -208,7 +225,6 @@ - @@ -228,6 +244,22 @@ + + + + + + + + + +

+ Mutator selber is noncopyable +

+ + +
+
@@ -264,20 +296,53 @@ + + + + + + +

+ nur auf dem Mutator +

+ + +
+
+ + + + + + +

+ dieser ist nicht kopierbar +

+

+ und muß dediziert erstellt werden +

+ + +
+
+
- - - + + + + + + - + - + @@ -287,7 +352,8 @@ scheidet aus, wegen Wertsemantik

-
+ +
@@ -327,7 +393,7 @@ - + @@ -337,10 +403,11 @@ heißt: in der Diff-Verarbeitung wird dieser spezielle check verwendet

-
+ + - + @@ -348,7 +415,7 @@ - + @@ -358,10 +425,11 @@ m.E. die einzig saubere Desgin-Variante!

-
+ +
- + @@ -397,7 +465,7 @@ - + @@ -407,7 +475,8 @@ heißt: wird direkt von standard-equality so behandelt

-
+ + @@ -513,6 +582,9 @@ + + +
From 00dc968d7b0c2582e63f614b304b1d58cc408d35 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 8 Jul 2015 04:12:10 +0200 Subject: [PATCH 42/47] implement generic attribute access in Record type --- src/lib/diff/gen-node.hpp | 2 +- src/lib/diff/record.hpp | 42 +++++++++++++++++++++++++++++---------- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index f902b9895..4b49ca4ae 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -279,7 +279,7 @@ namespace diff{ static GenNode::ID fabricateRefID (string const& symbolicID) { - X* typeID; + X* typeID(0); return ID(typeID, symbolicID); } diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp index 44c48ac7f..8e4ed78b6 100644 --- a/src/lib/diff/record.hpp +++ b/src/lib/diff/record.hpp @@ -93,6 +93,7 @@ //#include "lib/format-string.hpp" #include +#include #include #include #include @@ -133,6 +134,7 @@ namespace diff{ using _Vec = std::vector; using Attribs = _Vec; using Children = _Vec; + using ElmIter = typename _Vec::const_iterator; string type_; Attribs attribs_; @@ -180,7 +182,7 @@ namespace diff{ // all default copy operations acceptable - /** for diagnostic purpose, include format-util.hpp */ + /** for diagnostic purpose: include format-util.hpp */ operator std::string() const; @@ -200,7 +202,7 @@ namespace diff{ bool hasAttribute (string key) const { - return false; ////TODO + return attribs_.end() != findKey(key); } bool @@ -212,7 +214,11 @@ namespace diff{ VAL const& get (string key) const { - return "booo"; ////TODO + ElmIter found = findKey (key); + if (attribs_.end() == found) + throw error::Invalid ("Record has no attribute \""+key+"\""); + else + return *found; } /** @@ -282,14 +288,14 @@ namespace diff{ checkPoint (const Record* src, ITER& pos) { REQUIRE (src); - if ((pos != ITER()) && (pos != src->children_.end())) - return true; + if ((pos != ITER()) && (pos == src->attribs_.end()) && !src->children_.empty()) + { + pos = src->children_.begin(); + return true; + } else - if (pos != ITER() && (pos == src->attribs_.end()) && !src->children_.empty()) - { - pos = src->children_.begin(); - return true; - } + if (pos != ITER() && (pos != src->children_.end())) + return true; else { pos = ITER(); @@ -307,10 +313,24 @@ namespace diff{ static VAL extractVal (VAL const& v); + ElmIter + findKey (string key) const + { + return std::find_if (attribs_.begin() + ,attribs_.end() + ,[=](VAL const& elm) + { + return key == extractKey(elm); + }); + } + + friend bool operator== (Record const& r1, Record const& r2) { - return false; ////TODO + return r1.type_ == r2.type_ + && r1.attribs_ == r2.attribs_ + && r1.children_ == r2.children_; } friend bool From f565ae4639a97a3a7e7ae83fa840f068902a6b25 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 15 Jul 2015 03:27:10 +0200 Subject: [PATCH 43/47] weird warning turns out to be GCC 4.7.2 Bug 56402 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56402 The lambda definition captures the this pointer, but the ctor of the lamda does not initialise this capture. In our case, we're lucky, as we don't use the "this" pointer; otherwise, we'd get a crash a runtime. Fixed since GCC-4.7.3 --> it's *really* time to upgrade to Debian/Jessie --- src/lib/diff/record.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp index 8e4ed78b6..4cedb08c5 100644 --- a/src/lib/diff/record.hpp +++ b/src/lib/diff/record.hpp @@ -322,7 +322,7 @@ namespace diff{ { return key == extractKey(elm); }); - } + } ///////////////////TODO this lambda triggers a GCC-4.7.2 Bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56402 friend bool From 430107fcd8bf211f7875f64fd0935404ef6eab14 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 15 Jul 2015 04:16:22 +0200 Subject: [PATCH 44/47] draft impl of Record this specialisation of the Record template is provided as default implementation for simple unit tests --- src/lib/diff/record.hpp | 71 ++++++++++++++++-------------- wiki/thinkPad.ichthyo.mm | 93 +++++++++++++++++++++++----------------- 2 files changed, 94 insertions(+), 70 deletions(-) diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp index 4cedb08c5..7b83023b0 100644 --- a/src/lib/diff/record.hpp +++ b/src/lib/diff/record.hpp @@ -538,53 +538,62 @@ namespace diff{ /* === Specialisations to define the handling of attributes === */ - template<> - inline bool - Record::isAttribute (string const& v) - { - return false; ////TODO - } - - template<> - inline bool - Record::isTypeID (string const& v) - { - return false; ////TODO - } - - template<> - inline string - Record::extractTypeID (string const& v) - { - return "todo"; ////TODO - } - - template<> - inline string - Record::buildTypeAttribute (string const& typeID) - { - return string(); ///TODO - } - template<> inline string Record::extractKey (string const& v) { - return "todo"; ////TODO + size_t pos = v.find('='); + if (string::npos == pos) + return ""; + else + return v.substr(0,pos); //////////////////////////////TODO should trim here } template<> inline string Record::extractVal (string const& v) { - return string(); ///TODO + size_t pos = v.find('='); + if (string::npos == pos) + return v; + else + return v.substr(pos+1, v.length() - pos); ////////////TODO should trim here + } + + template<> + inline bool + Record::isAttribute (string const& v) + { + return string::npos != v.find('='); + } + + template<> + inline bool + Record::isTypeID (string const& v) + { + return isAttribute(v) + && "type" == extractKey(v); + } + + template<> + inline string + Record::extractTypeID (string const& v) + { + return "todo"; //////////////////////////////////////////TODO do we really need this function? + } + + template<> + inline string + Record::buildTypeAttribute (string const& typeID) + { + return "type = "+typeID; } template<> inline string Record::renderAttribute (string const& attrib) { - return extractKey(attrib) + " = " + string(extractVal(attrib)); + return extractKey(attrib) + " = " + extractVal(attrib); } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index d2fe9bbb6..603e1d74a 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -69,8 +69,7 @@ eine Referenz oder einen konstanten Wert verwenden

- - + @@ -89,8 +88,7 @@ aber hat eine Builder-Mechanik

- - +
@@ -104,8 +102,7 @@ eigentlich fehlte nur die get()-Operation

- - +
@@ -125,8 +122,7 @@ aber nicht wirklich; der workaround könnte schon die Lösung sein #963

- - + @@ -148,8 +144,7 @@ Und ich wollte keine Zeit auf einen exzessiven Unit-Test verwenden

- - +
@@ -161,8 +156,7 @@ generische Lösung verschoben #963

- -
+
@@ -176,8 +170,7 @@ C++11 erlaubt =default

- - +
@@ -223,8 +216,7 @@ Object builder

- - + @@ -241,8 +233,7 @@ Und die Builder-Funktionen brauchen eigentlich spezielles Wissen über den zu konstruierenden Zieltyp

- -
+ @@ -257,8 +248,7 @@ Mutator selber is noncopyable

- - +
@@ -274,8 +264,7 @@ pro / contra

- - + @@ -294,8 +283,7 @@ Nicht sicher, ob der Optimiser das hinbekommt

- -
+ @@ -306,8 +294,7 @@ nur auf dem Mutator

- -
+
@@ -322,16 +309,49 @@ und muß dediziert erstellt werden

- -
+
- + + + + + + + + + + +

+ möglicherweise schon gelöst, +

+

+ denn Record ist insgesamt immutable. +

+

+ Also können wir einen Find mit einem const_iterator machen +

+ + +
+
+ + + + + + + + + + + +
@@ -352,8 +372,7 @@ scheidet aus, wegen Wertsemantik

- - +
@@ -403,8 +422,7 @@ heißt: in der Diff-Verarbeitung wird dieser spezielle check verwendet

- - + @@ -425,8 +443,7 @@ m.E. die einzig saubere Desgin-Variante!

- - +
@@ -456,8 +473,7 @@ - - + @@ -475,8 +491,7 @@ heißt: wird direkt von standard-equality so behandelt

- - + From 266cce9abe6c95a01c33f82ff1074e2e78917d9f Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 15 Aug 2015 22:31:36 +0200 Subject: [PATCH 45/47] fix for compiling with CLang (3.5) --- src/lib/variant.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/variant.hpp b/src/lib/variant.hpp index 35a9d40e0..19f272b01 100644 --- a/src/lib/variant.hpp +++ b/src/lib/variant.hpp @@ -408,7 +408,7 @@ namespace lib { X const& get() const { - return unConst(this)->get(); + return unConst(this)->template get(); } void From 9ff79b86cf55d38a1b1b620e07e61288c2f012f0 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 16 Aug 2015 00:16:30 +0200 Subject: [PATCH 46/47] fix warnings found by CLang (3.5) Note: not fixing all relevant warnings. Especially, the "-Woverloaded-virtual" of Clang defeats the whole purpose of generated generic interfaces. For example, our Variant type is instantiated with a list of types the variant can hold. Through metaprogramming, this instantiation generates also an embedded Visitor interface, which has virtual 'handle(TY)' functions for all the types in question The client now may implement, or even partially implement this Visitor, to retrieve specific data out of given Variant instance with unknown conent. To complain that some other virtual overload is now shaddowed is besides the point, so we might consider to disable this warning altogether --- src/gui/widget/timeline-widget.hpp | 2 ++ src/gui/widget/timeline/timeline-body.hpp | 1 + src/lib/diff/gen-node.hpp | 4 ++-- src/lib/diff/tree-mutator.hpp | 2 +- src/lib/format-string.hpp | 2 +- src/lib/iter-adapter.hpp | 2 +- src/lib/meta/virtual-copy-support.hpp | 17 ++++++++++++++--- src/lib/variant.hpp | 8 ++++++++ src/proc/asset/procpatt.hpp | 2 +- src/proc/asset/struct-scheme.hpp | 2 +- .../mobject/builder/model-port-registry.hpp | 2 +- .../mobject/session/query/fake-configrules.hpp | 2 +- src/proc/streamtype.hpp | 4 ++++ tests/basics/streamtypebasicstest.cpp | 2 +- .../builder/model-port-registry-test.cpp | 1 + tests/library/diff/gen-node-basic-test.cpp | 2 +- 16 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/gui/widget/timeline-widget.hpp b/src/gui/widget/timeline-widget.hpp index 2eaae7a14..8e40f606d 100644 --- a/src/gui/widget/timeline-widget.hpp +++ b/src/gui/widget/timeline-widget.hpp @@ -23,6 +23,7 @@ /** @file timeline-widget.hpp ** This file defines the core component of the Lumiera GUI ** + ** @deprecated broken since transition to GTK-3 ** @todo needs to be reworked from ground as if 5/2015 ** GTK-3 uses different event handling callbacks, ** so the existing implementation is defunct. @@ -67,6 +68,7 @@ namespace widget { * Core timeline display (custom widget). * @remarks This widget is a composite of several widgets contained * within the timeline namespace. + * @deprecated dysfunctional and broken by switch to GTK-3. Needs to be rewritten */ class TimelineWidget : public Gtk::Table diff --git a/src/gui/widget/timeline/timeline-body.hpp b/src/gui/widget/timeline/timeline-body.hpp index d3dd39e43..f31380f0d 100644 --- a/src/gui/widget/timeline/timeline-body.hpp +++ b/src/gui/widget/timeline/timeline-body.hpp @@ -125,6 +125,7 @@ protected: /** * The event handler for when the TimelineWidget's state is switched. + * @deprecated needs to be rewritten from scratch for GTK-3 */ void on_state_changed (shared_ptr newState); diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index 4b49ca4ae..ffb589db8 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -120,7 +120,7 @@ namespace diff{ using std::string; - class GenNode; + struct GenNode; using Rec = Record; using RecRef = RecordRef; @@ -170,7 +170,7 @@ namespace diff{ class ID : public idi::BareEntryID { - friend class GenNode; + friend struct GenNode; template ID (X*, string const& symbolicID) diff --git a/src/lib/diff/tree-mutator.hpp b/src/lib/diff/tree-mutator.hpp index 2e3254f3d..25e44f57a 100644 --- a/src/lib/diff/tree-mutator.hpp +++ b/src/lib/diff/tree-mutator.hpp @@ -75,7 +75,7 @@ namespace diff{ namespace { template - class Builder; + struct Builder; using ID = Literal; using Attribute = DataCap; diff --git a/src/lib/format-string.hpp b/src/lib/format-string.hpp index fa884b174..173ebc8b8 100644 --- a/src/lib/format-string.hpp +++ b/src/lib/format-string.hpp @@ -117,7 +117,7 @@ namespace std { // forward declaration to avoid including template - class char_traits; + struct char_traits; template class basic_ostream; diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index 69d5407fd..ccf36f3d5 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -107,7 +107,7 @@ namespace lib { namespace { // internal helpers - void + inline void _throwIterExhausted() { throw lumiera::error::Invalid ("Can't iterate further", diff --git a/src/lib/meta/virtual-copy-support.hpp b/src/lib/meta/virtual-copy-support.hpp index 9e1cfee30..fee8effa4 100644 --- a/src/lib/meta/virtual-copy-support.hpp +++ b/src/lib/meta/virtual-copy-support.hpp @@ -34,16 +34,16 @@ ** a virtual function (which requires a VTable): Even if for everyone else any ** knowledge regarding the exact implementation type has been discarded ("erased"), ** the function pointers in the VTable still implicitly hold onto that precise - ** implementation type, since they were setup during construction, where the + ** implementation type, since they were set up during construction, where the ** type was still available. Such a scheme of dealing with "opaque" copy operations ** is known as virtual copy -- it can be dangerous and tricky to get right ** and is preferably used only in flat class hierarchies. ** ** This helper template simplifies the construction of such a scheme. ** - a base interface defines the available virtual copy operations - ** - a set of CRTP-style templates covers all the case of + ** - a set of CRTP-style templates covers all the cases of ** - full copy support - ** - copy construction but not assignment + ** - copy construction but no assignment ** - only move construction allowed ** - noncopyable type ** - we use type traits and a policy template to pick the correct implementation @@ -144,6 +144,8 @@ namespace meta{ : public BASE { public: + virtual ~VirtualCopySupportInterface() { } + virtual void copyInto (void* targetStorage) const =0; virtual void moveInto (void* targetStorage) =0; virtual void copyInto (IFA& target) const =0; @@ -198,6 +200,9 @@ namespace meta{ D& src = static_cast (*this); new(targetStorage) D(move(src)); } + + using I::copyInto; + using I::moveInto; }; @@ -211,6 +216,9 @@ namespace meta{ D const& src = static_cast (*this); new(targetStorage) D(src); } + + using I::copyInto; + using I::moveInto; }; @@ -233,6 +241,9 @@ namespace meta{ D& s = static_cast (*this); t = move(s); } + + using I::copyInto; + using I::moveInto; }; diff --git a/src/lib/variant.hpp b/src/lib/variant.hpp index 19f272b01..9902e1746 100644 --- a/src/lib/variant.hpp +++ b/src/lib/variant.hpp @@ -59,6 +59,14 @@ ** concrete type does not support assignment or copy construction, the respective access ** function is replaced by an implementation raising a runtime error. ** + ** @note we use a Visitor interface generated through metaprogramming. + ** This may generate a lot of warnings "-Woverloaded-virtual", + ** since one \c handle(TX) function may shadow other \c handle(..) functions + ** from the inherited (generated) Visitor interface. These warnings are besides + ** the point, since not the \em client uses these functions, but the Variant does, + ** after upcasting to the interface. Make sure you define your specialisations with + ** the override modifier; when done so, it is safe to disable this warning here. + ** ** @see Veriant_test ** @see lib::diff::GenNode ** @see virtual-copy-support.hpp diff --git a/src/proc/asset/procpatt.hpp b/src/proc/asset/procpatt.hpp index 7f61e0751..b6202085a 100644 --- a/src/proc/asset/procpatt.hpp +++ b/src/proc/asset/procpatt.hpp @@ -39,7 +39,7 @@ namespace asset { class Proc; class ProcPatt; - class BuildInstruct; + struct BuildInstruct; typedef lib::P PProc; typedef lib::P PProcPatt; diff --git a/src/proc/asset/struct-scheme.hpp b/src/proc/asset/struct-scheme.hpp index 350f1d090..ab5a837a5 100644 --- a/src/proc/asset/struct-scheme.hpp +++ b/src/proc/asset/struct-scheme.hpp @@ -48,7 +48,7 @@ using boost::format; namespace proc { - class StreamType; + struct StreamType; namespace mobject { namespace session { diff --git a/src/proc/mobject/builder/model-port-registry.hpp b/src/proc/mobject/builder/model-port-registry.hpp index 694e5d783..e836c2289 100644 --- a/src/proc/mobject/builder/model-port-registry.hpp +++ b/src/proc/mobject/builder/model-port-registry.hpp @@ -87,7 +87,7 @@ namespace builder { public: /** @internal record to describe a model port */ - struct ModelPortDescriptor; + class ModelPortDescriptor; static void shutdown (); diff --git a/src/proc/mobject/session/query/fake-configrules.hpp b/src/proc/mobject/session/query/fake-configrules.hpp index 93058a54e..b74c18993 100644 --- a/src/proc/mobject/session/query/fake-configrules.hpp +++ b/src/proc/mobject/session/query/fake-configrules.hpp @@ -175,7 +175,7 @@ namespace session { { typedef typename WrapReturn::Wrapper Ret; - public: + /** (dummy) implementation of the QueryHandler interface */ virtual bool resolve (Ret& solution, Query const& q) diff --git a/src/proc/streamtype.hpp b/src/proc/streamtype.hpp index e357bd694..0c9c677fb 100644 --- a/src/proc/streamtype.hpp +++ b/src/proc/streamtype.hpp @@ -47,6 +47,10 @@ namespace proc { using lib::Symbol; +// "yes mummy, we all know this code is not finished yet..." +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" + /** * TODO write type comment */ diff --git a/tests/basics/streamtypebasicstest.cpp b/tests/basics/streamtypebasicstest.cpp index c0deaa898..53f8a6809 100644 --- a/tests/basics/streamtypebasicstest.cpp +++ b/tests/basics/streamtypebasicstest.cpp @@ -50,7 +50,7 @@ namespace test_format { class StreamTypeBasics_test : public Test { virtual void - run (Arg arg) + run (Arg) { ImplType iType = buildImplType (); basicImplTypeProperties (iType); diff --git a/tests/core/proc/mobject/builder/model-port-registry-test.cpp b/tests/core/proc/mobject/builder/model-port-registry-test.cpp index c769e802b..1d47fb689 100644 --- a/tests/core/proc/mobject/builder/model-port-registry-test.cpp +++ b/tests/core/proc/mobject/builder/model-port-registry-test.cpp @@ -161,6 +161,7 @@ namespace test { CHECK (mp1); CHECK (mp2); CHECK (mp1x); + CHECK (mp2x); CHECK (!mpNull); // bool check verifies setup and connected state CHECK ( ModelPort::exists (pipeA)); // this is the same check, but invoked just with an pipe-ID diff --git a/tests/library/diff/gen-node-basic-test.cpp b/tests/library/diff/gen-node-basic-test.cpp index cac1344a4..6fe4e4ca3 100644 --- a/tests/library/diff/gen-node-basic-test.cpp +++ b/tests/library/diff/gen-node-basic-test.cpp @@ -198,7 +198,7 @@ namespace test{ ++scope; CHECK (luid == scope->data.get()); ++scope; - CHECK (Time(0.92,0) == scope->data.get().end()); + CHECK (Time(920,0) == scope->data.get().end()); ++scope; auto spam = *scope; CHECK (!++scope); From 40decd68d40748a4ac719234814501101cb16615 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 16 Aug 2015 00:38:52 +0200 Subject: [PATCH 47/47] partial revert of preceding: rejected by GCC this was an half hearted attempt to satisfy CLang, but GCC as keen as a razor insists on these inherited functions not being accessible -- seems like the time is over, when GCC used to be forgiving and CLang briliantly precise... So the conclusion of this "round trip" is: whenever GCC also starts whining about shadowed overloaded virtual functions, we'll just switch to "-Wno-overloaded-virtual" and be done with that pointless discussion. Since C++11, we have the Java style override specifier, which does a way better job at spotting signature mismatches --- src/lib/meta/virtual-copy-support.hpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/lib/meta/virtual-copy-support.hpp b/src/lib/meta/virtual-copy-support.hpp index fee8effa4..0e502f579 100644 --- a/src/lib/meta/virtual-copy-support.hpp +++ b/src/lib/meta/virtual-copy-support.hpp @@ -200,9 +200,6 @@ namespace meta{ D& src = static_cast (*this); new(targetStorage) D(move(src)); } - - using I::copyInto; - using I::moveInto; }; @@ -216,9 +213,6 @@ namespace meta{ D const& src = static_cast (*this); new(targetStorage) D(src); } - - using I::copyInto; - using I::moveInto; }; @@ -241,9 +235,6 @@ namespace meta{ D& s = static_cast (*this); t = move(s); } - - using I::copyInto; - using I::moveInto; };