WIP: define binding behaviour for diff->GenNode
...need still to solve a problem with circular definition dependencies
This commit is contained in:
parent
c791763890
commit
f907ff05d6
4 changed files with 130 additions and 70 deletions
|
|
@ -190,9 +190,11 @@ namespace diff{
|
|||
template<typename X>
|
||||
X const& get() const;
|
||||
|
||||
/** determine if payload constitutes a nested scope ("object") */
|
||||
bool isNested() const;
|
||||
|
||||
/** peek into the type field of a nested `Record<GenNode>` */
|
||||
string
|
||||
recordType() const;
|
||||
string recordType() const;
|
||||
|
||||
/** visit _children_ of a nested `Record<GenNode>` */
|
||||
Rec::scopeIter
|
||||
|
|
@ -712,6 +714,12 @@ namespace diff{
|
|||
: util::BOTTOM_INDICATOR;
|
||||
}
|
||||
|
||||
inline bool
|
||||
DataCap::isNested() const
|
||||
{
|
||||
return util::BOTTOM_INDICATOR != recordType();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -76,11 +76,11 @@
|
|||
* an otherwise opaque implementation data structure.
|
||||
*
|
||||
* @tparam COLL a STL compliant collection type holding "child elements"
|
||||
* @tparam MAT a closure to determine if a child matches a diff spec (GenNode)
|
||||
* @tparam CTR a closure to construct a new child element from a given diff spec
|
||||
* @tparam MAT a functor to determine if a child matches a diff spec (GenNode)
|
||||
* @tparam CTR a functor to construct a new child element from a given diff spec
|
||||
* @tparam SEL predicate to determine if this binding layer has to process a diff message
|
||||
* @tparam ASS a closure to assign / set a new value from a given diff spec
|
||||
* @tparam MUT a closure to construct a nested mutator for some child element
|
||||
* @tparam ASS a functor to assign / set a new value from a given diff spec
|
||||
* @tparam MUT a functor to construct a nested mutator for some child element
|
||||
*/
|
||||
template<class COLL, class MAT, class CTR, class SEL, class ASS, class MUT>
|
||||
struct CollectionBinding
|
||||
|
|
@ -436,42 +436,70 @@
|
|||
};
|
||||
|
||||
|
||||
using lib::meta::enable_if;
|
||||
using lib::diff::can_wrap_in_GenNode;
|
||||
/** builder function to synthesise builder type from given functors */
|
||||
template<class COLL, class MAT, class CTR, class SEL, class ASS, class MUT>
|
||||
inline auto
|
||||
createCollectionBindingBuilder (COLL& coll, MAT m, CTR c, SEL s, ASS a, MUT u)
|
||||
{
|
||||
using Coll = typename Strip<COLL>::TypeReferred;
|
||||
|
||||
return CollectionBindingBuilder<Coll, MAT,CTR,SEL,ASS,MUT> {coll, m,c,s,a,u};
|
||||
}
|
||||
|
||||
|
||||
template<typename ELM, typename SEL =void>
|
||||
struct _DefaultPayload
|
||||
template<class ELM>
|
||||
struct _EmptyBinding
|
||||
{
|
||||
static bool
|
||||
match (GenNode const&, ELM const&)
|
||||
__ERROR_missing_matcher (GenNode const&, ELM const&)
|
||||
{
|
||||
throw error::Logic ("unable to build a sensible default matching predicate");
|
||||
}
|
||||
|
||||
static ELM
|
||||
construct (GenNode const&)
|
||||
__ERROR_missing_constructor (GenNode const&)
|
||||
{
|
||||
throw error::Logic ("unable to build a sensible default for creating new elements");
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ELM>
|
||||
struct _DefaultPayload<ELM, enable_if<can_wrap_in_GenNode<ELM>>>
|
||||
{
|
||||
|
||||
static bool
|
||||
match (GenNode const& spec, ELM const& elm)
|
||||
ignore_selector (GenNode const&)
|
||||
{
|
||||
return spec.matches(elm);
|
||||
return true; // by default apply diff unconditionally
|
||||
}
|
||||
|
||||
static ELM
|
||||
construct (GenNode const& spec)
|
||||
static bool
|
||||
disable_assignment (ELM&, GenNode const&)
|
||||
{
|
||||
return spec.data.get<ELM>();
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
disable_childMutation (ELM&, GenNode::ID const&, TreeMutator::Handle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template<class COLL>
|
||||
static auto
|
||||
attachTo (COLL& coll)
|
||||
{
|
||||
return createCollectionBindingBuilder (coll
|
||||
,__ERROR_missing_matcher
|
||||
,__ERROR_missing_constructor
|
||||
,ignore_selector
|
||||
,disable_assignment
|
||||
,disable_childMutation
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
using lib::meta::enable_if;
|
||||
using lib::diff::can_wrap_in_GenNode;
|
||||
|
||||
/**
|
||||
* starting point for configuration of a binding to STL container.
|
||||
* When using the "nested DSL" to setup a binding to child elements
|
||||
|
|
@ -482,56 +510,74 @@
|
|||
* the created (\ref CollectionBindingBuilder) to replace
|
||||
* those defaults with lambdas tied into the actual
|
||||
* implementation of the target data structure.
|
||||
* @note depending on the payload type within the collection,
|
||||
* we provide some preconfigured default specialisations
|
||||
*/
|
||||
template<class COLL>
|
||||
template<class ELM, typename SEL =void>
|
||||
struct _DefaultBinding
|
||||
: _EmptyBinding<ELM>
|
||||
{ };
|
||||
|
||||
template<class ELM>
|
||||
struct _DefaultBinding<ELM, enable_if<can_wrap_in_GenNode<ELM>>>
|
||||
{
|
||||
using Coll = typename Strip<COLL>::TypeReferred;
|
||||
using Elm = typename Coll::value_type;
|
||||
|
||||
using Payload = _DefaultPayload<Elm>;
|
||||
|
||||
static bool
|
||||
ignore_selector (GenNode const&)
|
||||
template<class COLL>
|
||||
static auto
|
||||
attachTo (COLL& coll)
|
||||
{
|
||||
return true; // by default apply diff unconditionally
|
||||
}
|
||||
|
||||
static bool
|
||||
disable_assignment (Elm&, GenNode const&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
disable_childMutation (Elm&, GenNode::ID const&, TreeMutator::Handle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
using FallbackBindingConfiguration
|
||||
= CollectionBindingBuilder<Coll
|
||||
,decltype(&Payload::match)
|
||||
,decltype(&Payload::construct)
|
||||
,decltype(&ignore_selector)
|
||||
,decltype(&disable_assignment)
|
||||
,decltype(&disable_childMutation)
|
||||
>;
|
||||
|
||||
static FallbackBindingConfiguration
|
||||
attachTo (Coll& coll)
|
||||
{
|
||||
return FallbackBindingConfiguration{ coll
|
||||
, Payload::match
|
||||
, Payload::construct
|
||||
, ignore_selector
|
||||
, disable_assignment
|
||||
, disable_childMutation
|
||||
};
|
||||
return _EmptyBinding<ELM>::attachTo(coll)
|
||||
.matchElement([](GenNode const& spec, ELM const& elm)
|
||||
{
|
||||
return spec.matches(elm);
|
||||
})
|
||||
.constructFrom([](GenNode const& spec) -> ELM
|
||||
{
|
||||
return spec.data.get<ELM>();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct _DefaultBinding<GenNode>
|
||||
{
|
||||
template<class COLL>
|
||||
static auto
|
||||
attachTo (COLL& coll)
|
||||
{
|
||||
return _EmptyBinding<GenNode>::attachTo(coll)
|
||||
.matchElement([](GenNode const& spec, GenNode const& elm)
|
||||
{
|
||||
return spec.matches(elm);
|
||||
})
|
||||
.constructFrom([](GenNode const& spec) -> GenNode
|
||||
{
|
||||
return GenNode{spec};
|
||||
})
|
||||
.assignElement ([](GenNode& target, GenNode const& spec) -> bool
|
||||
{
|
||||
target.data = spec.data;
|
||||
return true;
|
||||
})
|
||||
.buildChildMutator ([](GenNode& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
|
||||
{
|
||||
if (target.idi == subID // require match on already existing child object
|
||||
and target.data.isNested())
|
||||
{
|
||||
Rec& nestedScope = target.data.get<Rec>();
|
||||
buff.create (
|
||||
TreeMutator::build()
|
||||
.attach (mutateInPlace (nestedScope)));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Entry point to a nested DSL
|
||||
|
|
@ -544,9 +590,11 @@
|
|||
*/
|
||||
template<class COLL>
|
||||
auto
|
||||
collection (COLL& coll) -> decltype(_DefaultBinding<COLL>::attachTo(coll))
|
||||
collection (COLL& coll)
|
||||
{
|
||||
return _DefaultBinding<COLL>::attachTo(coll);
|
||||
using Elm = typename COLL::value_type;
|
||||
|
||||
return _DefaultBinding<Elm>::attachTo(coll);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@
|
|||
|
||||
|
||||
|
||||
using lib::diff::GenNode;
|
||||
|
||||
|
||||
using Storage = RecordSetup<GenNode>::Storage;
|
||||
|
||||
|
|
@ -96,7 +96,11 @@
|
|||
{
|
||||
return builderBase
|
||||
.attach (collection (accessChildren(targetTree)))
|
||||
.attach (collection (accessAttribs(targetTree)));
|
||||
.attach (collection (accessAttribs(targetTree)))
|
||||
.isApplicableIf ([&](GenNode const& spec)
|
||||
{
|
||||
return spec.isNamed(); // »Selector« : treat key-value elements here
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ namespace test{
|
|||
.attach (collection(nestedObj_)
|
||||
.isApplicableIf ([&](GenNode const& spec) -> bool
|
||||
{
|
||||
return BOTTOM_INDICATOR != spec.data.recordType(); // »Selector« : require object-like sub scope
|
||||
return spec.data.isNested(); // »Selector« : require object-like sub scope
|
||||
})
|
||||
.matchElement ([&](GenNode const& spec, Opaque const& elm) -> bool
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue