2015-03-21 02:00:55 +01:00
|
|
|
/*
|
|
|
|
|
GEN-NODE.hpp - generic node element for tree like data representation
|
|
|
|
|
|
|
|
|
|
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 gen-node.hpp
|
|
|
|
|
** Generic building block for tree shaped (meta)data structures.
|
2015-05-02 01:11:39 +02:00
|
|
|
** A representation built from GenNode elements is intended to support
|
2015-06-08 01:58:39 +02:00
|
|
|
** (limited) introspection of data structures and exchange of mutations
|
|
|
|
|
** in the form of \link diff-language.hpp diff messages. \endlink
|
2015-03-21 02:00:55 +01:00
|
|
|
**
|
|
|
|
|
** Despite of the name, GenNode is \em not meant to be an universal
|
|
|
|
|
** data representation; rather it is limited to embody a fixed hard
|
2015-06-08 01:58:39 +02:00
|
|
|
** wired set of data types, able to stand-in for attributes
|
2015-05-02 01:11:39 +02:00
|
|
|
** and sub scope contents of the lumiera high-level data model.
|
2015-03-21 02:00:55 +01:00
|
|
|
**
|
|
|
|
|
** \par Anatomy of a GenNode
|
|
|
|
|
**
|
2015-06-04 19:26:45 +02:00
|
|
|
** GenNode is a polymorphic value with well defined identity and type.
|
2015-03-21 02:00:55 +01:00
|
|
|
** 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
|
|
|
|
|
** is an inline container and attachment point for payload data. Simple
|
|
|
|
|
** attribute values can be carried alongside, while more complex types
|
|
|
|
|
** or entities bound to a reference and registration system (e.g. Placement)
|
|
|
|
|
** 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,
|
2015-06-08 01:58:39 +02:00
|
|
|
** 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.
|
2015-03-21 02:00:55 +01:00
|
|
|
**
|
2015-03-21 19:23:41 +01:00
|
|
|
** \par Requirements
|
|
|
|
|
**
|
|
|
|
|
** GenNode elements are to be used in the diff detection and implementation.
|
|
|
|
|
** This implies some requirements for the (opaque) elements used in diff:
|
|
|
|
|
** - they need to support the notion of equality
|
|
|
|
|
** - we need to derive a key type for usage in index tables
|
2015-05-02 01:11:39 +02:00
|
|
|
** - this implies the necessity to support std::less comparisons for tree-maps
|
|
|
|
|
** - and the necessity to support hash code generation for unordered (hash)maps
|
|
|
|
|
** - 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
|
2015-03-21 19:23:41 +01:00
|
|
|
** - in addition, the tree diffing suggests a mechanism to re-gain the fully
|
2015-06-04 19:26:45 +02:00
|
|
|
** typed context, either based on some kind of embedded type tag, or
|
|
|
|
|
** alternatively by visitation and matching
|
2015-03-21 19:23:41 +01:00
|
|
|
** - finally, the handling of changes prompts us to support installation
|
|
|
|
|
** of a specifically typed <i>change handling closure</i>.
|
|
|
|
|
**
|
|
|
|
|
** \par monadic nature
|
|
|
|
|
**
|
|
|
|
|
** As suggested by the usage for representation of tree shaped data, we acknowledge
|
|
|
|
|
** that GenNode is a <b>Monad</b>. We support the basic operations \em construction
|
|
|
|
|
** and \em flatMap. To fit in with this generic processing pattern, the one element
|
|
|
|
|
** flavours of GenNode are considered the special case, while the collective flavours
|
|
|
|
|
** form the base case -- every GenNode can be iterated. The \em construction requirement
|
|
|
|
|
** suggests that GenNode may be created readily, just by wrapping any given and suitable
|
|
|
|
|
** element, thereby picking up the element's type. For sake of code organisation and
|
|
|
|
|
** dependency management, we solve this requirement with the help of a trait type,
|
|
|
|
|
** expecting the actual usage to supply the necessary specialisations on site.
|
|
|
|
|
**
|
2015-06-04 19:26:45 +02:00
|
|
|
** @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
|
|
|
|
|
**
|
2015-03-21 02:00:55 +01:00
|
|
|
** @see diff-index-table-test.cpp
|
|
|
|
|
** @see diff-list-generation-test.cpp
|
|
|
|
|
** @see DiffDetector
|
|
|
|
|
**
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef LIB_DIFF_GEN_NODE_H
|
|
|
|
|
#define LIB_DIFF_GEN_NODE_H
|
|
|
|
|
|
|
|
|
|
|
2015-05-02 01:11:39 +02:00
|
|
|
#include "lib/hash-indexed.hpp" ///< @warning needs to be first, see explanation in lib/hash-standard.hpp
|
|
|
|
|
|
2015-03-21 02:00:55 +01:00
|
|
|
#include "lib/error.hpp"
|
2015-05-02 01:11:39 +02:00
|
|
|
#include "lib/variant.hpp"
|
|
|
|
|
#include "lib/time/timevalue.hpp"
|
|
|
|
|
#include "lib/diff/record.hpp"
|
2015-07-03 04:13:16 +02:00
|
|
|
#include "lib/idi/entry-id.hpp"
|
2015-03-21 02:00:55 +01:00
|
|
|
//#include "lib/util.hpp"
|
|
|
|
|
//#include "lib/format-string.hpp"
|
2015-07-03 19:18:49 +02:00
|
|
|
//#include "lib/format-util.hpp"
|
2015-05-02 01:11:39 +02:00
|
|
|
#include "lib/variant.hpp"
|
|
|
|
|
#include "lib/util.hpp"
|
|
|
|
|
|
2015-03-21 02:00:55 +01:00
|
|
|
//#include <vector>
|
2015-05-02 01:11:39 +02:00
|
|
|
#include <string>
|
2015-03-21 02:00:55 +01:00
|
|
|
//#include <map>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
namespace diff{
|
|
|
|
|
|
|
|
|
|
namespace error = lumiera::error;
|
|
|
|
|
|
2015-05-02 01:11:39 +02:00
|
|
|
using std::string;
|
|
|
|
|
|
2015-08-16 00:16:30 +02:00
|
|
|
struct GenNode;
|
2015-05-02 01:11:39 +02:00
|
|
|
|
2015-08-17 02:40:57 +02:00
|
|
|
/** Define actual data storage and access types used */
|
|
|
|
|
template<>
|
|
|
|
|
struct RecordSetup<GenNode>
|
|
|
|
|
{
|
|
|
|
|
using Storage = std::vector<GenNode>;
|
|
|
|
|
using ElmIter = typename Storage::const_iterator;
|
|
|
|
|
|
|
|
|
|
/** using const reference data access
|
|
|
|
|
* relevant for handling large subtrees */
|
|
|
|
|
using Access = GenNode const&;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-06-08 01:58:39 +02:00
|
|
|
using Rec = Record<GenNode>;
|
2015-07-04 20:50:18 +02:00
|
|
|
using RecRef = RecordRef<GenNode>;
|
2015-07-04 14:55:36 +02:00
|
|
|
using MakeRec = Rec::Mutator;
|
2015-05-02 01:11:39 +02:00
|
|
|
using DataValues = meta::Types<int
|
|
|
|
|
,int64_t
|
|
|
|
|
,short
|
|
|
|
|
,char
|
|
|
|
|
,bool
|
|
|
|
|
,double
|
|
|
|
|
,string
|
|
|
|
|
,time::Time
|
2015-06-08 01:58:39 +02:00
|
|
|
,time::Offset
|
2015-05-02 01:11:39 +02:00
|
|
|
,time::Duration
|
|
|
|
|
,time::TimeSpan
|
|
|
|
|
,hash::LuidH
|
2015-07-04 20:50:18 +02:00
|
|
|
,RecRef
|
2015-06-08 01:58:39 +02:00
|
|
|
,Rec
|
2015-05-02 01:11:39 +02:00
|
|
|
>;
|
|
|
|
|
|
2015-03-21 02:00:55 +01:00
|
|
|
|
2015-05-02 01:11:39 +02:00
|
|
|
|
|
|
|
|
class DataCap
|
|
|
|
|
: public Variant<DataValues>
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
template<typename X>
|
2015-07-03 18:16:54 +02:00
|
|
|
DataCap(X&& x)
|
2015-05-02 01:11:39 +02:00
|
|
|
: Variant<DataValues>(std::forward<X>(x))
|
|
|
|
|
{ }
|
2015-07-03 04:13:16 +02:00
|
|
|
|
2015-07-03 18:08:28 +02:00
|
|
|
////////////////////////TICKET #963 Forwarding shadows copy operations -- generic solution??
|
2015-07-03 18:16:54 +02:00
|
|
|
DataCap(DataCap const&) =default;
|
|
|
|
|
DataCap(DataCap&&) =default;
|
|
|
|
|
DataCap(DataCap& o)
|
|
|
|
|
: DataCap((DataCap const&)o)
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
DataCap& operator= (DataCap const&) =default;
|
|
|
|
|
DataCap& operator= (DataCap&&) =default;
|
2015-08-28 16:12:04 +02:00
|
|
|
|
|
|
|
|
bool operator== (DataCap const&) const;
|
2015-05-02 01:11:39 +02:00
|
|
|
};
|
2015-03-21 02:00:55 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/** generic data element node within a tree */
|
2015-07-03 04:13:16 +02:00
|
|
|
struct GenNode
|
2015-03-21 02:00:55 +01:00
|
|
|
{
|
2015-07-03 04:13:16 +02:00
|
|
|
class ID
|
|
|
|
|
: public idi::BareEntryID
|
|
|
|
|
{
|
2015-08-16 00:16:30 +02:00
|
|
|
friend struct GenNode;
|
2015-07-03 04:13:16 +02:00
|
|
|
|
|
|
|
|
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
|
2015-07-03 19:18:49 +02:00
|
|
|
|
|
|
|
|
operator string() const
|
|
|
|
|
{
|
|
|
|
|
return "ID(\""+getSym()+"\")";
|
|
|
|
|
}
|
2015-07-03 04:13:16 +02:00
|
|
|
};
|
|
|
|
|
|
2015-07-03 18:21:46 +02:00
|
|
|
|
|
|
|
|
//------GenNode Data fields---
|
|
|
|
|
|
2015-07-03 04:13:16 +02:00
|
|
|
ID idi;
|
|
|
|
|
DataCap data;
|
|
|
|
|
|
2015-07-03 18:08:28 +02:00
|
|
|
|
2015-07-03 04:13:16 +02:00
|
|
|
template<typename X>
|
2015-07-03 18:16:54 +02:00
|
|
|
GenNode(X&& val)
|
2015-07-03 04:13:16 +02:00
|
|
|
: 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))
|
|
|
|
|
{ }
|
|
|
|
|
|
2015-07-03 18:16:54 +02:00
|
|
|
////////////////////////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;
|
|
|
|
|
|
2015-07-03 04:13:16 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
isNamed() const
|
|
|
|
|
{
|
2015-08-28 13:40:57 +02:00
|
|
|
return !util::startsWith (idi.getSym(), "_CHILD_");
|
2015-07-03 04:13:16 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-04 14:55:36 +02:00
|
|
|
bool
|
2015-07-05 03:17:39 +02:00
|
|
|
contains (GenNode const& elm) const
|
2015-07-04 14:55:36 +02:00
|
|
|
{
|
2015-07-05 03:17:39 +02:00
|
|
|
return contains (elm.idi);
|
2015-07-04 14:55:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
contains (ID const&) const
|
|
|
|
|
{
|
|
|
|
|
UNIMPLEMENTED("containment check by ID");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-07-03 04:13:16 +02:00
|
|
|
friend string
|
|
|
|
|
name (GenNode const& node)
|
|
|
|
|
{
|
|
|
|
|
return node.idi.getSym();
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-03 18:21:46 +02:00
|
|
|
friend bool
|
|
|
|
|
operator== (GenNode const& n1, GenNode const& n2)
|
|
|
|
|
{
|
2015-08-28 16:12:04 +02:00
|
|
|
return n1.idi == n2.idi
|
|
|
|
|
&& n1.data == n2.data;
|
2015-07-03 18:21:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend bool
|
|
|
|
|
operator!= (GenNode const& n1, GenNode const& n2)
|
|
|
|
|
{
|
2015-08-28 16:12:04 +02:00
|
|
|
return ! (n1 == n2);
|
2015-07-03 18:21:46 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-04 20:50:18 +02:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2015-07-08 04:12:10 +02:00
|
|
|
X* typeID(0);
|
2015-07-04 20:50:18 +02:00
|
|
|
return ID(typeID, symbolicID);
|
|
|
|
|
}
|
2015-07-03 18:21:46 +02:00
|
|
|
|
2015-07-03 04:13:16 +02:00
|
|
|
private:
|
|
|
|
|
template<typename X>
|
|
|
|
|
static string
|
|
|
|
|
buildChildID()
|
|
|
|
|
{
|
|
|
|
|
return "_CHILD_" + idi::generateSymbolicID<X>();
|
|
|
|
|
}
|
2015-03-21 02:00:55 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2015-07-04 20:50:18 +02:00
|
|
|
/**
|
|
|
|
|
* 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_"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2015-03-21 02:00:55 +01:00
|
|
|
|
2015-07-04 03:39:53 +02:00
|
|
|
/* === Specialisation to add fluent GenNode builder API to Record<GenNode> === */
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
inline GenNode&&
|
2015-07-04 15:22:04 +02:00
|
|
|
MakeRec::genNode()
|
2015-07-04 03:39:53 +02:00
|
|
|
{
|
2015-07-04 15:22:04 +02:00
|
|
|
return std::move (GenNode(std::move(record_)));
|
2015-07-04 03:39:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
inline GenNode&&
|
2015-07-04 15:22:04 +02:00
|
|
|
MakeRec::genNode(string const& symbolicID)
|
2015-07-04 03:39:53 +02:00
|
|
|
{
|
2015-07-04 15:22:04 +02:00
|
|
|
return std::move (GenNode(symbolicID, std::move(record_)));
|
2015-07-04 03:39:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-06-06 02:40:18 +02:00
|
|
|
/* === Specialisation for handling of attributes in Record<GenNode> === */
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
inline bool
|
2015-08-28 18:27:23 +02:00
|
|
|
Rec::isAttribute (GenNode const& attrib)
|
2015-06-06 02:40:18 +02:00
|
|
|
{
|
2015-08-28 18:27:23 +02:00
|
|
|
return attrib.isNamed();
|
2015-06-06 02:40:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
inline bool
|
2015-06-08 01:58:39 +02:00
|
|
|
Rec::isTypeID (GenNode const& v)
|
2015-06-06 02:40:18 +02:00
|
|
|
{
|
2015-08-28 18:27:23 +02:00
|
|
|
return "type" == v.idi.getSym();
|
2015-06-06 02:40:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
inline string
|
2015-06-08 01:58:39 +02:00
|
|
|
Rec::extractTypeID (GenNode const& v)
|
2015-06-06 02:40:18 +02:00
|
|
|
{
|
2015-08-28 18:27:23 +02:00
|
|
|
return isTypeID(v)? v.data.get<string>()
|
|
|
|
|
: Rec::TYPE_NIL;
|
2015-06-06 02:40:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
inline string
|
2015-06-08 01:58:39 +02:00
|
|
|
Rec::extractKey (GenNode const& v)
|
2015-06-06 02:40:18 +02:00
|
|
|
{
|
2015-08-28 18:27:23 +02:00
|
|
|
return isAttribute(v)? v.idi.getSym()
|
|
|
|
|
: "";
|
2015-06-06 02:40:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<>
|
2015-08-17 02:40:57 +02:00
|
|
|
inline GenNode const&
|
2015-06-08 01:58:39 +02:00
|
|
|
Rec::extractVal (GenNode const& v)
|
2015-06-06 02:40:18 +02:00
|
|
|
{
|
2015-08-28 18:27:23 +02:00
|
|
|
return v;
|
2015-06-06 02:40:18 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-05 03:17:39 +02:00
|
|
|
template<>
|
|
|
|
|
inline string
|
|
|
|
|
Rec::renderAttribute (GenNode const& a)
|
|
|
|
|
{
|
2015-08-28 18:27:23 +02:00
|
|
|
return a.idi.getSym() +" = "+string(a.data);
|
2015-07-05 03:17:39 +02:00
|
|
|
}
|
|
|
|
|
|
Generic Record: finish implementation of Mutator
especially setting (changing) attributes turned out to be tricky,
since in case of a GenNode this would mean to re-bind the hash ID;
we can not possibly do that properly without knowing the type of the payload,
and by design this payload type is opaque (erased).
As resort, I changed the semantics of the assign operation:
now it rather builds a new payload element, with a given initialiser.
In case of the strings, this ends up being the same operation,
while in case of GenNode, this is now something entirely different:
we can now build a new GenNode "in place" of the old one, and both
will have the same symbolic ID (attribute key). Incidentally,
our Variant implementation will reject such a re-building operatinon
when this means to change the (opaque) payload type.
in addition, I created a new API function on the Mutator,
allowing to move-in a complete attribute object. Actually this
new function became the working implementation. This way, it is
still possible to emplace a new attribute efficiently (consider
this to be a whole object graph!). But only, if the key (ID)
embedded in the attribute object is already what is the intended
key for this attribute. This way, we elegantly circumvent the
problem of having to re-bind a hash ID without knowing the type seed
2015-08-17 20:31:07 +02:00
|
|
|
template<>
|
|
|
|
|
template<typename X>
|
|
|
|
|
inline GenNode
|
|
|
|
|
Rec::buildAttribute (string const& key, X&& payload)
|
|
|
|
|
{
|
|
|
|
|
return GenNode(key, payload);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-03-21 02:00:55 +01:00
|
|
|
|
|
|
|
|
}} // namespace lib::diff
|
|
|
|
|
#endif /*LIB_DIFF_GEN_NODE_H*/
|