2016-03-18 20:03:27 +01:00
|
|
|
/*
|
|
|
|
|
TREE-MUTATOR-ATTRIBUTE-BINDING.hpp - diff::TreeMutator implementation building block
|
|
|
|
|
|
|
|
|
|
Copyright (C) Lumiera.org
|
|
|
|
|
2016, 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 tree-mutator-attribute-binding.hpp
|
|
|
|
|
** Special binding implementation for TreeMutator, allowing to map
|
|
|
|
|
** tree diff operations onto native object attributes. TreeMutator is a
|
|
|
|
|
** customisable intermediary, which enables otherwise opaque implementation
|
|
|
|
|
** data structures to receive and respond to generic structural change messages
|
|
|
|
|
** ("tree diff").
|
|
|
|
|
**
|
|
|
|
|
** Each concrete TreeMutator instance will be configured differently, and this
|
|
|
|
|
** adaptation is done by implementing binding templates, in the way of building
|
|
|
|
|
** blocks, attached and customised through lambdas. It is possible to layer
|
|
|
|
|
** several bindings on top of a single TreeMutator -- and this header defines
|
2016-06-09 01:10:52 +02:00
|
|
|
** a building block for one specific kind of layer, used to bind object fields
|
|
|
|
|
** through "setter" lambdas.
|
|
|
|
|
**
|
|
|
|
|
** ## architecture considerations
|
|
|
|
|
** Together with the (\ref tree-mutator-collection-binding.hpp), the attribute binding
|
|
|
|
|
** is the most relevant building block -- yet it is special in several respects. There
|
|
|
|
|
** is kind of a "impedance mismatch" between the concept of an "attribute", as used in
|
|
|
|
|
** the context of diff messages and »External Tree Description«, and the nature of
|
|
|
|
|
** data fields as used within the imperative or object oriented implementation: the
|
|
|
|
|
** latter is rooted within a _class definition_ -- which is conceived as a _type_,
|
|
|
|
|
** a conceptual entity used for construction of code, yet not really embodied into
|
|
|
|
|
** the actual code at execution time. Thus, with respect to the _behaviour_ at execution,
|
|
|
|
|
** the structure defined through typing and classes appears as static backdrop. This leads
|
|
|
|
|
** to the consequence, that, on a generic (unspecific) level, we don't have any correlate
|
|
|
|
|
** to the notion of _ordering_ and _sequence_, as found within the diff language.
|
|
|
|
|
**
|
|
|
|
|
** On the other hand, this very notion of _ordering_ and _sequence_ is essential to the
|
|
|
|
|
** meaning of "diff", as far as collections of "children" are involved. This leaves us
|
|
|
|
|
** with the decision, either to increase complexity of the diff language's definition
|
|
|
|
|
** and concept, absorb this discrepancy within the complexity of implementation.
|
|
|
|
|
** Deliberately, the whole concept of a "diff language" builds onto the notion of
|
|
|
|
|
** _layered semantics,_ where the precise meaning of some terms remains a private
|
|
|
|
|
** extension within specific usage context. There is a lot of leeway within the
|
|
|
|
|
** language, and the _correct usage protocol_ is linked to the actual scope of
|
|
|
|
|
** usage. We need the diff language to be a connecting medium, to link some
|
|
|
|
|
** remote partners based on a locally shared common understanding of structure.
|
|
|
|
|
**
|
|
|
|
|
** And so we use the same approach when it comes to "attributes": We'll assume that
|
|
|
|
|
** the partners connected through diff messages are _structurally compatible_ -- thus
|
|
|
|
|
** any "change" message emitted at one side is assumed to basically make sense on the
|
|
|
|
|
** receiving side. Consequently, the binding of an "attribute" to an object or data field
|
|
|
|
|
** will either ignore or reject any specifics about field order. It will _reject_ an
|
|
|
|
|
** explicit demand to re-order a field, and it will silently pass down other notions
|
|
|
|
|
** related to ordering -- down to lower "onion layers" of the concrete binding. So
|
|
|
|
|
** it depends on the concrete setup of the data binding (TreeMutator), if some
|
|
|
|
|
** expression in diff language will be deemed incompatible -- which happens when
|
|
|
|
|
** in the end no "onion layer" of the concrete binding was able to absorb and
|
|
|
|
|
** comply to the diff message.
|
|
|
|
|
**
|
|
|
|
|
** Another architectural consideration is relevant to the way attribute bindings are
|
|
|
|
|
** constructed: we rather construct a separate binding for each individual attribute,
|
|
|
|
|
** instead of building a collective binding for all attributes of a given object.
|
|
|
|
|
** This gives us the benefit of a simple and flexible solution plus it avoids the
|
|
|
|
|
** overhead of managing a _collection of attribute definitions_ (which would likely
|
|
|
|
|
** cause a heap allocation for storage). The downside is that we loose any coherence
|
|
|
|
|
** between attributes of "the same object", we loose possible consistency checks and
|
|
|
|
|
** we get a linear search for access to any attribute binding. Moreover, we cannot
|
|
|
|
|
** protect against creation of a nonsensical binding, e.g. a binding which ties
|
|
|
|
|
** the same attribute several times in contradictory fashion. The client code
|
|
|
|
|
** constructing the concrete TreeMutator needs to have adequate understanding
|
|
|
|
|
** regarding mode of operation and "mechanics" of such a binding.
|
|
|
|
|
**
|
reorganise inclusion of TreeMutator-DSL builders
previously they where included in the middle of tree-mutator.hpp
This was straight forward, since the builder relies on the classes
defined in the detail headers.
However, the GenNode-binding needs to use a specifically configured
collection binding, and this in turn requires writing a recursive
lambda to deal with nested scopes. This gets us into trouble with
circular definition dependencies.
As a workaround we now only *declare* the DSL builder functions
in the tree-mutator-builder object, and additionally use auto on
all return types. This allows us to spell out the complete builder
definition, without mentioning any of the implementation classes.
Obviously, the detail headers have then to be included *after*
the builder definition, at bottom of tree-mutator.hpp
This also allows us to turn these implementation headers into
completely normal headers, with namespaces and transitive #includes
In the end, the whole setup looks much more "innocent" now.
But beware: the #include of the implementation headers at bottom
of tree-mutator.hpp needs to be given in reverse dependency order,
due to the circular inclusion (back to tree-mutator.hpp) in
conjunction with the inclusion guards!
2016-09-02 01:29:32 +02:00
|
|
|
** @note the header tree-mutator-attribute-binding.hpp was split off for sake of readability
|
|
|
|
|
** and is included automatically from bottom of tree-mutator.hpp
|
2016-03-18 20:03:27 +01:00
|
|
|
**
|
|
|
|
|
** @see tree-mutator-test.cpp
|
|
|
|
|
** @see TreeMutator::build()
|
|
|
|
|
**
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef LIB_DIFF_TREE_MUTATOR_ATTRIBUTE_BINDING_H
|
|
|
|
|
#define LIB_DIFF_TREE_MUTATOR_ATTRIBUTE_BINDING_H
|
|
|
|
|
|
|
|
|
|
|
reorganise inclusion of TreeMutator-DSL builders
previously they where included in the middle of tree-mutator.hpp
This was straight forward, since the builder relies on the classes
defined in the detail headers.
However, the GenNode-binding needs to use a specifically configured
collection binding, and this in turn requires writing a recursive
lambda to deal with nested scopes. This gets us into trouble with
circular definition dependencies.
As a workaround we now only *declare* the DSL builder functions
in the tree-mutator-builder object, and additionally use auto on
all return types. This allows us to spell out the complete builder
definition, without mentioning any of the implementation classes.
Obviously, the detail headers have then to be included *after*
the builder definition, at bottom of tree-mutator.hpp
This also allows us to turn these implementation headers into
completely normal headers, with namespaces and transitive #includes
In the end, the whole setup looks much more "innocent" now.
But beware: the #include of the implementation headers at bottom
of tree-mutator.hpp needs to be given in reverse dependency order,
due to the circular inclusion (back to tree-mutator.hpp) in
conjunction with the inclusion guards!
2016-09-02 01:29:32 +02:00
|
|
|
#include "lib/error.hpp"
|
|
|
|
|
#include "lib/symbol.hpp"
|
|
|
|
|
#include "lib/diff/gen-node.hpp"
|
|
|
|
|
#include "lib/diff/tree-mutator.hpp"
|
|
|
|
|
#include "lib/format-string.hpp"
|
|
|
|
|
#include "lib/idi/entry-id.hpp"
|
|
|
|
|
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
namespace diff{
|
|
|
|
|
|
|
|
|
|
namespace { // Mutator-Builder decorator components...
|
2016-03-18 20:03:27 +01:00
|
|
|
|
|
|
|
|
|
2016-08-26 16:29:50 +02:00
|
|
|
/**
|
|
|
|
|
* Generic behaviour of any binding to object fields (attributes).
|
|
|
|
|
* Since object fields as such are part of the class definition, a diff
|
|
|
|
|
* will never be able to add, insert, delted or re-order fields. Thus we
|
|
|
|
|
* do not need to keep track of an "old" and "new" order; rather there
|
|
|
|
|
* is always one single fixed element present to work on.
|
|
|
|
|
* @note consequently, several diff operations are either implemented NOP,
|
|
|
|
|
* or passed to the parent (lower onion layers).
|
|
|
|
|
*/
|
2016-06-04 14:20:59 +02:00
|
|
|
template<class PAR>
|
2016-05-29 03:01:27 +02:00
|
|
|
class AttributeBindingBase
|
2016-05-27 03:33:56 +02:00
|
|
|
: public PAR
|
2016-03-18 20:03:27 +01:00
|
|
|
{
|
|
|
|
|
|
2016-06-04 14:20:59 +02:00
|
|
|
BareEntryID attribID_;
|
2016-05-27 03:33:56 +02:00
|
|
|
|
2016-05-29 03:01:27 +02:00
|
|
|
protected:
|
2016-06-04 14:20:59 +02:00
|
|
|
AttributeBindingBase (BareEntryID attribID, PAR&& chain)
|
2016-05-29 03:01:27 +02:00
|
|
|
: PAR(std::forward<PAR>(chain))
|
|
|
|
|
, attribID_(attribID)
|
|
|
|
|
{ }
|
2016-05-27 03:33:56 +02:00
|
|
|
|
|
|
|
|
/** hard wired "selector predicate" for this binding layer.
|
2016-06-04 14:20:59 +02:00
|
|
|
* We handle only mutation operations pertaining attributes
|
2016-05-27 03:33:56 +02:00
|
|
|
* which additionally match the key defined at binding time.
|
|
|
|
|
* Any other operations are passed down the chain.
|
|
|
|
|
* @param spec target specification given within the diff verb
|
|
|
|
|
* @return if this binding is in charge for handling the spec
|
|
|
|
|
*/
|
|
|
|
|
bool
|
|
|
|
|
isApplicable (GenNode const& spec)
|
|
|
|
|
{
|
|
|
|
|
return spec.isNamed()
|
2016-05-28 03:41:03 +02:00
|
|
|
and attribID_ == spec.idi;
|
2016-05-27 03:33:56 +02:00
|
|
|
}
|
|
|
|
|
|
2016-08-26 16:29:50 +02:00
|
|
|
void
|
|
|
|
|
__ifApplicable_refuse_to(Literal oper, GenNode const& spec)
|
2016-05-27 02:08:29 +02:00
|
|
|
{
|
2016-08-26 16:29:50 +02:00
|
|
|
if (this->isApplicable(spec))
|
|
|
|
|
throw error::Logic (_Fmt{"attempt to %s attribute '%s', "
|
|
|
|
|
"but this binding for '%s' is linked to a data field and "
|
|
|
|
|
"thus does not support any notion of 'order' or 'position', "
|
|
|
|
|
"inserting or deletion."}
|
|
|
|
|
% oper % spec.idi % this->attribID_);
|
2016-05-27 02:08:29 +02:00
|
|
|
}
|
|
|
|
|
|
2016-08-26 16:29:50 +02:00
|
|
|
|
|
|
|
|
/* ==== re-Implementation of the operation API ==== */
|
|
|
|
|
public:
|
2016-05-27 03:33:56 +02:00
|
|
|
/** ensure the given spec is deemed appropriate at that point.
|
|
|
|
|
* Due to the hard wired nature of an object field binding, this can
|
|
|
|
|
* only be verified passively: a spec targeted at an unknown attribute
|
|
|
|
|
* will be rejected. But since there is no notion of "ordering" for
|
|
|
|
|
* (object) data fields, we can not verify the diff's completeness. */
|
2016-05-27 02:08:29 +02:00
|
|
|
virtual bool
|
|
|
|
|
matchSrc (GenNode const& spec) override
|
|
|
|
|
{
|
2016-05-27 03:33:56 +02:00
|
|
|
return isApplicable (spec)
|
|
|
|
|
or PAR::matchSrc (spec);
|
2016-05-27 02:08:29 +02:00
|
|
|
}
|
|
|
|
|
|
2016-08-26 16:29:50 +02:00
|
|
|
|
|
|
|
|
// note: hasSrc() not overridden here --> delegate to parent layer
|
|
|
|
|
|
|
|
|
|
|
2016-05-27 03:33:56 +02:00
|
|
|
/** accept status quo, after verifying the spec from the diff verb */
|
2016-05-27 02:08:29 +02:00
|
|
|
virtual bool
|
2016-05-27 03:33:56 +02:00
|
|
|
acceptSrc (GenNode const& spec) override
|
2016-05-27 02:08:29 +02:00
|
|
|
{
|
2016-06-04 15:08:10 +02:00
|
|
|
return isApplicable (spec)
|
|
|
|
|
or PAR::acceptSrc (spec);
|
2016-05-27 02:08:29 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-27 03:33:56 +02:00
|
|
|
/** any reordering or deletion of object fields is prohibited
|
|
|
|
|
* @throw error::Logic when this binding layer becomes responsible for
|
|
|
|
|
* handling the given diff spec, because a proper diff must be arranged
|
|
|
|
|
* in a way not to ask this binding to "reorder" a field from an
|
|
|
|
|
* existing class definition.
|
|
|
|
|
*/
|
investigate and confirm the logic underlying the matchSrc, skipSrc and acceptSrc primitives
In Theory, acceptSrc and skipSrc are to operate symmetrically,
with the sole difference that skipSrc does not move anything
into the new content.
BUT, since skipSrc is also used to implement the `skip` verb,
which serves to discard garbage left back by a preceeding `find`,
we cannot touch the data found in the src position without risk
of SEGFAULT. For this reason, there is a dedicated matchSrc operation,
which shall be used to generate the verification step to properly
implement the `del` verb.
I've spent quite some time to verify the logic of predicate evaluation.
It seems to be OK: whenever the SELECTOR applies, then we'll perform
the local match, and then also we'll perform the skipSrc. Otherwise,
we'll delegate both operations likewise to the next lower layer,
without touching anything here.
2016-08-09 23:42:42 +02:00
|
|
|
virtual void
|
|
|
|
|
skipSrc (GenNode const& refSpec) override
|
|
|
|
|
{
|
2016-08-26 16:29:50 +02:00
|
|
|
__ifApplicable_refuse_to ("skip or drop", refSpec);
|
|
|
|
|
PAR::skipSrc (refSpec);
|
investigate and confirm the logic underlying the matchSrc, skipSrc and acceptSrc primitives
In Theory, acceptSrc and skipSrc are to operate symmetrically,
with the sole difference that skipSrc does not move anything
into the new content.
BUT, since skipSrc is also used to implement the `skip` verb,
which serves to discard garbage left back by a preceeding `find`,
we cannot touch the data found in the src position without risk
of SEGFAULT. For this reason, there is a dedicated matchSrc operation,
which shall be used to generate the verification step to properly
implement the `del` verb.
I've spent quite some time to verify the logic of predicate evaluation.
It seems to be OK: whenever the SELECTOR applies, then we'll perform
the local match, and then also we'll perform the skipSrc. Otherwise,
we'll delegate both operations likewise to the next lower layer,
without touching anything here.
2016-08-09 23:42:42 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-27 02:08:29 +02:00
|
|
|
virtual bool
|
|
|
|
|
findSrc (GenNode const& refSpec) override
|
|
|
|
|
{
|
2016-08-26 16:29:50 +02:00
|
|
|
__ifApplicable_refuse_to ("re-order", refSpec);
|
|
|
|
|
return PAR::findSrc (refSpec);
|
2016-05-27 02:08:29 +02:00
|
|
|
}
|
|
|
|
|
|
2016-06-05 16:31:29 +02:00
|
|
|
/** there is no real support for navigating to a 'position',
|
|
|
|
|
* since attribute / data field binding has no notion of ordering.
|
|
|
|
|
* An attempt to fast-forward to "the end" is tolerated though.
|
|
|
|
|
* @throw error::Logic when this binding becomes responsible and a
|
|
|
|
|
* request to navigate to some specific attribute is detected
|
|
|
|
|
* @note the diff spec `Ref::END` or `Ref::ATTRIBS` is tolerated
|
|
|
|
|
* and implemented as NOP (since there is no 'position'
|
|
|
|
|
* incorporated into the binding implementation.
|
|
|
|
|
*/
|
2016-05-27 02:08:29 +02:00
|
|
|
virtual bool
|
2016-05-29 03:01:27 +02:00
|
|
|
accept_until (GenNode const& spec) override
|
2016-05-27 02:08:29 +02:00
|
|
|
{
|
2016-09-02 18:40:16 +02:00
|
|
|
if (Ref::END == spec or Ref::ATTRIBS == spec)
|
|
|
|
|
return PAR::accept_until(spec);
|
2016-06-05 16:31:29 +02:00
|
|
|
else
|
2016-08-26 16:29:50 +02:00
|
|
|
{
|
|
|
|
|
__ifApplicable_refuse_to ("navigate to a position behind", spec);
|
2016-06-05 16:31:29 +02:00
|
|
|
return PAR::accept_until(spec);
|
2016-08-26 16:29:50 +02:00
|
|
|
}
|
2016-05-27 02:08:29 +02:00
|
|
|
}
|
2016-05-29 03:01:27 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class PAR, class CLO>
|
|
|
|
|
class ChangeOperation
|
2016-06-04 14:20:59 +02:00
|
|
|
: public AttributeBindingBase<PAR>
|
2016-05-29 03:01:27 +02:00
|
|
|
{
|
2016-06-04 14:20:59 +02:00
|
|
|
using CloArgs = typename _ClosureType<CLO>::Args;
|
|
|
|
|
using ValueType = typename lib::meta::Pick<CloArgs, 0>::Type;
|
|
|
|
|
using ID = idi::EntryID<ValueType>;
|
2016-05-29 03:01:27 +02:00
|
|
|
|
|
|
|
|
|
2016-06-04 14:20:59 +02:00
|
|
|
CLO setter_;
|
2016-05-29 03:01:27 +02:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
ChangeOperation (Symbol attribKey, CLO clo, PAR&& chain)
|
2016-06-04 14:20:59 +02:00
|
|
|
: AttributeBindingBase<PAR>(ID{attribKey}, std::forward<PAR>(chain))
|
|
|
|
|
, setter_(clo)
|
2016-05-29 03:01:27 +02:00
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ==== Implementation of value assignment operation ==== */
|
|
|
|
|
|
|
|
|
|
/** while, strictly speaking, one can not "insert" fields into a
|
|
|
|
|
* given class definition, this binding can tolerate an `INS` verb
|
|
|
|
|
* whenever this means to touch a field which is actually known and
|
|
|
|
|
* present in the class definition underlying this binding. In such
|
|
|
|
|
* a case, we just assign the given value. This implementation leeway
|
|
|
|
|
* is deliberate to support classes with optional / defaultable properties.
|
|
|
|
|
*/
|
|
|
|
|
virtual bool
|
|
|
|
|
injectNew (GenNode const& spec) override
|
|
|
|
|
{
|
|
|
|
|
if (not this->isApplicable(spec))
|
|
|
|
|
return PAR::injectNew(spec);
|
|
|
|
|
|
2016-06-04 14:20:59 +02:00
|
|
|
setter_(spec.data.get<ValueType>());
|
2016-05-29 03:01:27 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2016-05-27 02:08:29 +02:00
|
|
|
|
2016-06-09 01:10:52 +02:00
|
|
|
/** invoke the setter lambda, when this binding layer is in charge */
|
2016-05-27 02:08:29 +02:00
|
|
|
virtual bool
|
2016-05-29 03:01:27 +02:00
|
|
|
assignElm (GenNode const& spec) override
|
2016-05-27 02:08:29 +02:00
|
|
|
{
|
2016-05-29 03:01:27 +02:00
|
|
|
if (not this->isApplicable(spec))
|
2016-05-27 03:33:56 +02:00
|
|
|
return PAR::assignElm(spec);
|
2016-05-28 01:17:45 +02:00
|
|
|
|
2016-06-04 14:20:59 +02:00
|
|
|
setter_(spec.data.get<ValueType>());
|
2016-05-28 01:17:45 +02:00
|
|
|
return true;
|
2016-05-27 02:08:29 +02:00
|
|
|
}
|
2016-05-29 03:01:27 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2016-06-04 14:20:59 +02:00
|
|
|
|
|
|
|
|
template<class PAR, class MUT>
|
2016-05-29 03:01:27 +02:00
|
|
|
class MutationOperation
|
2016-06-04 14:20:59 +02:00
|
|
|
: public AttributeBindingBase<PAR>
|
2016-05-29 03:01:27 +02:00
|
|
|
{
|
|
|
|
|
|
2016-06-09 01:18:21 +02:00
|
|
|
ASSERT_VALID_SIGNATURE (MUT, void(TreeMutator::Handle));
|
2016-06-04 14:20:59 +02:00
|
|
|
|
2016-05-29 03:01:27 +02:00
|
|
|
MUT mutatorBuilder_;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
2016-06-04 14:20:59 +02:00
|
|
|
MutationOperation (BareEntryID const& attribID, MUT clo, PAR&& chain)
|
|
|
|
|
: AttributeBindingBase<PAR>(attribID, std::forward<PAR>(chain))
|
2016-05-29 03:01:27 +02:00
|
|
|
, mutatorBuilder_(clo)
|
|
|
|
|
{ }
|
|
|
|
|
|
2016-05-27 02:08:29 +02:00
|
|
|
|
2016-06-04 14:20:59 +02:00
|
|
|
/** if this binding layer is in charge, then invoke the closure,
|
|
|
|
|
* which is assumed to construct a nested TreeMutator into the provided Buffer,
|
|
|
|
|
* able to deal with the nested attribute object referred by this binding */
|
2016-05-27 02:08:29 +02:00
|
|
|
virtual bool
|
2016-06-09 01:18:21 +02:00
|
|
|
mutateChild (GenNode const& spec, TreeMutator::Handle targetBuff) override
|
2016-05-27 02:08:29 +02:00
|
|
|
{
|
2016-06-04 14:20:59 +02:00
|
|
|
if (not this->isApplicable(spec))
|
|
|
|
|
return PAR::mutateChild(spec, targetBuff);
|
|
|
|
|
|
|
|
|
|
mutatorBuilder_(targetBuff);
|
|
|
|
|
return true;
|
2016-05-27 02:08:29 +02:00
|
|
|
}
|
2016-08-26 02:56:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/** default setup: silently absorb insert.
|
|
|
|
|
* @remark this whole binding flavour (MutateOperation) deals with an
|
|
|
|
|
* odd corner case, namely an object valued attribute, which is deliberately
|
|
|
|
|
* treated as nested scope; a sub-scope accessed by name. Since attributes
|
|
|
|
|
* do not support any notion of "inserting" or "reordering" anyway, it is
|
|
|
|
|
* typical in such a situation for the implementation data structure to manage
|
|
|
|
|
* the underlying object automatically or default construct it anyway; moreover
|
|
|
|
|
* it would be pointless to define a setter, since the whole point is not to
|
|
|
|
|
* assign, but rather to enter and populate the nested target object. Yet,
|
|
|
|
|
* the diff language requires us to send an empty initial value at least
|
|
|
|
|
* once prior to mutation.
|
|
|
|
|
* @note if you really _need_ a dedicated custom setter, just define it
|
|
|
|
|
* _after_ the `mutateAttribute` spec; it's implementation will then
|
|
|
|
|
* be layered on top and shadow this default case.
|
|
|
|
|
*/
|
|
|
|
|
virtual bool
|
|
|
|
|
injectNew (GenNode const& spec) override
|
|
|
|
|
{
|
|
|
|
|
return this->isApplicable(spec)
|
|
|
|
|
or PAR::injectNew(spec);
|
|
|
|
|
}
|
2016-03-18 20:03:27 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
reorganise inclusion of TreeMutator-DSL builders
previously they where included in the middle of tree-mutator.hpp
This was straight forward, since the builder relies on the classes
defined in the detail headers.
However, the GenNode-binding needs to use a specifically configured
collection binding, and this in turn requires writing a recursive
lambda to deal with nested scopes. This gets us into trouble with
circular definition dependencies.
As a workaround we now only *declare* the DSL builder functions
in the tree-mutator-builder object, and additionally use auto on
all return types. This allows us to spell out the complete builder
definition, without mentioning any of the implementation classes.
Obviously, the detail headers have then to be included *after*
the builder definition, at bottom of tree-mutator.hpp
This also allows us to turn these implementation headers into
completely normal headers, with namespaces and transitive #includes
In the end, the whole setup looks much more "innocent" now.
But beware: the #include of the implementation headers at bottom
of tree-mutator.hpp needs to be given in reverse dependency order,
due to the circular inclusion (back to tree-mutator.hpp) in
conjunction with the inclusion guards!
2016-09-02 01:29:32 +02:00
|
|
|
|
|
|
|
|
/** Entry point for DSL builder */
|
|
|
|
|
template<class PAR>
|
|
|
|
|
template<typename CLO>
|
|
|
|
|
inline auto
|
|
|
|
|
Builder<PAR>::change (Symbol attributeID, CLO setterClosure)
|
|
|
|
|
{
|
|
|
|
|
return chainedBuilder<ChangeOperation<PAR,CLO>> (attributeID, setterClosure);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Entry point for DSL builder */
|
|
|
|
|
template<class PAR>
|
|
|
|
|
template<typename CLO>
|
|
|
|
|
inline auto
|
|
|
|
|
Builder<PAR>::mutateAttrib (Symbol attributeID, CLO mutatorBuilderClosure)
|
|
|
|
|
{
|
|
|
|
|
idi::EntryID<Rec> key{attributeID};
|
|
|
|
|
return chainedBuilder<MutationOperation<PAR,CLO>> (key, mutatorBuilderClosure);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}//(END)Mutator-Builder decorator components...
|
|
|
|
|
|
|
|
|
|
}} // namespace lib::diff
|
2016-03-18 20:03:27 +01:00
|
|
|
#endif /*LIB_DIFF_TREE_MUTATOR_ATTRIBUTE_BINDING_H*/
|