Merge Platform upgrade and Diff-Framework development

This commit is contained in:
Fischlurch 2015-08-16 01:39:25 +02:00
commit 24d7f55935
92 changed files with 3551 additions and 695 deletions

View file

@ -155,6 +155,29 @@ would require a ``deep copy'' of any underlying data structures.
Ichthyostega:: 'Sa 07 Jan 2012 21:49:09 CET' ~<prg@ichthyostega.de>~
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' ~<prg@ichthyostega.de>~
//endof_comments:

View file

@ -37,7 +37,6 @@ namespace lumiera{
namespace query {
using lib::P;
using lumiera::Query;
using boost::scoped_ptr;
@ -73,14 +72,14 @@ namespace query {
* is considered \e misconfiguration.
*/
template<class TAR>
P<TAR> operator() (Query<TAR> const&);
lib::P<TAR> operator() (Query<TAR> const&);
/** search through the registered defaults, never create anything.
* @return object fulfilling the query, \c empty ptr if not found.
*/
template<class TAR>
P<TAR> search (Query<TAR> const&);
lib::P<TAR> search (Query<TAR> const&);
/** retrieve an object fulfilling the query and register it as default.
* The resolution is delegated to the ConfigQuery system (which may cause
@ -88,7 +87,7 @@ namespace query {
* @return object fulfilling the query, \c empty ptr if no solution.
*/
template<class TAR>
P<TAR> create (Query<TAR> const&);
lib::P<TAR> create (Query<TAR> const&);
/** register the given object as default, after ensuring it fulfils the
* query. The latter may cause some properties of the object to be set,
@ -97,13 +96,13 @@ namespace query {
* @note only a weak ref to the object is stored
*/
template<class TAR>
bool define (P<TAR> const&, Query<TAR> const& =Query<TAR>());
bool define (lib::P<TAR> const&, Query<TAR> const& =Query<TAR>());
/** remove the defaults registration of the given object, if there was such
* @return false if nothing has been changed because the object wasn't registered
*/
template<class TAR>
bool forget (P<TAR> const&);
bool forget (lib::P<TAR> const&);
/** @internal for session lifecycle */

View file

@ -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

View file

@ -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<TimelineState> newState);

View file

@ -121,13 +121,13 @@ namespace lumiera {
};
typedef lib::IterSource<proc::mobject::ModelPort>::iterator ModelPorts;
typedef lib::IterSource<proc::mobject::OutputDesignation>::iterator Pipes;
typedef proc::play::POutputManager Output;
typedef proc::mobject::session::PClipMO Clip;
typedef proc::mobject::PFork Fork;
typedef proc::asset::PTimeline Timeline;
typedef proc::asset::PViewer Viewer;
using ModelPorts = lib::IterSource<proc::mobject::ModelPort>::iterator;
using Pipes = lib::IterSource<proc::mobject::OutputDesignation>::iterator;
using Output = proc::play::POutputManager;
using Clip = proc::mobject::session::PClip;
using Fork = proc::mobject::PFork;
using Timeline = proc::asset::PTimeline;
using Viewer = proc::asset::PViewer;
/* ==== convenience shortcuts for common use cases ==== */

View file

@ -121,7 +121,7 @@ namespace diff{
using Handler = HandlerFun<Interpreter,Val>;
};
template<class I, typename E>
template<class I, typename E> ///< alternatively derive value and interpreter from a Handler binding
struct InterpreterScheme<HandlerFun<I,E>>
{
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

View file

@ -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

View file

@ -24,17 +24,17 @@
/** @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
**
** 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,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 is 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<GenNode>
** 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
**
@ -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 <i>change handling closure</i>.
**
@ -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
@ -96,10 +101,10 @@
#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"
#include "lib/format-util.hpp"
//#include "lib/format-util.hpp"
#include "lib/variant.hpp"
#include "lib/util.hpp"
@ -115,8 +120,11 @@ namespace diff{
using std::string;
class GenNode;
struct GenNode;
using Rec = Record<GenNode>;
using RecRef = RecordRef<GenNode>;
using MakeRec = Rec::Mutator;
using DataValues = meta::Types<int
,int64_t
,short
@ -125,10 +133,12 @@ namespace diff{
,double
,string
,time::Time
,time::Offset
,time::Duration
,time::TimeSpan
,hash::LuidH
,Record<GenNode>
,RecRef
,Rec
>;
@ -141,17 +151,250 @@ namespace diff{
DataCap(X&& x)
: Variant<DataValues>(std::forward<X>(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;
};
/** generic data element node within a tree */
class GenNode
struct GenNode
{
class ID
: public idi::BareEntryID
{
friend struct GenNode;
template<typename X>
ID (X*, string const& symbolicID)
: idi::BareEntryID (symbolicID,
idi::getTypeHash<X>())
{ }
public:
ID (GenNode const& node)
: ID(node.idi)
{ }
// standard copy operations acceptable
operator string() const
{
return "ID(\""+getSym()+"\")";
}
};
//------GenNode Data fields---
ID idi;
DataCap data;
template<typename X>
GenNode(X&& val)
: idi(&val, buildChildID<X>())
, data(std::forward<X>(val))
{ }
template<typename X>
GenNode(string const& symbolicID, X&& val)
: idi(&val, symbolicID)
, data(std::forward<X>(val))
{ }
GenNode(string const& symbolicID, const char* text)
: GenNode(symbolicID, string(text))
{ }
GenNode(const char* text)
: GenNode(string(text))
{ }
////////////////////////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
isNamed() const
{
return util::startsWith (idi.getSym(), "_CHILD_");
}
bool
contains (GenNode const& elm) const
{
return contains (elm.idi);
}
bool
contains (ID const&) const
{
UNIMPLEMENTED("containment check by ID");
}
friend string
name (GenNode const& node)
{
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;
}
protected:
/** @internal for dedicated builder subclasses */
GenNode (ID&& id, DataCap&& d)
: idi(std::move(id))
, data(std::move(d))
{ }
template<typename X>
static GenNode::ID
fabricateRefID (string const& symbolicID)
{
X* typeID(0);
return ID(typeID, symbolicID);
}
private:
template<typename X>
static string
buildChildID()
{
return "_CHILD_" + idi::generateSymbolicID<X>();
}
};
/**
* 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<RecRef> (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<GenNode>
*/
Ref(GenNode& oNode)
: GenNode(ID(oNode)
, DataCap(RecRef(oNode.data.get<Rec>())))
{ }
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<GenNode> === */
template<>
inline GenNode&&
MakeRec::genNode()
{
return std::move (GenNode(std::move(record_)));
}
template<>
inline GenNode&&
MakeRec::genNode(string const& symbolicID)
{
return std::move (GenNode(symbolicID, std::move(record_)));
}
/* === Specialisation for handling of attributes in Record<GenNode> === */
template<>
inline bool
Rec::isAttribute (GenNode const& v)
{
return false; ////TODO
}
template<>
inline bool
Rec::isTypeID (GenNode const& v)
{
return false; ////TODO
}
template<>
inline string
Rec::extractTypeID (GenNode const& v)
{
return "todo"; ////TODO
}
template<>
inline GenNode
Rec::buildTypeAttribute (string const& typeID)
{
return GenNode("type", typeID);
}
template<>
inline string
Rec::extractKey (GenNode const& v)
{
return v.idi.getSym();
}
template<>
inline GenNode
Rec::extractVal (GenNode const& v)
{
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*/

View file

@ -32,9 +32,50 @@
** 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.
**
** @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
**
*/
@ -44,11 +85,19 @@
#include "lib/error.hpp"
//#include "lib/util.hpp"
//#include "lib/format-string.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 <boost/noncopyable.hpp>
#include <algorithm>
#include <utility>
#include <vector>
#include <string>
//#include <vector>
//#include <map>
namespace lib {
@ -57,19 +106,521 @@ namespace diff{
namespace error = lumiera::error;
//using util::_Fmt;
using std::string;
/** 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<GenNode> -- using the
* embedded name-ID of the GenNode elements as key for attributes.
*
* 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. Essentially, Lumiera's diff framework relies on this.
*/
template<typename VAL>
class Record
{
using _Vec = std::vector<VAL>;
using Attribs = _Vec;
using Children = _Vec;
using ElmIter = typename _Vec::const_iterator;
string type_;
Attribs attribs_;
Children children_;
public:
Record()
: type_("NIL")
{ }
template<typename A, typename C>
Record(Symbol typeID, A&& att, C&& chi)
: type_(typeID)
, attribs_(std::forward<A> (att))
, children_(std::forward<C> (chi))
{ }
template<typename A, typename C>
Record(Symbol typeID, std::initializer_list<A> const&& att
, std::initializer_list<C> const&& chi)
: type_(typeID)
, attribs_(att)
, children_(chi)
{ }
template<typename SEQ>
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<VAL> const&& ili)
: Record(ili)
{ }
// all default copy operations acceptable
/** for diagnostic purpose: include format-util.hpp */
operator std::string() const;
bool
empty() const
{
return attribs_.empty()
&& children_.empty();
}
string
getType() const
{
return type_;
}
bool
hasAttribute (string key) const
{
return attribs_.end() != findKey(key);
}
bool
contains (VAL const& val) const
{
return util::contains (children_, val);
}
VAL const&
get (string key) const
{
ElmIter found = findKey (key);
if (attribs_.end() == found)
throw error::Invalid ("Record has no attribute \""+key+"\"");
else
return *found;
}
/**
* 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 ====== */
using iterator = IterAdapter<typename _Vec::const_iterator, const Record*>;
using scopeIter = typename iter_stl::_SeqT<const _Vec>::Range;
using keyIter = TransformIter<scopeIter, string>;
using valIter = TransformIter<scopeIter, VAL>;
/** 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_); }
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<class ITER>
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<class ITER>
friend bool
checkPoint (const Record* src, ITER& pos)
{
REQUIRE (src);
if ((pos != ITER()) && (pos == src->attribs_.end()) && !src->children_.empty())
{
pos = src->children_.begin();
return true;
}
else
if (pos != ITER() && (pos != src->children_.end()))
return true;
else
{
pos = ITER();
return false;
} }
private:
/* === 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 renderAttribute (VAL const& a);
static string extractKey (VAL const& v);
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);
});
} ///////////////////TODO this lambda triggers a GCC-4.7.2 Bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56402
friend bool
operator== (Record const& r1, Record const& r2)
{
return r1.type_ == r2.type_
&& r1.attribs_ == r2.attribs_
&& r1.children_ == r2.children_;
}
friend bool
operator!= (Record const& r1, Record const& r2)
{
return ! (r1 == r2);
}
};
template<typename VAL>
class Record<VAL>::Mutator
: boost::noncopyable
{
using Rec = Record<VAL>;
Rec record_;
public:
Mutator()
: record_()
{ }
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_);
}
bool
empty() const
{
return record_.empty();
}
/* === functions to alter contents === */
void
setType (string const& newTypeID)
{
set ("type", Rec::buildTypeAttribute (newTypeID));
record_.type_ = newTypeID;
}
Mutator&
type (string const& typeID)
{
setType (typeID);
return *this;
}
Mutator&
set (string const& key, VAL const& newValue)
{
///////////TODO;
return *this;
}
Mutator&
appendChild (VAL const& newChild)
{
record_.children_.push_back (newChild);
return *this;
}
Mutator&
prependChild (VAL const& newChild)
{
record_.children_.insert (record_.children_.begin(), newChild);
return *this;
}
/* === 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<GenNode>
*/
VAL&& genNode();
VAL&& genNode(string const& symbolicID);
template<typename X, typename...ARGS>
Mutator& attrib (string const& key, X const& initialiser, ARGS&& ...args)
{
set (key, VAL(initialiser));
return attrib (std::forward<ARGS>(args)...);
}
template<typename X>
Mutator&
attrib (string const& key, X const& initialiser)
{
set (key, VAL(initialiser));
return *this;
}
template<typename X, typename...ARGS>
Mutator& scope (X const& initialiser, ARGS&& ...args)
{
appendChild (VAL(initialiser));
return scope (std::forward<ARGS>(args)...);
}
template<typename X>
Mutator& scope (X const& initialiser)
{
appendChild (VAL(initialiser));
return *this;
}
};
/**
* 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.
* @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<typename VAL>
class RecordRef
{
using Target = Record<VAL>;
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
explicit
operator bool() const
{
return empty();
}
bool
empty() const
{
return bool(record_);
}
/** target is accessed by cast
* @throws error::Logic on bottom reference
*/
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<>
inline string
Record<string>::extractKey (string const& v)
{
size_t pos = v.find('=');
if (string::npos == pos)
return "";
else
return v.substr(0,pos); //////////////////////////////TODO should trim here
}
template<>
inline string
Record<string>::extractVal (string const& v)
{
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<string>::isAttribute (string const& v)
{
return string::npos != v.find('=');
}
template<>
inline bool
Record<string>::isTypeID (string const& v)
{
return isAttribute(v)
&& "type" == extractKey(v);
}
template<>
inline string
Record<string>::extractTypeID (string const& v)
{
return "todo"; //////////////////////////////////////////TODO do we really need this function?
}
template<>
inline string
Record<string>::buildTypeAttribute (string const& typeID)
{
return "type = "+typeID;
}
template<>
inline string
Record<string>::renderAttribute (string const& attrib)
{
return extractKey(attrib) + " = " + extractVal(attrib);
}
/* === Diagnostics === */
template<typename VAL>
Record<VAL>::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
#endif /*LIB_DIFF_GEN_NODE_H*/

View file

@ -75,7 +75,7 @@ namespace diff{
namespace {
template<class PAR>
class Builder;
struct Builder;
using ID = Literal;
using Attribute = DataCap;

View file

@ -117,7 +117,7 @@
namespace std { // forward declaration to avoid including <iostream>
template<typename C>
class char_traits;
struct char_traits;
template<typename C, class _TRAITS>
class basic_ostream;

View file

@ -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 <string>
#include <sstream>
#include <cstring>
#include <typeinfo>
#include <boost/lexical_cast.hpp>
@ -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<class CON, typename TOGGLE = void>
struct _RangeIter
{
using StlIter = typename CON::const_iterator;
lib::RangeIter<StlIter> iter;
_RangeIter(CON const& collection)
: iter(begin(collection), end(collection))
{ }
};
template<class IT>
struct _RangeIter<IT, typename enable_if< can_IterForEach<IT> >::type>
{
IT iter;
_RangeIter(IT&& srcIter)
: iter(std::forward<IT>(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<class CON>
inline string
join (CON&& coll, string const& delim =", ")
{
using Coll = typename lib::meta::Strip<CON>::Type;
using Val = typename Coll::value_type;
std::function<string(Val const&)> toString = [] (Val const& val) { return str(val); };
_RangeIter<Coll> range(std::forward<Coll>(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

View file

@ -39,13 +39,13 @@
*/
#ifndef ASSET_ENTRY_ID_H
#define ASSET_ENTRY_ID_H
#ifndef LIB_IDI_ENTRY_ID_H
#define LIB_IDI_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"
#include <boost/functional/hash.hpp>
@ -54,19 +54,37 @@
#include <string>
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;
using std::string;
using std::ostream;
namespace idi {
using lib::idi::generateSymbolicID;
using lib::idi::getTypeHash;
using lib::idi::typeSymbol;
using lib::hash::LuidH;
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
@ -82,16 +100,22 @@ namespace asset {
* 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<void*> (seed));
return reinterpret_cast<LuidH&> (tmpLUID);
@ -111,7 +135,6 @@ namespace asset {
class BareEntryID
: public boost::equality_comparable<BareEntryID>
{
typedef lib::hash::LuidH LuidH;
string symbol_;
LuidH hash_;
@ -123,9 +146,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)
: symbol_(util::sanitise(symbolID))
, hash_(idi::buildHash (symbol_, seed))
BareEntryID (string const& symbolID, HashVal seed =0)
: symbol_(symbolID)
, hash_(buildHash (symbol_, seed))
{ }
public:
@ -160,7 +183,7 @@ namespace asset {
template<typename TAR>
EntryID<TAR> recast() const;
EntryID<TAR> const& recast() const;
};
@ -193,7 +216,7 @@ namespace asset {
/** case-1: auto generated symbolic ID */
EntryID()
: BareEntryID (idi::generateSymbolID<TY>(), getTypeHash())
: BareEntryID (generateSymbolicID<TY>(), getTypeHash<TY>())
{ }
/** case-2: explicitly specify a symbolic ID to use.
@ -202,31 +225,10 @@ namespace asset {
*/
explicit
EntryID (string const& symbolID)
: BareEntryID (symbolID, getTypeHash())
: BareEntryID (util::sanitise(symbolID), getTypeHash<TY>())
{ }
/** 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<TY>::catFolder());
return Asset::Ident (this->getSym(), cat);
}
static idi::HashVal
getTypeHash()
{
return hash_value (Category (STRUCT, idi::StructTraits<TY>::catFolder()));
}
/** @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
@ -235,23 +237,23 @@ namespace asset {
static bool
canRecast (BareEntryID const& bID)
{
return bID.getHash() == idi::buildHash (bID.getSym(), getTypeHash());
return bID.getHash() == buildHash (bID.getSym(), getTypeHash<TY>());
}
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<EntryID const&> (bID);
}
operator string () const
{
return "ID<"+idi::StructTraits<TY>::idSymbol()+">-"+EntryID::getSym();
return "ID<"+typeSymbol<TY>()+">-"+EntryID::getSym();
}
friend ostream& operator<< (ostream& os, EntryID const& id) { return os << string(id); }
@ -280,12 +282,13 @@ namespace asset {
* Exception if it doesn't match the stored hash.
*/
template<typename TAR>
EntryID<TAR> BareEntryID::recast() const
EntryID<TAR> const&
BareEntryID::recast() const
{
return EntryID<TAR>::recast(*this);
}
}} // namespace proc::asset
#endif
}} // namespace lib::idi
#endif /*LIB_IDI_ENTRY_ID_H*/

76
src/lib/idi/genfunc.cpp Normal file
View file

@ -0,0 +1,76 @@
/*
GenFunc - generic identification functions (raw)
Copyright (C) Lumiera.org
2015, Hermann Vosseler <Ichthyostega@web.de>
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 <string>
#include <boost/functional/hash.hpp>
using util::_Fmt;
using std::string;
namespace lib {
namespace test{ // see test-helper.cpp
std::string demangleCxx (lib::Literal rawName);
}
namespace idi {
namespace format { // 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

139
src/lib/idi/genfunc.hpp Normal file
View file

@ -0,0 +1,139 @@
/*
GENFUNC.hpp - generic identification functions
Copyright (C) Lumiera.org
2015, Hermann Vosseler <Ichthyostega@web.de>
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/symbol.hpp"
#include "lib/typed-counter.hpp"
//#include "lib/hash-standard.hpp"
#include <typeinfo>
#include <string>
namespace lib {
namespace idi {
using lib::HashVal;
using std::string;
namespace format { // 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...
/** 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<typename TY>
inline string
typeSymbol()
{
return format::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<typename TY>
inline string
typeFullID()
{
return format::demangled_sanitised_name (typeid(TY).name());
}
template<typename TY>
inline string
categoryFolder()
{
return typeSymbol<TY>();
}
template<typename TY>
inline string
namePrefix()
{
return typeSymbol<TY>();
}
/** 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.
*/
template<class TY>
inline string
generateSymbolicID()
{
static TypedCounter instanceCounter;
return format::instance_formatter (namePrefix<TY>(), instanceCounter.inc<TY>());
}
/**
* @return a boost hash value, based on the full (mangled) C++ type name
*/
template<typename TY>
inline HashVal
getTypeHash()
{
Literal rawTypeName (typeid(TY).name());
return hash_value (rawTypeName);
}
}} // namespace lib::idi
#endif /*LIB_IDI_GENFUNC_H*/

View file

@ -0,0 +1,334 @@
/*
ITER-ADAPTER-PTR-DEREF.hpp - wrapping iterator to dereference pointers automatically
Copyright (C) Lumiera.org
2015, Hermann Vosseler <Ichthyostega@web.de>
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-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.
**
** 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
**
*/
#ifndef LIB_ITER_ADAPTER_PTR_DEREF_H
#define LIB_ITER_ADAPTER_PTR_DEREF_H
#include "lib/iter-adapter.hpp"
#include <boost/type_traits/remove_const.hpp>
namespace lib {
namespace {
/** helper to remove pointer,
* while retaining const */
template<typename T>
struct RemovePtr { typedef T Type; };
template<typename T>
struct RemovePtr<T*> { typedef T Type; };
template<typename T>
struct RemovePtr<const T*> { typedef const T Type; };
template<typename T>
struct RemovePtr<T* const> { typedef const T Type; };
template<typename T>
struct RemovePtr<const T* const> { typedef const T 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 IT>
class PtrDerefIter
: public lib::BoolCheckable<PtrDerefIter<IT> >
{
IT i_; ///< nested source iterator
public:
typedef typename IT::value_type pointer;
typedef typename RemovePtr<pointer>::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<value_type>::type ValueTypeBase; // value_type without const
typedef typename IterType<IT>::template SimilarIter< ValueTypeBase* * >::Type WrappedIterType;
typedef typename IterType<IT>::template SimilarIter<const ValueTypeBase* * >::Type WrappedConstIterType;
typedef PtrDerefIter<WrappedIterType> IterType;
typedef PtrDerefIter<WrappedConstIterType> 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 <i>additional
* ctor besides the default copy ctor</i> */
PtrDerefIter (PtrDerefIter<WrappedIterType> const& oIter)
: i_(reinterpret_cast<IT const&> (oIter.getBase()))
{ }
PtrDerefIter&
operator= (PtrDerefIter<WrappedIterType> const& ref)
{
i_ = reinterpret_cast<IT const&> (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<WrappedConstIterType>`
* only exposes const elements.
*/
static PtrDerefIter
build_by_cast (WrappedIterType const& srcIter)
{
return PtrDerefIter (reinterpret_cast<IT const&> (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<class I1, class I2>
bool operator== (PtrDerefIter<I1> const& il, PtrDerefIter<I2> const& ir) { return il.getBase() == ir.getBase(); }
template<class I1, class I2>
bool operator!= (PtrDerefIter<I1> const& il, PtrDerefIter<I2> 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 IT>
class AddressExposingIter
: public lib::BoolCheckable<AddressExposingIter<IT> >
{
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<class I1, class I2>
bool operator== (AddressExposingIter<I1> const& il, AddressExposingIter<I2> const& ir) { return il.getBase() == ir.getBase(); }
template<class I1, class I2>
bool operator!= (AddressExposingIter<I1> const& il, AddressExposingIter<I2> const& ir) { return !(il == ir); }
}// namespace lib
#endif /*LIB_ITER_ADAPTER_PTR_DEREF_H*/

View file

@ -42,7 +42,7 @@
#include "lib/iter-adapter.hpp"
#include "lib/iter-adapter-ptr-deref.hpp"

View file

@ -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,14 +102,12 @@
#include "lib/bool-checkable.hpp"
#include "lib/iter-type-binding.hpp"
#include <boost/type_traits/remove_const.hpp>
namespace lib {
namespace { // internal helpers
void
inline void
_throwIterExhausted()
{
throw lumiera::error::Invalid ("Can't iterate further",
@ -114,6 +115,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<ITER&&> (it); } \
friend ITER end (ITER const&) { return ITER(); }
/**
@ -244,6 +251,9 @@ namespace lib {
_throwIterExhausted();
}
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (IterAdapter);
/// comparison is allowed to access impl iterator
template<class P1, class P2, class CX>
friend bool operator== (IterAdapter<P1,CX> const&, IterAdapter<P2,CX> const&);
@ -357,6 +367,9 @@ namespace lib {
}
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (IterStateWrapper);
/// comparison is allowed to access state implementation core
template<class T1, class T2, class STX>
friend bool operator== (IterStateWrapper<T1,STX> const&, IterStateWrapper<T2,STX> const&);
@ -469,6 +482,9 @@ namespace lib {
const IT& getEnd() const { return e_; }
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (RangeIter);
private:
void
@ -492,28 +508,6 @@ namespace lib {
namespace {
/** helper to remove pointer,
* while retaining const */
template<typename T>
struct RemovePtr { typedef T Type; };
template<typename T>
struct RemovePtr<T*> { typedef T Type; };
template<typename T>
struct RemovePtr<const T*> { typedef const T Type; };
template<typename T>
struct RemovePtr<T* const> { typedef const T Type; };
template<typename T>
struct RemovePtr<const T* const> { typedef const T Type; };
}
/**
* Helper for type rewritings:
* get the element type for an iterator like entity
@ -548,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 IT>
class PtrDerefIter
: public lib::BoolCheckable<PtrDerefIter<IT> >
{
IT i_; ///< nested source iterator
public:
typedef typename IT::value_type pointer;
typedef typename RemovePtr<pointer>::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<value_type>::type ValueTypeBase; // value_type without const
typedef typename IterType<IT>::template SimilarIter< ValueTypeBase* * >::Type WrappedIterType;
typedef typename IterType<IT>::template SimilarIter<const ValueTypeBase* * >::Type WrappedConstIterType;
typedef PtrDerefIter<WrappedIterType> IterType;
typedef PtrDerefIter<WrappedConstIterType> 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 <i>additional
* ctor besides the default copy ctor</i> */
PtrDerefIter (PtrDerefIter<WrappedIterType> const& oIter)
: i_(reinterpret_cast<IT const&> (oIter.getBase()))
{ }
PtrDerefIter&
operator= (PtrDerefIter<WrappedIterType> const& ref)
{
i_ = reinterpret_cast<IT const&> (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<WrappedConstIterType>`
* only exposes const elements.
*/
static PtrDerefIter
build_by_cast (WrappedIterType const& srcIter)
{
return PtrDerefIter (reinterpret_cast<IT const&> (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<class I1, class I2>
bool operator== (PtrDerefIter<I1> const& il, PtrDerefIter<I2> const& ir) { return il.getBase() == ir.getBase(); }
template<class I1, class I2>
bool operator!= (PtrDerefIter<I1> const& il, PtrDerefIter<I2> 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 IT>
class AddressExposingIter
: public lib::BoolCheckable<AddressExposingIter<IT> >
{
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<class I1, class I2>
bool operator== (AddressExposingIter<I1> const& il, AddressExposingIter<I2> const& ir) { return il.getBase() == ir.getBase(); }
template<class I1, class I2>
bool operator!= (AddressExposingIter<I1> const& il, AddressExposingIter<I2> const& ir) { return !(il == ir); }
/** wrapper to declare exposed values const */
template<class IT>
class ConstIter
@ -874,5 +614,5 @@ namespace lib {
} // namespace lib
#endif
}// namespace lib
#endif /*LIB_ITER_ADAPTER_H*/

View file

@ -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)
};

View file

@ -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 <b>virtual copy</b> -- 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;

View file

@ -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"

View file

@ -68,7 +68,8 @@ namespace lib {
and should be usable both with \c std::tr1 and
\c <boost/functional/hash.hpp> . 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)

View file

@ -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");

View file

@ -52,7 +52,7 @@ namespace time {
{
public:
virtual ~TCode();
virtual ~TCode() { }
operator string() const { return show(); }
string describe() const { return string(tcID()); }

View file

@ -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

View file

@ -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 <typename MAP>
inline bool

View file

@ -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
@ -111,6 +119,12 @@ namespace lib {
using Type = X;
};
template<typename X, typename TYPES>
struct CanBuildFrom<const X, Node<X, TYPES>>
{
using Type = X;
};
template<typename X, typename T,typename TYPES>
struct CanBuildFrom<X, Node<T, TYPES>>
{
@ -136,12 +150,11 @@ namespace lib {
= meta::InstantiateForEach<typename TYPES::List, ValueAcceptInterface>;
/////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.
@ -154,6 +167,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<typename TYPES>
class Variant
@ -395,6 +412,12 @@ namespace lib {
return buff<X>().access();
}
template<typename X>
X const&
get() const
{
return unConst(this)->template get<X>();
}
void
accept (Visitor& visitor)

View file

@ -87,7 +87,6 @@ namespace asset {
using std::static_pointer_cast;
using lib::HashVal;
using lib::P;
@ -119,8 +118,8 @@ namespace asset {
class Asset;
class AssetManager;
typedef const ID<Asset>& IDA;
typedef P<Asset> PAsset;
typedef P<const Asset> PcAsset;
typedef lib::P<Asset> PAsset;
typedef lib::P<const Asset> PcAsset;

View file

@ -44,15 +44,14 @@ using std::string;
namespace proc {
namespace asset {
using lib::P;
using lib::Symbol;
using lib::Literal;
class Proc;
class ProcPatt;
typedef P<const asset::Proc> PProc;
typedef P<const asset::ProcPatt> PProcPatt;
using PProc = lib::P<const asset::Proc>;
using PProcPatt = lib::P<const asset::ProcPatt>;
static Symbol CURRENT = "current";

View file

@ -49,7 +49,7 @@ namespace asset {
mediaref.ident.version );
}
Media::PClipMO
Media::PClip
createClipMO (const Clip& thisClipAsset, const Media& mediaChannel)
{
return mobject::MObject::create (thisClipAsset,mediaChannel);
@ -76,7 +76,7 @@ namespace asset {
* Placements or no placement at all (meaning it need not
* be placed within the session)
*/
Media::PClipMO
Media::PClip
Clip::createClip () const
{
return clipMO_;
@ -87,7 +87,7 @@ namespace asset {
/** return this wrapped into a shared ptr,
* because it's already the desired asset::Clip
*/
Media::PClip
Media::PClipAsset
Clip::getClipAsset ()
{
return AssetManager::wrap (*this);

View file

@ -42,21 +42,21 @@ namespace asset {
const Media& source_;
/** the corresponding (dependent) clip-MO */
PClipMO clipMO_;
PClip clipMO_;
public:
virtual PClipMO createClip () const;
virtual PClip createClip () const;
protected:
Clip (Media& mediaref);
friend class MediaFactory;
virtual PClip getClipAsset ();
virtual PClipAsset getClipAsset ();
virtual PMedia checkCompound () const;
};
typedef P<const asset::Clip> PClipAsset;
typedef lib::P<const asset::Clip> PClipAsset;
const string CLIP_SUBFOLDER = "clips"; // TODO: handling of hard-wired constants....

View file

@ -107,7 +107,7 @@ namespace asset {
public:
template<class KIND>
P<KIND>
lib::P<KIND>
get (ID<KIND> hash) const
{
return dynamic_pointer_cast<KIND,Asset> (find (hash));
@ -115,7 +115,7 @@ namespace asset {
template<class KIND>
void
put (ID<KIND> hash, P<KIND>& ptr)
put (ID<KIND> hash, lib::P<KIND>& ptr)
{
table[hash] = static_pointer_cast (ptr);
}

View file

@ -41,7 +41,7 @@ namespace asset {
{};
typedef P<Inventory> PInv;
typedef lib::P<Inventory> PInv;
}} // namespace proc::asset

View file

@ -75,11 +75,11 @@ namespace asset {
Media::PClipMO
Media::PClip
Media::createClip ()
{
PClip clipAsset (getClipAsset());
PClipMO clipMO = clipAsset->createClip();
PClipAsset clipAsset (getClipAsset());
PClip clipMO = clipAsset->createClip();
ENSURE (clipMO->isValid());
return clipMO;
@ -90,7 +90,7 @@ namespace asset {
* or to get the right reference to some already existing asset::Clip,
* especially when this media is part of a compound (multichannel) media.
*/
Media::PClip
Media::PClipAsset
Media::getClipAsset ()
{
if (PMedia parent = this->checkCompound())
@ -228,7 +228,7 @@ namespace asset {
* @throw Invalid if the given media asset is not top-level,
* but rather part or a multichannel (compound) media
*/
P<Clip>
lib::P<Clip>
MediaFactory::operator() (Media& mediaref)
{
if (mediaref.checkCompound())

View file

@ -75,10 +75,10 @@ namespace asset {
const Duration len_;
public:
typedef P<Media> PMedia;
typedef P<proc::asset::Clip> PClip;
typedef P<proc::asset::ProcPatt> PProcPatt;
typedef proc::mobject::session::PClipMO PClipMO;
using PMedia = lib::P<Media>;
using PClipAsset = lib::P<proc::asset::Clip>;
using PProcPatt = lib::P<proc::asset::ProcPatt>;
using PClip = mobject::Placement<mobject::session::Clip>;
static MediaFactory create;
@ -103,7 +103,7 @@ namespace asset {
* it can be regenerated from the corresponding asset::Clip
* @return a Placement smart ptr owning the new Clip MObject
*/
PClipMO createClip ();
PClip createClip ();
/** @return the overall length of the media represented by this asset */
virtual Duration getLength () const;
@ -116,7 +116,7 @@ namespace asset {
/** get or create the correct asset::Clip
* corresponding to this media */
virtual PClip getClipAsset ();
virtual PClipAsset getClipAsset ();
/** predicate to decide if this asset::Media
* is part of a compound (multichannel) media.
@ -144,7 +144,7 @@ namespace asset {
: boost::noncopyable
{
public:
typedef P<Media> PType;
typedef lib::P<Media> PType;
PType operator() (Asset::Ident& key, const string& file="");
PType operator() (const string& file, const Category& cat);
@ -154,7 +154,7 @@ namespace asset {
PType operator() (const char* file, const Category& cat);
PType operator() (const char* file, asset::Kind);
P<Clip>
lib::P<Clip>
operator() (Media& mediaref);
};

View file

@ -25,6 +25,7 @@
#include "proc/asset/meta.hpp"
#include "lib/util.hpp"
using lib::idi::EntryID;
namespace proc {
namespace asset {

View file

@ -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 <boost/noncopyable.hpp>
@ -139,7 +139,7 @@ namespace asset {
inline ID<Meta>::ID(HashVal id) : ID<Asset> (id) {};
inline ID<Meta>::ID(const Meta& meta) : ID<Asset> (meta.getID()) {};
typedef P<Meta> PMeta;
typedef lib::P<Meta> PMeta;
@ -150,13 +150,13 @@ namespace asset {
: boost::noncopyable
{
public:
typedef P<asset::Meta> PType;
typedef lib::P<asset::Meta> PType;
template<class MA>
meta::Builder<MA> operator() (EntryID<MA> elementIdentity);
meta::Builder<MA> operator() (lib::idi::EntryID<MA> elementIdentity);
template<class MA>
meta::Builder<MA> operator() (meta::Descriptor const& prototype, EntryID<MA> elementIdentity);
meta::Builder<MA> operator() (meta::Descriptor const& prototype, lib::idi::EntryID<MA> elementIdentity);
};

View file

@ -22,7 +22,7 @@
#include "proc/asset/meta/time-grid.hpp"
#include "proc/asset/entry-id.hpp"
#include "proc/asset/struct-scheme.hpp"
#include "proc/assetmanager.hpp"
#include "lib/time/quantiser.hpp"
#include "lib/time/timevalue.hpp"
@ -36,7 +36,6 @@
using util::_Fmt;
using util::cStr;
using util::isnil;
using boost::str;
using std::string;
@ -49,8 +48,8 @@ namespace meta {
/** */
TimeGrid::TimeGrid (EntryID<TimeGrid> const& nameID)
: Meta (nameID.getIdent())
TimeGrid::TimeGrid (GridID const& nameID)
: Meta (idi::getAssetIdent (nameID))
{ }
@ -108,12 +107,12 @@ namespace meta {
{
public:
SimpleTimeGrid (Time start, Duration frameDuration, EntryID<TimeGrid> const& name)
SimpleTimeGrid (Time start, Duration frameDuration, GridID const& name)
: TimeGrid (name)
, FixedFrameQuantiser(frameDuration,start)
{ }
SimpleTimeGrid (Time start, FrameRate frames_per_second, EntryID<TimeGrid> const& name)
SimpleTimeGrid (Time start, FrameRate frames_per_second, GridID const& name)
: TimeGrid (name)
, FixedFrameQuantiser(frames_per_second,start)
{ }
@ -136,7 +135,7 @@ namespace meta {
* Later on the intention is that in such cases, instead of creating a new grid
* we'll silently return the already registered existing and equivalent grid.
*/
P<TimeGrid>
lib::P<TimeGrid>
Builder<TimeGrid>::commit()
{
if (predecessor_)
@ -149,7 +148,7 @@ namespace meta {
_Fmt gridIdFormat("grid(%f_%d)");
id_ = string(gridIdFormat % fps_ % _raw(origin_));
}
EntryID<TimeGrid> nameID (id_);
GridID nameID (id_);
return publishWrapped (*new SimpleTimeGrid(origin_, fps_, nameID));
}

View file

@ -48,6 +48,7 @@
#define ASSET_META_TIME_GRID_H
#include "proc/asset/meta.hpp"
#include "lib/idi/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<TimeGrid> PGrid;
using PGrid = lib::P<TimeGrid>;
using GridID = lib::idi::EntryID<TimeGrid>;
/**
@ -91,7 +93,7 @@ namespace meta {
static PGrid build (Symbol gridID, FrameRate frames_per_second, Time origin);
protected:
TimeGrid (EntryID<TimeGrid> const&);
TimeGrid (GridID const&);
};
@ -111,7 +113,7 @@ namespace meta {
* the origin of this (local) grid.
* @todo currently not supported (as of 12/2010)
*/
P<TimeGrid> predecessor_;
lib::P<TimeGrid> predecessor_;
/**
* initialise to blank (zero).
@ -128,7 +130,7 @@ namespace meta {
/** create a time grid
* based on settings within this builder
*/
P<TimeGrid> commit();
lib::P<TimeGrid> commit();
};

View file

@ -40,7 +40,7 @@ namespace asset {
using std::string;
class Pipe;
typedef P<Pipe> PPipe;
typedef lib::P<Pipe> PPipe;
template<>

View file

@ -50,7 +50,7 @@ namespace asset {
class Proc;
class ProcFactory;
typedef P<const Proc> PProc;
typedef lib::P<const Proc> PProc;
@ -121,7 +121,7 @@ namespace asset {
: boost::noncopyable
{
public:
typedef P<asset::Proc> PType;
typedef lib::P<asset::Proc> PType;
PType operator() (Asset::Ident& key); ////////////TODO define actual operation

View file

@ -65,7 +65,7 @@ namespace asset {
* some ProcPatt as a template for creating more
* specialised patterns.
*/
P<ProcPatt>
lib::P<ProcPatt>
ProcPatt::newCopy (string newID) const
{
TODO ("implement the Pattern-ID within the propDescriptor!");

View file

@ -39,9 +39,9 @@ namespace asset {
class Proc;
class ProcPatt;
class BuildInstruct;
typedef P<const asset::Proc> PProc;
typedef P<const asset::ProcPatt> PProcPatt;
struct BuildInstruct;
typedef lib::P<const asset::Proc> PProc;
typedef lib::P<const asset::ProcPatt> PProcPatt;
typedef vector<BuildInstruct> InstructionSequence;
@ -64,7 +64,7 @@ namespace asset {
friend class StructFactoryImpl;
public:
P<ProcPatt> newCopy (string newID) const;
lib::P<ProcPatt> newCopy (string newID) const;
ProcPatt& attach (Symbol where, PProc& node);
ProcPatt& operator+= (PProcPatt& toReuse);

View file

@ -78,7 +78,7 @@ namespace asset {
class Sequence;
typedef P<Sequence> PSequence;
typedef lib::P<Sequence> PSequence;
@ -103,7 +103,6 @@ namespace asset {
};
typedef P<Sequence> PSequence;
///////////////////////////TODO currently just fleshing the API

View file

@ -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;

View file

@ -37,6 +37,8 @@
#include "lib/symbol.hpp"
#include "proc/asset.hpp"
#include "lib/idi/entry-id.hpp"
#include <cstdlib>
@ -45,11 +47,9 @@
using boost::format;
namespace lumiera {
class StreamType;
}
namespace proc {
struct StreamType;
namespace mobject {
namespace session {
@ -105,7 +105,7 @@ namespace asset{
static Symbol catFolder() { return "pipes";}
static Symbol idSymbol() { return "pipe"; }
};
template<> struct StructTraits<lumiera::StreamType>
template<> struct StructTraits<proc::StreamType>
{
static Symbol namePrefix() { return "type"; }
static Symbol catFolder() { return "stream-types";}
@ -147,18 +147,22 @@ namespace asset{
template<class STRU>
inline string
generateSymbolID()
/** 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<typename TY>
inline Asset::Ident
getAssetIdent (lib::idi::EntryID<TY> const& entryID)
{
static uint i=0;
static format namePattern ("%s.%03d");
////////////////////////////////////////////////////////////////////////////////TICKET #166 : needs to be pushed down into a *.cpp
return str(namePattern % StructTraits<STRU>::namePrefix() % (++i) );
Category cat (STRUCT, idi::StructTraits<TY>::catFolder());
return Asset::Ident (entryID.getSym(), cat);
}
}}} // namespace asset::idi
#endif

View file

@ -76,7 +76,7 @@ namespace asset {
* asset is a code smell ////////////////////////////TICKET #691
*/
template<class STRU>
P<STRU>
lib::P<STRU>
StructFactory::newInstance (Symbol nameID)
{
Query<STRU> desired_name (isnil(nameID)? "" : "id("+nameID+")");
@ -103,10 +103,10 @@ namespace asset {
* created as a side effect of calling the concrete Struct subclass ctor.
*/
template<class STRU>
P<STRU>
lib::P<STRU>
StructFactory::operator() (Query<STRU> const& capabilities)
{
P<STRU> res;
lib::P<STRU> res;
QueryHandler<STRU>& typeHandler = ConfigResolver::instance();
typeHandler.resolve (res, capabilities);
@ -131,7 +131,7 @@ namespace asset {
* re-invoking the ConfigRules....
*/
template<class STRU>
P<STRU>
lib::P<STRU>
StructFactory::made4fake (Query<STRU> const& query)
{
STRU* pS = impl_->fabricate(query);
@ -149,7 +149,7 @@ namespace asset {
* @see ProcPatt
* @see DefaultsManager
*/
P<Pipe>
lib::P<Pipe>
StructFactory::newPipe (string pipeID, string streamID)
{
normaliseID (pipeID);
@ -178,18 +178,19 @@ namespace asset {
namespace proc {
namespace asset {
using PPipe = lib::P<Pipe>;
template P<Pipe> StructFactory::operator() (Query<Pipe> const&);
template PPipe StructFactory::operator() (Query<Pipe> const&);
template PProcPatt StructFactory::operator() (Query<const ProcPatt> const&);
template PTimeline StructFactory::operator() (Query<Timeline> const&);
template PSequence StructFactory::operator() (Query<Sequence>const&);
template P<Pipe> StructFactory::newInstance (Symbol);
template PPipe StructFactory::newInstance (Symbol);
template PProcPatt StructFactory::newInstance (Symbol);
template PTimeline StructFactory::newInstance (Symbol);
template PSequence StructFactory::newInstance (Symbol);
template P<Pipe> StructFactory::made4fake (Query<Pipe> const&);
template PPipe StructFactory::made4fake (Query<Pipe> const&);
template PProcPatt StructFactory::made4fake (Query<const ProcPatt> const&);
template PTimeline StructFactory::made4fake (Query<Timeline> const&);
template PSequence StructFactory::made4fake (Query<Sequence>const&);

View file

@ -151,17 +151,17 @@ namespace asset {
public:
template<class STRU>
P<STRU> operator() (Query<STRU> const& query);
lib::P<STRU> operator() (Query<STRU> const& query);
// P<Timeline> operator() (MORef<Binding>); ///////////TODO doesn't this create circular includes?? Any better idea how to refer to an existing binding?
// lib::P<Timeline> operator() (MORef<Binding>); ///////////TODO doesn't this create circular includes?? Any better idea how to refer to an existing binding?
template<class STRU>
P<STRU> newInstance (Symbol nameID ="");
lib::P<STRU> newInstance (Symbol nameID ="");
template<class STRU>
P<STRU> made4fake (Query<STRU> const& query); ///< @warning to be removed in Alpha when using a real resolution engine /////TICKET #710
lib::P<STRU> made4fake (Query<STRU> const& query); ///< @warning to be removed in Alpha when using a real resolution engine /////TICKET #710
P<Pipe> newPipe (string pipeID, string streamID);
lib::P<Pipe> newPipe (string pipeID, string streamID);
};

View file

@ -80,7 +80,7 @@ namespace asset {
class Timeline;
typedef P<Timeline> PTimeline;
typedef lib::P<Timeline> PTimeline;
/**

View file

@ -75,7 +75,7 @@ namespace asset {
class Viewer;
typedef P<Viewer> PViewer;
typedef lib::P<Viewer> PViewer;
/**

View file

@ -117,7 +117,7 @@ namespace asset {
DB::Lock guard(&registry);
//////////////////////////////////////////////////////////TICKET #840 handle duplicate Registrations
P<KIND> smart_ptr (obj, &destroy);
lib::P<KIND> smart_ptr (obj, &destroy);
registry.put (asset_id, smart_ptr);
return asset_id;
@ -129,10 +129,10 @@ namespace asset {
* of the stored object differs and can't be casted.
*/
template<class KIND>
P<KIND>
lib::P<KIND>
AssetManager::getAsset (const ID<KIND>& id)
{
if (P<KIND> obj = registry.get (id))
if (lib::P<KIND> obj = registry.get (id))
return obj;
else
if (known (id)) // provide Ident tuple of existing Asset
@ -148,7 +148,7 @@ namespace asset {
* is explicitly given by type KIND.
*/
template<class KIND>
P<KIND>
lib::P<KIND>
AssetManager::wrap (const KIND& asset)
{
ENSURE (instance().known(asset.id),
@ -250,6 +250,7 @@ namespace asset {
namespace proc {
namespace asset {
using lib::P;
template ID<Asset> AssetManager::reg (Asset* obj, const Asset::Ident& idi);

View file

@ -81,11 +81,11 @@ namespace asset {
/** retrieve the registered smart-ptr for any asset */
template<class KIND>
static P<KIND> wrap (const KIND& asset);
static lib::P<KIND> wrap (const KIND& asset);
/** find and return corresponding object */
template<class KIND>
P<KIND> getAsset (const ID<KIND>& id);
lib::P<KIND> getAsset (const ID<KIND>& id);
/** @return true if the given id is registered in the internal asset DB */

View file

@ -71,7 +71,7 @@ namespace query {
using proc::asset::PSequence;
using proc::mobject::session::Fork;
using proc::mobject::session::PFork;
using PFork = lib::P<Fork>;
template PPipe DefsManager::operator() (Query<Pipe> const&);
template PProcPatt DefsManager::operator() (Query<const ProcPatt> const&);

View file

@ -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 <boost/operators.hpp>
@ -181,19 +180,8 @@ namespace control {
return undo_ && capture_ && isCaptured_;
}
operator std::string() const
{
if (!undo_ || !capture_)
return "·noUNDO·";
if (!isCaptured_)
return "<mem:missing>";
return "<"
+ util::str(memento_, "mem: ", "·memento·")
+ ">";
}
/** for diagnostics: include format-util.hpp */
operator std::string() const;
/// Supporting equality comparisons...
@ -212,6 +200,24 @@ namespace control {
};
template<typename SIG, typename MEM>
MementoTie<SIG,MEM>::operator std::string() const
{
if (!undo_ || !capture_)
return "·noUNDO·";
if (!isCaptured_)
return "<mem:missing>";
return "<"
#ifdef LIB_FORMAT_UTIL_H
+ util::str(memento_, "mem: ", "·memento·")
#else
+ std::string("memento")
#endif
+ ">";
}
}} // namespace proc::control

View file

@ -52,7 +52,6 @@
namespace proc {
namespace control {
// using lib::P;
// using std::string;
// using boost::format;

View file

@ -181,8 +181,6 @@ namespace engine{
/* ===== Quality-of-Service ===== */
EngineService::Quality::~Quality() { } // emit vtables here...
enum CalcType {
PLAYBACK,
RENDER,

View file

@ -121,7 +121,7 @@ namespace engine{
class Quality
{
public:
virtual ~Quality(); ///< this is an Interface
virtual ~Quality() { }; ///< this is an Interface
};

View file

@ -68,7 +68,6 @@ namespace mobject {
namespace builder {
using lib::P;
/**
* Policy invoking an catch-all function for processing
@ -146,10 +145,10 @@ namespace mobject {
}
template<class TAR>
P<TAR>
lib::P<TAR>
getPtr ()
{
P<TAR>* pP = currentWrapper_.get<P<TAR>*>();
lib::P<TAR>* pP = currentWrapper_.get<lib::P<TAR>*>();
ENSURE (pP, "wrong target type when invoking %s", __PRETTY_FUNCTION__);
return *pP;
}

View file

@ -87,7 +87,7 @@ namespace builder {
public:
/** @internal record to describe a model port */
struct ModelPortDescriptor;
class ModelPortDescriptor;
static void shutdown ();

View file

@ -46,7 +46,6 @@ namespace proc {
namespace mobject {
using std::string;
using lib::P;
//NOBUG_DECLARE_FLAG (mobjectmem);

View file

@ -65,8 +65,8 @@
namespace proc {
namespace asset {
class Timeline; typedef P<Timeline> PTimeline;
class Sequence; typedef P<Sequence> PSequence;
class Timeline; typedef lib::P<Timeline> PTimeline;
class Sequence; typedef lib::P<Sequence> PSequence;
}
namespace mobject {

View file

@ -32,7 +32,7 @@ namespace proc {
namespace asset {
class Sequence;
typedef P<Sequence> PSequence;
typedef lib::P<Sequence> PSequence;
}

View file

@ -32,7 +32,7 @@ namespace proc {
namespace asset {
class Pipe;
typedef P<Pipe> PPipe;
typedef lib::P<Pipe> PPipe;
}

View file

@ -26,6 +26,7 @@
#include "proc/mobject/session/abstractmo.hpp"
#include "lib/time/timevalue.hpp"
#include "lib/p.hpp"
namespace proc {
@ -40,8 +41,12 @@ namespace session {
using asset::Media;
using lib::time::TimeVar;
typedef P<Media> PMedia;
typedef P<asset::Clip> PClipAsset;
class Clip;
using PMedia = lib::P<Media>;
using PClipAsset = lib::P<asset::Clip>;
using PClip = Placement<Clip>;
/**
@ -103,7 +108,6 @@ namespace session {
};
typedef Placement<Clip> PClipMO;

View file

@ -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"
@ -34,25 +34,13 @@ namespace proc {
namespace mobject {
namespace session { //////////////////////////////////////////////////////TICKET #637
using lib::P;
using lib::time::Time;
class Fork;
typedef P<Fork> PFork;
}}
namespace asset { //////////////////////////////////////////////////////TICKET #637
typedef EntryID<mobject::session::Fork> ForkID;
}
using ForkID = lib::idi::EntryID<Fork>;
namespace mobject {
namespace session {
using asset::ForkID;
//////////////////////////////////////////////////////TICKET #646

View file

@ -40,8 +40,8 @@ namespace session {
using asset::Media;
using lib::time::TimeVar;
typedef P<Media> PMedia;
typedef P<asset::Clip> PClipAsset;
typedef lib::P<Media> PMedia;
typedef lib::P<asset::Clip> PClipAsset;
/**
@ -84,8 +84,6 @@ namespace session {
};
typedef Placement<Clip> PClipMO;
}}} // namespace proc::mobject::session

View file

@ -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"
@ -42,7 +42,7 @@ namespace asset {
class Effect;
class Sequence;
typedef P<Sequence> PSequence;
typedef lib::P<Sequence> PSequence;
}
@ -70,7 +70,7 @@ namespace session {
Placement<Root> operator() (lumiera::query::DefsManager&);
Placement<Clip> operator() (asset::Clip const&, asset::Media const&);
Placement<Clip> operator() (asset::Clip const&, vector<asset::Media const*>);
Placement<Fork> operator() (asset::EntryID<Fork> const&);
Placement<Fork> operator() (lib::idi::EntryID<Fork> const&);
Placement<Effect> operator() (asset::Effect const&);
Placement<Label> operator() (lib::Symbol);
Placement<Binding>operator() (asset::PSequence const&);

View file

@ -175,7 +175,7 @@ namespace session {
{
typedef typename WrapReturn<TY>::Wrapper Ret;
public:
/** (dummy) implementation of the QueryHandler interface */
virtual bool
resolve (Ret& solution, Query<TY> const& q)

View file

@ -36,7 +36,7 @@
#include "lib/symbol.hpp"
//#include "common/query.hpp"
#include "proc/asset/entry-id.hpp"
#include "lib/idi/entry-id.hpp"
#include <boost/noncopyable.hpp>
@ -46,6 +46,7 @@ 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"
/**
@ -74,7 +75,7 @@ namespace proc {
class ImplFacade;
class ImplConstraint;
typedef asset::EntryID<StreamType> ID;
typedef lib::idi::EntryID<StreamType> ID;
Prototype const& prototype;
@ -192,4 +193,10 @@ namespace proc {
} // namespace proc
#endif
namespace lumiera {
using proc::StreamType;
}
#endif /*PROC_STREAMTYPE_H*/

View file

@ -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

View file

@ -34,14 +34,14 @@ END
TEST "plain entry-ID" EntryID_test <<END
out: ID<..proc.asset.+Dummy.>-..proc.asset.+Dummy.\.001
out: ID<..proc.asset.+Dummy.>-strange
out: ID<..proc.asset.+Dummy.>-..proc.asset.+Dummy.\.002
out: ID<fork>-fork\.001
out: ID<fork>-fork\.002
out: ID<fork>-special
out: sizeof\( ..proc.asset.+EntryID.+mobject.+session.+Fork.+ \) =
out: sizeof\( ..proc.asset.+BareEntryID. \) =
out-lit: ID<Dummy>-Dummy.001
out-lit: ID<Dummy>-strange
out-lit: ID<Dummy>-Dummy.002
out-lit: ID<Fork>-Fork.001
out-lit: ID<Fork>-Fork.002
out-lit: ID<Fork>-special
out: sizeof\( ..lib.idi.EntryID.+mobject.+session.+Fork.+ \) =
out: sizeof\( ..lib.idi.+BareEntryID. \) =
return: 0
END

View file

@ -50,7 +50,7 @@ namespace test_format {
class StreamTypeBasics_test : public Test
{
virtual void
run (Arg arg)
run (Arg)
{
ImplType iType = buildImplType ();
basicImplTypeProperties (iType);

View file

@ -76,7 +76,7 @@ namespace asset {
template<class CHI, class PAR>
inline bool
dependencyCheck (P<CHI> child, P<PAR> parent)
dependencyCheck (lib::P<CHI> child, lib::P<PAR> parent)
{
return (child == parent)
|| (0 < child->getParents().size()

View file

@ -153,8 +153,8 @@ namespace test {
void dependProcPatt(string pID)
{
typedef P<Pipe> PPipe; /////TODO: transition to P<>
typedef P<const ProcPatt> PProcPatt;
typedef lib::P<Pipe> PPipe; /////TODO: transition to P<>
typedef lib::P<const ProcPatt> PProcPatt;
PPipe thePipe = Pipe::query ("pipe("+pID+")");
CHECK (thePipe);

View file

@ -65,7 +65,7 @@ namespace test {
////////////////////////////////////TICKET #589
typedef P<Media> PM; /////TODO: transition to P<>
typedef lib::P<Media> PM; /////TODO: transition to P<>
/** @test Creating and automatically registering Asset instances.
* Re-Retrieving the newly created objects from AssetManager.

View file

@ -198,8 +198,8 @@ namespace test {
Depend4Test<backend::test::MediaAccessMock> within_this_scope;
// -----Media and Clip--------------------------------
typedef P<Media> PM;
typedef P<Clip> PC;
typedef lib::P<Media> PM;
typedef lib::P<Clip> PC;
PM mm = asset::Media::create("test-1", VIDEO);
PC cc = mm->createClip()->findClipAsset();
CHECK (dependencyCheck (cc,mm));

View file

@ -24,7 +24,8 @@
#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"
#include "lib/meta/trait-special.hpp"
@ -47,9 +48,9 @@ using std::endl;
namespace proc {
namespace asset{
namespace test {
namespace lib {
namespace idi {
namespace test{
using lumiera::error::LUMIERA_ERROR_WRONG_TYPE;
@ -57,11 +58,11 @@ namespace test {
struct Dummy { };
typedef EntryID<Dummy> DummyID;
typedef EntryID<mobject::session::Fork> ForkID;
typedef EntryID<mobject::session::Clip> ClipID;
}
using DummyID = EntryID<Dummy>;
using ForkID = EntryID<proc::mobject::session::Fork>;
using ClipID = EntryID<proc::mobject::session::Clip>;
@ -132,14 +133,19 @@ namespace test {
void
checkBasicProperties ()
{
using proc::asset::Asset;
using proc::asset::STRUCT;
using proc::asset::Category;
using proc::asset::idi::getAssetIdent;
ForkID tID(" test ⚡ ☠ ☭ ⚡ track ");
CHECK (tID.getIdent() == Asset::Ident("test_track", Category(STRUCT,"forks"), "lumi", 0));
CHECK (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() == getAssetIdent(tID).name);
CHECK (getAssetIdent(ForkID()).category == Category (STRUCT,"forks"));
CHECK (getAssetIdent(ClipID()).category == Category (STRUCT,"clips"));
ClipID cID2,cID3;
CHECK (cID2.getSym() < cID3.getSym());
@ -153,7 +159,7 @@ namespace test {
tID = arbitrary;
CHECK (tID.getHash() == arbitrary.getHash());
CHECK (tID.getSym() == arbitrary.getSym());
CHECK (tID.getIdent()== arbitrary.getIdent());
CHECK (getAssetIdent(tID)== getAssetIdent(arbitrary));
}
cout << showSizeof<ForkID>() << endl;
@ -199,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 mobject::session::Fork;
using mobject::session::Clip;
ForkID tIDnew = bIDt.recast<Fork>();
using proc::mobject::session::Fork;
using proc::mobject::session::Clip;
ForkID tIDnew = bIDf.recast<Fork>();
ClipID cIDnew = bIDc.recast<Clip>();
CHECK (tIDnew == tID);
CHECK (tIDnew == fID);
CHECK (cIDnew == cID);
VERIFY_ERROR (WRONG_TYPE, bIDt.recast<Clip>());
VERIFY_ERROR (WRONG_TYPE, bIDf.recast<Clip>());
VERIFY_ERROR (WRONG_TYPE, bIDc.recast<Fork>());
VERIFY_ERROR (WRONG_TYPE, bIDc.recast<Dummy>());
VERIFY_ERROR (WRONG_TYPE, bIDt.recast<Dummy>());
VERIFY_ERROR (WRONG_TYPE, bIDf.recast<Dummy>());
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"));
}
@ -252,20 +258,25 @@ namespace test {
typedef std::unordered_map<DummyID, string, DummyID::UseEmbeddedHash> 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());
}
@ -287,7 +298,7 @@ namespace test {
/** Register this test class... */
LAUNCHER (EntryID_test, "unit asset");
LAUNCHER (EntryID_test, "unit common");
}}} // namespace proc::asset::test
}}} // namespace lib::idi::test

View file

@ -54,8 +54,8 @@ namespace test {
*/
class MakeClip_test : public Test
{
typedef P<asset::Media> PM;
typedef asset::Media::PClipMO PC;
typedef lib::P<asset::Media> PM;
typedef asset::Media::PClip PC;
virtual void run (Arg)
{

View file

@ -48,7 +48,6 @@ namespace test {
using namespace lib::time;
typedef Builder<TimeGrid> GridBuilder;
typedef EntryID<TimeGrid> GridID;
namespace { // Test definitions...

View file

@ -79,7 +79,7 @@ namespace test {
* within AssetManager by the Asset base class ctor
*/
template<class A>
P<TestAsset<A> >
lib::P<TestAsset<A> >
TestAsset<A>::ptrFromThis ()
{
return static_pointer_cast<TestAsset<A>,Asset>
@ -111,8 +111,8 @@ namespace test {
template TestAsset<Asset>::TestAsset (PAsset& pRef);
template TestAsset<Unknown>::TestAsset (PAsset& pRef);
template P<TestAsset<Asset> > TestAsset<Asset>::ptrFromThis ();
template P<TestAsset<Unknown> > TestAsset<Unknown>::ptrFromThis ();
template lib::P<TestAsset<Asset> > TestAsset<Asset>::ptrFromThis ();
template lib::P<TestAsset<Unknown> > TestAsset<Unknown>::ptrFromThis ();

View file

@ -53,7 +53,7 @@ namespace test {
static void deleter (TestAsset<A>* aa) { delete aa; }
public:
typedef P<TestAsset<A> > PA;
typedef lib::P<TestAsset<A> > PA;
static PA create () { return (new TestAsset<A> )->ptrFromThis(); }
static PA create (PAsset& pRef) { return (new TestAsset<A> (pRef))->ptrFromThis(); }

View file

@ -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"
@ -84,14 +84,14 @@ namespace query {
namespace proc {
namespace asset{
namespace test {
namespace lib {
namespace idi {
namespace test{
using mobject::session::test::DummyEntity;
using mobject::session::test::PDum;
using proc::mobject::session::test::DummyEntity;
using proc::mobject::session::test::PDum;
typedef EntryID<DummyEntity> DummyID;
using DummyID = EntryID<DummyEntity>;
namespace { // Test definitions...
@ -244,4 +244,4 @@ namespace test {
LAUNCHER (TypedID_test, "unit asset");
}}} // namespace proc::asset::test
}}} // namespace lib::idi::test

View file

@ -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

View file

@ -25,14 +25,25 @@
#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 <utility>
//#include <string>
//#include <vector>
#include <cmath>
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;
//using std::vector;
//using std::swap;
using std::fabs;
namespace lib {
@ -40,6 +51,8 @@ namespace diff{
namespace test{
// using lumiera::error::LUMIERA_ERROR_LOGIC;
using error::LUMIERA_ERROR_WRONG_TYPE;
using error::LUMIERA_ERROR_BOTTOM_VALUE;
namespace {//Test fixture....
@ -58,6 +71,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<GenNode>
* 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
@ -69,9 +94,9 @@ namespace test{
run (Arg)
{
simpleUsage();
verifySnapshot();
objectShortcut();
symbolReference();
sequenceIteration();
duplicateDetection();
copy_and_move();
}
@ -79,30 +104,168 @@ namespace test{
void
simpleUsage()
{
// can build from the supported value types
GenNode n1(42);
CHECK (42 == n1.data.get<int>());
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<double>()) < 1e-5 );
CHECK (n2.isNamed());
CHECK ("π" == n2.idi.getSym());
CHECK ("π" == name(n2));
// is a copyable value
GenNode n11(n1);
CHECK (n1 == n11);
CHECK (42 == n11.data.get<int>());
// is assignable with compatible payload value
n11.data = 24;
CHECK (n1 != n11);
CHECK (24 == n11.data.get<int>());
CHECK (42 == n1.data.get<int>());
// 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({GenNode("type", "spam"), GenNode("ham", "eggs")}));
CHECK ("spam" == n3.data.get<Rec>().getType());
CHECK ("eggs" == n3.data.get<Rec>().get("ham").data.get<string>());
CHECK ("ham" == n3.data.get<Rec>().get("ham").idi.getSym());
CHECK (n3.data.get<Rec>().get("ham").isNamed());
CHECK (!n3.isNamed());
}
void
verifySnapshot()
objectShortcut()
{
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<Rec>()));
CHECK ("NIL" == o0.data.get<Rec>().getType());
CHECK (o1.isNamed());
CHECK ("νόμος" == o1.idi.getSym());
CHECK (isnil(o1.data.get<Rec>()));
CHECK (!o2.isNamed());
CHECK ("spam" == o0.data.get<Rec>().getType());
CHECK (isnil(o2.data.get<Rec>()));
CHECK (o3.isNamed());
CHECK ("λόγος" == o3.idi.getSym());
CHECK ("NIL" == o3.data.get<Rec>().getType());
CHECK (GenNode("Ψ", int64_t(42)) == o3.data.get<Rec>().get("Ψ"));
CHECK (42L == o3.data.get<Rec>().get("Ψ").data.get<int64_t>());
CHECK (1e-7 > fabs (3.14159265 - o3.data.get<Rec>().get("π").data.get<double>()));
LuidH luid;
//Demonstration: object builder is based on the mutator mechanism for Records...
auto o4 = Rec::Mutator(o2.data.get<Rec>()) // ...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());
CHECK ("spam" == o4.data.get<Rec>().getType()); // this was "inherited" from o2
auto scope = o4.data.get<Rec>().scope();
CHECK (!isnil(scope));
CHECK (GenNode("τ", Time(1,2,3,4)) == *scope);
++scope;
CHECK (GenNode(char('*')) == *scope);
++scope;
CHECK ("" == scope->data.get<string>());
++scope;
CHECK (luid == scope->data.get<LuidH>());
++scope;
CHECK (Time(920,0) == scope->data.get<TimeSpan>().end());
++scope;
auto spam = *scope;
CHECK (!++scope);
CHECK ("ham" == spam.data.get<Rec>().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<Rec>()));
}
void
symbolReference()
{
GenNode ham = MakeRec().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!
Ref ref1("egg bacon sausage and spam");
Ref ref2(ham);
CHECK (ref1.idi == ham.idi);
CHECK (ref2.idi == ham.idi);
// can stand-in for the original Record...
CHECK (isSameObject (ham, ref2.data.get<Rec>()));
VERIFY_ERROR (BOTTOM_VALUE, ref1.data.get<Rec>());
RecRef rr1 = ref1.data.get<RecRef>();
RecRef rr2 = ref2.data.get<RecRef>();
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<RecRef>()));
CHECK (isnil (Ref::THIS.data.get<RecRef>()));
CHECK (isnil (Ref::CHILD.data.get<RecRef>()));
CHECK (isnil (Ref::ATTRIBS.data.get<RecRef>()));
}
void
sequenceIteration()
{
}
void
duplicateDetection()
{
UNIMPLEMENTED ("wtf");
}
void
copy_and_move()
{
UNIMPLEMENTED ("need to check that?");
}
};

View file

@ -23,15 +23,23 @@
#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?
#include <iostream>
//#include <utility>
//#include <string>
//#include <vector>
#include <string>
#include <vector>
//using std::string;
//using std::vector;
using std::string;
using util::isSameObject;
using util::isnil;
using std::vector;
//using std::swap;
using std::cout;
using std::endl;
namespace lib {
@ -39,9 +47,38 @@ namespace diff{
namespace test{
// using lumiera::error::LUMIERA_ERROR_LOGIC;
using lumiera::error::LUMIERA_ERROR_INVALID;
using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE;
namespace {//Test fixture....
using Seq = vector<string>;
using RecS = Record<string>;
template<class IT>
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<class X>
inline Seq
strings (std::initializer_list<X> const& con)
{
Seq collected;
for (auto elm : con)
collected.push_back(elm);
return collected;
}
}//(End)Test fixture
@ -52,11 +89,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 +103,230 @@ namespace test{
run (Arg)
{
simpleUsage();
verifySnapshot();
sequenceIteration();
duplicateDetection();
verifyCreation();
verifyMutations();
copy_and_move();
equality();
wrapRef();
}
void
simpleUsage()
{
RecS enterprise("starship"
, 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");
CHECK (enterprise.get("Registry") == "NCC-1701-D");
CHECK (enterprise.hasAttribute("Owner"));
CHECK (!enterprise.hasAttribute("owner"));
CHECK (!enterprise.hasAttribute("Owner "));
CHECK (enterprise.contains("Data"));
CHECK (!enterprise.contains("Woof"));
CHECK (util::contains (enterprise, "Worf"));
VERIFY_ERROR (INVALID, enterprise.get("warp10"));
cout << "enterprise = " << string(enterprise)<<endl;
for (string elm : enterprise) cout << elm<<endl;
for (string mbr : enterprise.scope()) cout << mbr<<endl;
for (string att : enterprise.attribs()) cout << att<<endl;
}
void
verifySnapshot()
verifyCreation()
{
}
RecS nil;
CHECK (isnil(nil));
CHECK ("NIL" == nil.getType());
CHECK (!nil.begin());
CHECK (nil.begin() == nil.end());
void
sequenceIteration()
{
}
RecS untyped({"x"});
CHECK (!isnil(untyped));
CHECK ("NIL" == untyped.getType());
CHECK (Seq{"x"} == contents(untyped));
CHECK (Seq{"x"} == contents(untyped.scope()));
CHECK (isnil (untyped.attribs()));
RecS untyped2({"x=y", "z"});
CHECK (!isnil(untyped2));
CHECK ("NIL" == untyped2.getType());
CHECK (Seq({"x=y", "z"}) == contents(untyped2));
CHECK (Seq{"x"} == contents (untyped2.keys()));
CHECK (Seq{"y"} == contents (untyped2.vals()));
CHECK (Seq{"z"} == contents (untyped2.scope()));
void
duplicateDetection()
{
RecS something({"type=thing", "a=1", "b=2", "c", "d"});
CHECK (!isnil(something));
CHECK ("thing" == something.getType());
CHECK (Seq({"type=thing", "a=1", "b=2", "c", "d"}) == contents(something));
CHECK (Seq({"a", "b"}) == contents (something.keys()));
CHECK (Seq({"1", "2"}) == contents (something.vals()));
CHECK (Seq({"c", "d"}) == contents (something.scope()));
}
void
copy_and_move()
{
RecS a({"type=thing", "a=1", "b=2", "c", "d"});
RecS b(a);
CHECK (a.getType() == b.getType());
CHECK (contents(a) == contents(b));
CHECK (contents(a.attribs()) == contents(b.attribs()));
CHECK (!isSameObject (a.get("a"), b.get("a")));
CHECK (!isSameObject (*a.scope(), *b.scope()));
string const& c = *b.scope();
CHECK ("c" == c);
RecS bb;
CHECK (isnil(bb));
bb = move(b);
CHECK ("b" == bb.get("b"));
CHECK (isSameObject(c, *bb.scope()));
swap (a, bb);
CHECK (!isSameObject(c, *bb.scope()));
CHECK ( isSameObject(c, *a.scope()));
CHECK (isnil (b));
b = bb;
CHECK (!isnil (b));
CHECK (!isSameObject(b.get("a"), bb.get("a")));
CHECK (!isSameObject(*b.scope(), *bb.scope()));
}
void
equality()
{
RecS a({"a"});
RecS aa({"a","aa"});
RecS aaa({"a","a"});
RecS ax({"type=a","a"});
RecS ay({"a=a","a"});
CHECK (a != aa); CHECK (aa != a);
CHECK (aa != aaa); CHECK (aaa != aa);
CHECK (a != aaa); CHECK (aaa != a);
CHECK (a != ax); CHECK (ax != a);
CHECK (a != ay); CHECK (ay != a);
CHECK (ax != ay); CHECK (ay != ax);
CHECK (aaa != ay); CHECK (ay != aaa);
RecS a2({"a","aa"});
CHECK (aa == a2); CHECK (a2 == aa);
RecS o1("oo", strings({"a=α", "b=β"}), strings({"γ", "δ", "ε"}));
RecS o2({"type=oo", "a = α", "b = β", "γ", "δ", "ε"});
RecS o3({"type=oO", "a = α", "b = β", "γ", "δ", "ε"});
RecS o4({"type=oo", "a = α", "b = β", "c=γ", "δ", "ε"});
RecS o5({"type=oo", "a = α", "b = β", "γ", "ε", "δ"});
RecS o6({"type=oo", "a = α", "b = β", "γ", "δ"});
CHECK (o1 == o2); CHECK (o2 == o1);
CHECK (o2 != o3); CHECK (o3 != o2);
CHECK (o3 != o4); CHECK (o4 != o3);
CHECK (o4 != o5); CHECK (o5 != o4);
CHECK (o5 != o6); CHECK (o6 != o5);
CHECK (o1 != o3); CHECK (o3 != o1);
CHECK (o1 != o4); CHECK (o4 != o1);
CHECK (o1 != o5); CHECK (o5 != o1);
CHECK (o1 != o6); CHECK (o6 != o1);
CHECK (o2 != o4); CHECK (o4 != o2);
CHECK (o2 != o5); CHECK (o5 != o2);
CHECK (o2 != o6); CHECK (o6 != o2);
CHECK (o3 != o5); CHECK (o5 != o3);
CHECK (o3 != o6); CHECK (o6 != o3);
CHECK (o4 != o6); CHECK (o6 != o4);
RecS o7({"type=oo", "b = β", "a = α", "γ", "δ", "ε"});
CHECK (o2 != o7); CHECK (o7 != o2);
// ideally, they would be equal, but this would require
// a way more expensive implementation
}
void
verifyMutations()
{
RecS a;
CHECK (isnil (a));
CHECK ("NIL" == a.getType());
RecS::Mutator mut(a);
mut.setType("u");
mut.appendChild("a");
mut.set("a", "1");
RecS aa(mut);
CHECK (a != aa);
CHECK ("u" == aa.getType());
CHECK (Seq({"type=u", "a=1", "a"}) == contents(aa));
CHECK (Seq({"a"}) == contents (aa.keys()));
CHECK (Seq({"1"}) == contents (aa.vals()));
CHECK (Seq({"a"}) == contents (aa.scope()));
CHECK (mut == aa);
mut.prependChild("");
mut.set("b", "β");
mut.set("a", "α");
CHECK (mut != aa);
mut.replace(a);
CHECK (isnil (mut));
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<string> empty;
CHECK (bool(empty) == false);
CHECK (nullptr == empty.get());
VERIFY_ERROR (BOTTOM_VALUE, empty.operator RecS&() );
RecordRef<string> ref(oo);
CHECK (ref);
CHECK (ref.get() == &oo);
RecS& oor = ref;
CHECK ("🌰" == oor.getType());
CHECK (oor.get("") == "saturn");
// are copyable and assignable
RecordRef<string> 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());
}
};

View file

@ -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()
{

View file

@ -55,7 +55,7 @@ namespace test{
/***********************************************************************************//**
* @test Demonstrate how to build, discover and traverse tree-like data representation.
* @test Demonstrate how to build, explore and traverse tree-like data representation.
*
* @see IndexTable
* @see DiffListApplication_test

View file

@ -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 <iostream>
#include <vector>
#include <string>
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<double> dubious;
for (uint i=0; i<10; ++i)
dubious.push_back(1.1*i);
std::function<AutoCounter(double)> justCount = [](double d){ return AutoCounter(d); };
cout << join(dubious, "--+--") << endl;
cout << join(transformIterator(eachElm(dubious)
,justCount)) << endl;
}
};
LAUNCHER (FormatHelper_test, "unit common");

View file

@ -58,9 +58,14 @@ namespace test{
run (Arg)
{
demonstrate_boost_hash_weakness();
verify_Knuth_workaround();
}
typedef boost::hash<string> BoostStringHasher;
typedef std::map<size_t, string> 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<string> BoostStringHasher;
typedef std::map<size_t, string> 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<string> (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 <<endl;
}
hashValues[hashVal] = candidate;
}
CHECK (!collisions, "the Knuth trick failed to spread our hash values evenly enough, what a shame...");
}
};

View file

@ -27,6 +27,7 @@
#include "lib/util-foreach.hpp"
#include "lib/iter-adapter.hpp"
#include "lib/iter-adapter-ptr-deref.hpp"
#include <boost/lexical_cast.hpp>
#include <iostream>
@ -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;
} }
};

View file

@ -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.</pre>
</div>
<div title="ExternalTreeDescription" creator="Ichthyostega" modifier="Ichthyostega" created="201505081647" modified="201505090059" tags="Concepts Model design spec draft" changecount="19">
<div title="ExternalTreeDescription" creator="Ichthyostega" modifier="Ichthyostega" created="201505081647" modified="201506121606" tags="Concepts Model design spec draft" changecount="27">
<pre>//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.
&amp;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 &quot;join&quot; 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 &quot;contents&quot;, the attributes or the children?
*will the function get to see {{{Record}}} elements, or will it immediately see the &quot;contents&quot;, 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 {{{&quot;type&quot;}}}. 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 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 &quot;supply&quot; 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 &quot;supply&quot; a list of children for symbolic representation as they see fit.
</pre>
</div>
<div title="Factories" modifier="Ichthyostega" created="200708100401" modified="201310132317" tags="def Concepts" changecount="6">
@ -2219,7 +2219,7 @@ Of course, we can place other ~MObjects relative to some fork (that's the main r
&amp;rarr; [[Anatomy of the high-level model|HighLevelModel]]
</pre>
</div>
<div title="ForwardIterator" modifier="Ichthyostega" created="200910312114" modified="200912190027" tags="Concepts def spec">
<div title="ForwardIterator" modifier="Ichthyostega" created="200910312114" modified="201507042115" tags="Concepts def spec" changecount="1">
<pre>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 &amp;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.
@ -2305,13 +2316,17 @@ For this Lumiera design, we could consider making GOP just another raw media dat
&amp;rarr;see in [[Wikipedia|http://en.wikipedia.org/wiki/Group_of_pictures]]
</pre>
</div>
<div title="GenNode" creator="Ichthyostega" modifier="Ichthyostega" created="201501171413" modified="201505081546" tags="Model GuiIntegration GuiPattern def draft" changecount="11">
<div title="GenNode" creator="Ichthyostega" modifier="Ichthyostega" created="201501171413" modified="201507031718" tags="Model GuiIntegration GuiPattern def draft" changecount="13">
<pre>//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 &quot;outside&quot;, 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 &quot;outside&quot;, 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 &quot;objects&quot; 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 &quot;openness&quot;. 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 &quot;open&quot; 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 &quot;within&quot; a ~GenNode.</pre>
@ -2433,7 +2448,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.
</pre>
</div>
<div title="GuiModelUpdate" creator="Ichthyostega" modifier="Ichthyostega" created="201410250121" modified="201503221633" tags="GuiIntegration GuiPattern design decision discuss draft" changecount="30">
<div title="GuiModelUpdate" creator="Ichthyostega" modifier="Ichthyostega" created="201410250121" modified="201506040022" tags="GuiIntegration GuiPattern design decision discuss draft" changecount="31">
<pre>Considerations regarding the [[structure of custom timeline widgets|GuiTimelineWidgetStructure]] highlight again the necessity of a clean separation of concerns and an &quot;open closed design&quot;. 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.
@ -2468,6 +2483,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 &quot;raw&quot; content is represented //as node//
@ -7503,7 +7521,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]]
</pre>
</div>
<div title="TimeMutation" modifier="Ichthyostega" created="201101231344" modified="201105261700" tags="spec discuss draft">
<div title="TimeMutation" modifier="Ichthyostega" created="201101231344" modified="201506032222" tags="spec discuss draft" changecount="1">
<pre>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
@ -7579,7 +7597,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)
@ -7929,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
</pre>
</div>
<div title="TreeDiffImplementation" creator="Ichthyostega" modifier="Ichthyostega" created="201412210015" modified="201504290114" tags="Model GuiPattern design draft" changecount="31">
<div title="TreeDiffImplementation" creator="Ichthyostega" modifier="Ichthyostega" created="201412210015" modified="201507041449" tags="Model GuiPattern design draft" changecount="38">
<pre>//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.
@ -7982,6 +8000,18 @@ 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? &amp;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? &amp;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
&quot;Objects&quot; 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
@ -7994,7 +8024,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 &amp;rarr; TreeMutator.
</pre>
</div>
<div title="TreeDiffModel" creator="Ichthyostega" modifier="Ichthyostega" created="201410270313" modified="201505310130" tags="Model GuiPattern spec draft" changecount="54">
<div title="TreeDiffModel" creator="Ichthyostega" modifier="Ichthyostega" created="201410270313" modified="201506072246" tags="Model GuiPattern spec draft" changecount="61">
<pre>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.
@ -8041,19 +8071,30 @@ 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 &quot;search for the term a~~5~~ and insert it at curent position&quot;. The more obvious rendering -- &quot;push term a~~4~~ back by +1 steps&quot; -- 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 &quot;object&quot; 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&lt;GenNode&gt;}}}. 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 &quot;diff language&quot;, 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.
</pre>
</div>
<div title="TreeMutator" creator="Ichthyostega" modifier="Ichthyostega" created="201503292115" modified="201505310130" tags="Model Concepts GuiPattern design draft" changecount="50">
<div title="TreeMutator" creator="Ichthyostega" modifier="Ichthyostega" created="201503292115" modified="201505312250" tags="Model Concepts GuiPattern design draft" changecount="52">
<pre>The TreeMutator is an intermediary to translate a generic structure pattern into heterogeneous local invocation sequences.
!Motivation
@ -8106,7 +8147,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
@ -8118,7 +8159,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(&quot;Fork&quot;), [&amp;](string type, string id) {
ForkType kind = determineForkkType(type);
ForkType kind = determineForkType(type);
this.forks_.push_back(MyForkImpl(kind, id);
})
.mutateChild(&quot;Fork&quot;), [&amp;](string id) {

1088
wiki/thinkPad.ichthyo.mm Normal file

File diff suppressed because it is too large Load diff