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!
This commit is contained in:
parent
f907ff05d6
commit
e00d6c2a4c
6 changed files with 141 additions and 91 deletions
|
|
@ -528,10 +528,10 @@ namespace diff{
|
|||
|
||||
|
||||
template<class PAR>
|
||||
Builder<TestWireTap<PAR>>
|
||||
auto
|
||||
Builder<PAR>::attachDummy (TestMutationTarget& dummy)
|
||||
{
|
||||
return WireTap (dummy, move(*this));
|
||||
return chainedBuilder<TestWireTap<PAR>> (dummy);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,8 +208,8 @@ namespace diff{
|
|||
* the return value in local scope as long as necessary
|
||||
*/
|
||||
template<class TAR>
|
||||
auto
|
||||
mutatorBinding (TAR& subject) -> typename TreeDiffTraits<TAR>::Ret
|
||||
typename TreeDiffTraits<TAR>::Ret
|
||||
mutatorBinding (TAR& subject)
|
||||
{
|
||||
using Wrapper = typename TreeDiffTraits<TAR>::Ret;
|
||||
return Wrapper{subject};
|
||||
|
|
|
|||
|
|
@ -84,9 +84,8 @@
|
|||
** constructing the concrete TreeMutator needs to have adequate understanding
|
||||
** regarding mode of operation and "mechanics" of such a binding.
|
||||
**
|
||||
** @note the header tree-mutator-attribute-binding.hpp with specific builder templates
|
||||
** is included way down, after the class definitions. This is done so for sake
|
||||
** of readability.
|
||||
** @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
|
||||
**
|
||||
** @see tree-mutator-test.cpp
|
||||
** @see TreeMutator::build()
|
||||
|
|
@ -96,13 +95,22 @@
|
|||
|
||||
#ifndef LIB_DIFF_TREE_MUTATOR_ATTRIBUTE_BINDING_H
|
||||
#define LIB_DIFF_TREE_MUTATOR_ATTRIBUTE_BINDING_H
|
||||
#ifndef LIB_DIFF_TREE_MUTATOR_H
|
||||
#error "this header shall not be used standalone (see tree-mutator.hpp)"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//== anonymous namespace...
|
||||
#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...
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -333,4 +341,30 @@
|
|||
};
|
||||
|
||||
|
||||
|
||||
/** 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
|
||||
#endif /*LIB_DIFF_TREE_MUTATOR_ATTRIBUTE_BINDING_H*/
|
||||
|
|
|
|||
|
|
@ -35,9 +35,8 @@
|
|||
** a building block for one such layer, especially for binding to a representation
|
||||
** of "child objects" managed within a typical STL container.
|
||||
**
|
||||
** @note the header tree-mutator-collection-binding.hpp with specific builder templates
|
||||
** is included way down, after the class definitions. This is done so for sake
|
||||
** of readability.
|
||||
** @note the header tree-mutator-collection-binding.hpp was split off for sake of readability
|
||||
** and is included automatically from bottom of tree-mutator.hpp
|
||||
**
|
||||
** @see tree-mutator-test.cpp
|
||||
** @see TreeMutator::build()
|
||||
|
|
@ -47,13 +46,21 @@
|
|||
|
||||
#ifndef LIB_DIFF_TREE_MUTATOR_COLLECTION_BINDING_H
|
||||
#define LIB_DIFF_TREE_MUTATOR_COLLECTION_BINDING_H
|
||||
#ifndef LIB_DIFF_TREE_MUTATOR_H
|
||||
#error "this header shall not be used standalone (see tree-mutator.hpp)"
|
||||
#endif
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/meta/trait.hpp"
|
||||
#include "lib/diff/gen-node.hpp"
|
||||
#include "lib/diff/tree-mutator.hpp"
|
||||
#include "lib/iter-adapter-stl.hpp"
|
||||
|
||||
//== anonymous namespace...
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace diff{
|
||||
|
||||
namespace { // Mutator-Builder decorator components...
|
||||
|
||||
|
||||
|
||||
|
|
@ -447,6 +454,7 @@
|
|||
}
|
||||
|
||||
|
||||
|
||||
template<class ELM>
|
||||
struct _EmptyBinding
|
||||
{
|
||||
|
|
@ -589,7 +597,7 @@
|
|||
* using lambdas as callback into the otherwise opaque implementation code.
|
||||
*/
|
||||
template<class COLL>
|
||||
auto
|
||||
inline auto
|
||||
collection (COLL& coll)
|
||||
{
|
||||
using Elm = typename COLL::value_type;
|
||||
|
|
@ -599,4 +607,17 @@
|
|||
|
||||
|
||||
|
||||
/** Entry point for DSL builder */
|
||||
template<class PAR>
|
||||
template<class BIN>
|
||||
inline auto
|
||||
Builder<PAR>::attach (BIN&& collectionBindingSetup)
|
||||
{
|
||||
return chainedBuilder<ChildCollectionMutator<PAR,BIN>> (forward<BIN>(collectionBindingSetup));
|
||||
}
|
||||
|
||||
|
||||
}//(END)Mutator-Builder decorator components...
|
||||
|
||||
}} // namespace lib::diff
|
||||
#endif /*LIB_DIFF_TREE_MUTATOR_COLLECTION_BINDING_H*/
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@
|
|||
** Each concrete TreeMutator instance will be configured differently, and this
|
||||
** adaptation is done by implementing binding templates, in the way of building
|
||||
** blocks, layered on top of each other. This header defines a special setup, based
|
||||
** on the two layered bindings for STL collections. The reason is that our »External
|
||||
** Tree Description« of object-like structures is comprised of recursively nested
|
||||
** on two layered bindings for STL collections. The reason is that our »External
|
||||
** Tree Description« of object-like structures is comprised of recursively nested
|
||||
** Record<GenNode> to represent "objects", and this representation is actually implemented
|
||||
** internally based on two collections -- one to hold the _attributes_ and one to hold the
|
||||
** _children._ So this special setup relies on implementation inside knowledge to apply
|
||||
|
|
@ -44,9 +44,8 @@
|
|||
** structure of our _diff language_ -- thus it is sufficient just to layer two collection
|
||||
** bindings, together with suitable closures (lambdas) for layer selection, matching, etc.
|
||||
**
|
||||
** @note the header tree-mutator-collection-binding.hpp with specific builder templates
|
||||
** is included way down, after the class definitions. This is done so for sake
|
||||
** of readability.
|
||||
** @note the header tree-mutator-collection-binding.hpp was split off for sake of readability
|
||||
** and is included automatically from bottom of tree-mutator.hpp
|
||||
**
|
||||
** @see tree-mutator-test.cpp
|
||||
** @see TreeMutator::build()
|
||||
|
|
@ -56,16 +55,19 @@
|
|||
|
||||
#ifndef LIB_DIFF_TREE_MUTATOR_GEN_NODE_BINDING_H
|
||||
#define LIB_DIFF_TREE_MUTATOR_GEN_NODE_BINDING_H
|
||||
#ifndef LIB_DIFF_TREE_MUTATOR_H
|
||||
#error "this header shall not be used standalone (see tree-mutator.hpp)"
|
||||
#endif
|
||||
|
||||
|
||||
#include "lib/diff/gen-node.hpp"
|
||||
#include "lib/diff/tree-mutator-collection-binding.hpp"
|
||||
#include "lib/diff/tree-mutator.hpp"
|
||||
|
||||
//== anonymous namespace...
|
||||
|
||||
|
||||
|
||||
#include <tuple>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace diff{
|
||||
|
||||
namespace { // Mutator-Builder decorator components...
|
||||
|
||||
|
||||
using Storage = RecordSetup<GenNode>::Storage;
|
||||
|
|
@ -83,26 +85,23 @@
|
|||
return std::get<1> (targetTree.exposeToDiff());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach to GenNode tree: Special setup to build a concrete `TreeMutator`.
|
||||
* This decorator is already outfitted with the necessary closures to work on
|
||||
* a diff::Record<GenNode> -- which is typically used as "meta representation"
|
||||
* of object-like structures. Thus this binding allows to apply a diff message
|
||||
* onto such a given »External Tree Description«, mutating it into new shape.
|
||||
*/
|
||||
|
||||
|
||||
/** Entry point for DSL builder */
|
||||
template<class PAR>
|
||||
inline auto
|
||||
twoLayeredGenNodeTreeMutator (Rec::Mutator& targetTree, PAR&& builderBase)
|
||||
Builder<PAR>::attach (Rec::Mutator& targetTree)
|
||||
{
|
||||
return builderBase
|
||||
.attach (collection (accessChildren(targetTree)))
|
||||
.attach (collection (accessAttribs(targetTree)))
|
||||
.isApplicableIf ([&](GenNode const& spec)
|
||||
{
|
||||
return spec.isNamed(); // »Selector« : treat key-value elements here
|
||||
});
|
||||
return this-> attach (collection (accessChildren(targetTree)))
|
||||
.attach (collection (accessAttribs(targetTree)))
|
||||
.isApplicableIf ([&](GenNode const& spec)
|
||||
{
|
||||
return spec.isNamed(); // »Selector« : treat key-value elements here
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}//(END)Mutator-Builder decorator components...
|
||||
|
||||
|
||||
}} // namespace lib::diff
|
||||
#endif /*LIB_DIFF_TREE_MUTATOR_GEN_NODE_BINDING_H*/
|
||||
|
|
|
|||
|
|
@ -324,6 +324,9 @@ namespace diff{
|
|||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace { // Mutator-Builder decorator components...
|
||||
|
||||
using lib::meta::Strip;
|
||||
|
|
@ -374,16 +377,7 @@ namespace diff{
|
|||
|
||||
|
||||
|
||||
/* == implementation detail headers == */
|
||||
|
||||
#include "lib/diff/tree-mutator-attribute-binding.hpp"
|
||||
#include "lib/diff/tree-mutator-collection-binding.hpp"
|
||||
#include "lib/diff/tree-mutator-gen-node-binding.hpp"
|
||||
|
||||
|
||||
|
||||
template<class PAR>
|
||||
struct TestWireTap;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -402,16 +396,13 @@ namespace diff{
|
|||
: PAR{forward<PAR> (par)}
|
||||
{ }
|
||||
|
||||
template<class CLO>
|
||||
using Change = ChangeOperation<PAR,CLO>;
|
||||
template<typename BIN, typename...ARGS>
|
||||
Builder<BIN>
|
||||
chainedBuilder (ARGS&&...args)
|
||||
{
|
||||
return Builder<BIN> (BIN{forward<ARGS>(args)..., move(*this)});
|
||||
}
|
||||
|
||||
template<class CLO>
|
||||
using MutateAttrib = MutationOperation<PAR,CLO>;
|
||||
|
||||
template<class BIN>
|
||||
using Collection = ChildCollectionMutator<PAR,BIN>;
|
||||
|
||||
using WireTap = TestWireTap<PAR>;
|
||||
|
||||
|
||||
/* ==== binding API ==== */
|
||||
|
|
@ -434,13 +425,12 @@ namespace diff{
|
|||
* "applicable" to this attribute and binding. Similar to
|
||||
* GenNode, the provided attributeID is used as-is,
|
||||
* without further sanitising.
|
||||
* @return a _chained builder,_ which establishes this building and
|
||||
* can then be used to define additional binding layers on top
|
||||
*/
|
||||
template<typename CLO>
|
||||
Builder<Change<CLO>>
|
||||
change (Symbol attributeID, CLO setterClosure)
|
||||
{
|
||||
return Change<CLO> (attributeID, setterClosure, move(*this));
|
||||
}
|
||||
auto change (Symbol attributeID, CLO setterClosure);
|
||||
|
||||
|
||||
/** set up a binding for an object valued "attribute" or _named scope_.
|
||||
* This covers the rather special case, where some relevant sub object is
|
||||
|
|
@ -458,12 +448,7 @@ namespace diff{
|
|||
* @see CollectionBindingBuilder::buildChildMutator
|
||||
*/
|
||||
template<typename CLO>
|
||||
Builder<MutateAttrib<CLO>>
|
||||
mutateAttrib (Symbol attributeID, CLO mutatorBuilderClosure)
|
||||
{
|
||||
idi::EntryID<Rec> key{attributeID};
|
||||
return MutateAttrib<CLO> (key, mutatorBuilderClosure, move(*this));
|
||||
}
|
||||
auto mutateAttrib (Symbol attributeID, CLO mutatorBuilderClosure);
|
||||
|
||||
///////////////////////////////////////TODO define variant taking a GenNode::ID ??
|
||||
|
||||
|
|
@ -498,30 +483,33 @@ namespace diff{
|
|||
* this nested mutator until encountering the corresponding `EMU` bracket verb.
|
||||
*/
|
||||
template<typename BIN>
|
||||
Builder<Collection<BIN>>
|
||||
attach (BIN&& collectionBindingSetup)
|
||||
{
|
||||
return Collection<BIN> {forward<BIN>(collectionBindingSetup), move(*this)};
|
||||
}
|
||||
auto attach (BIN&& collectionBindingSetup);
|
||||
|
||||
|
||||
/** set up binding to a GenNode tree: Special setup to build a concrete `TreeMutator`.
|
||||
* This decorator is already outfitted with the necessary closures to work on a
|
||||
* diff::Record<GenNode> -- which is typically used as "meta representation" of
|
||||
* object-like structures. Thus this binding allows to apply a diff message onto
|
||||
* such a given »External Tree Description«, mutating it into new shape.
|
||||
* @remarks our meta representation of "objects" is based on Record<GenNode>, which
|
||||
* is implemented through two STL collections, one for the attributes and
|
||||
* one for the child elements. Thus we'll using two binding layers, based
|
||||
* on the ChildCollectionMutator, configured with the necessary lambdas.
|
||||
*/
|
||||
auto attach (Rec::Mutator& targetTree);
|
||||
|
||||
auto
|
||||
attach (Rec::Mutator& targetTree)
|
||||
{
|
||||
return twoLayeredGenNodeTreeSetup (targetTree, move(*this));
|
||||
}
|
||||
|
||||
/** set up a diagnostic layer, binding to TestMutationTarget.
|
||||
* This can be used to monitor the behaviour of the resulting TreeMutator for tests.
|
||||
*/
|
||||
Builder<WireTap>
|
||||
attachDummy (TestMutationTarget& dummy);
|
||||
auto attachDummy (TestMutationTarget& dummy);
|
||||
|
||||
};
|
||||
|
||||
}//(END) Mutator-Builder...
|
||||
|
||||
|
||||
Builder<TreeMutator>
|
||||
inline Builder<TreeMutator>
|
||||
TreeMutator::build ()
|
||||
{
|
||||
return TreeMutator();
|
||||
|
|
@ -530,3 +518,11 @@ namespace diff{
|
|||
|
||||
}} // namespace lib::diff
|
||||
#endif /*LIB_DIFF_TREE_MUTATOR_H*/
|
||||
|
||||
|
||||
/* == implementation detail headers == */
|
||||
|
||||
#include "lib/diff/tree-mutator-gen-node-binding.hpp"
|
||||
#include "lib/diff/tree-mutator-attribute-binding.hpp"
|
||||
#include "lib/diff/tree-mutator-collection-binding.hpp"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue