2015-04-02 03:30:20 +02:00
|
|
|
/*
|
|
|
|
|
TREE-MUTATOR.hpp - flexible binding to map generic tree changing operations
|
|
|
|
|
|
|
|
|
|
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 tree-mutator.hpp
|
|
|
|
|
** Customisable intermediary to abstract generic tree mutation operations.
|
|
|
|
|
** This is the foundation for generic treatment of tree altering operations,
|
2015-04-13 15:49:38 +02:00
|
|
|
** and especially the handling of changes (diff) to hierarchical data structures.
|
2015-04-02 03:30:20 +02:00
|
|
|
** The goal is to represent a standard set of conceptual operations working on
|
|
|
|
|
** arbitrary data structures, without the need for these data structures to
|
|
|
|
|
** comply to any interface or base type. Rather, we allow each instance to
|
2015-04-13 15:49:38 +02:00
|
|
|
** define binding closures, which allows to tap into arbitrary internal data
|
2015-04-02 03:30:20 +02:00
|
|
|
** representation, without any need of disclosure. The only assumption is
|
|
|
|
|
** that the data to be treated is \em hierarchical and \em object-like,
|
|
|
|
|
** i.e. it has (named) attributes and it may have a collection of children.
|
|
|
|
|
** If necessary, typing constraints can be integrated through symbolic
|
|
|
|
|
** representation of types as chained identifiers. (path dependent types).
|
|
|
|
|
**
|
|
|
|
|
** The interface implemented by the TreeMutator is shaped such as to support
|
|
|
|
|
** the primitives of Lumiera's tree \link diff-language.hpp diff handling language. \endlink
|
|
|
|
|
** By default, each of these primitives is implemented as a \c NOP -- but each operation
|
|
|
|
|
** can be replaced by a binding closure, which allows to invoke arbitrary code in the
|
|
|
|
|
** context of the given object's implementation internals.
|
|
|
|
|
**
|
2016-02-27 01:47:33 +01:00
|
|
|
** ## Builder/Adapter concept
|
|
|
|
|
** TreeMutator is both an interface and a set of building blocks.
|
|
|
|
|
** On concrete usage, the (private, non disclosed) target data structure is assumed
|
|
|
|
|
** to _build a subclass of TreeMutator._ To this end, the TreeMutator is complemented
|
|
|
|
|
** by a builder API. Each call on this builder -- typically providing some closure --
|
|
|
|
|
** will add yet another decorating layer on top of the basic TreeMutator (recall all
|
|
|
|
|
** the "mutation primitives" are implemented NOP within the base class). So the actual
|
|
|
|
|
** TreeMutator will be structured like an onion, where each layer cares for the sole
|
|
|
|
|
** concrete aspect it was tied for by the supplied closure. For example, there might
|
|
|
|
|
** be a decorator to handle setting of a "foobar" attribute. Thus, when the diff
|
|
|
|
|
** dictates to mutate "foobar", the corresponding closure will be invoked.
|
|
|
|
|
**
|
|
|
|
|
** \par test dummy target
|
|
|
|
|
** There is a special adapter binding to support writing unit tests. The corresponding
|
|
|
|
|
** API is only declared (forward) by default. The TestMutationTarget is a helper class,
|
|
|
|
|
** which can be attached through this binding and allows a unit test fixture to record
|
|
|
|
|
** and verify all the mutation operations encountered.
|
2016-03-18 20:03:27 +01:00
|
|
|
**
|
|
|
|
|
** @note to improve readability, the actual implementation of the "binding layers"
|
|
|
|
|
** is defined in separate headers and included towards the bottom of this header.
|
2016-02-27 01:47:33 +01:00
|
|
|
**
|
2016-02-26 22:57:49 +01:00
|
|
|
** @see tree-mutator-test.cpp
|
2015-04-02 03:30:20 +02:00
|
|
|
** @see DiffDetector
|
|
|
|
|
**
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef LIB_DIFF_TREE_MUTATOR_H
|
|
|
|
|
#define LIB_DIFF_TREE_MUTATOR_H
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/error.hpp"
|
|
|
|
|
#include "lib/symbol.hpp"
|
2016-03-18 00:31:04 +01:00
|
|
|
#include "lib/meta/trait.hpp"
|
2015-05-02 01:11:39 +02:00
|
|
|
#include "lib/diff/gen-node.hpp"
|
2016-03-06 02:26:42 +01:00
|
|
|
#include "lib/opaque-holder.hpp"
|
2016-03-25 20:46:48 +01:00
|
|
|
#include "lib/iter-adapter-stl.hpp"
|
2015-04-02 03:30:20 +02:00
|
|
|
//#include "lib/util.hpp"
|
|
|
|
|
//#include "lib/format-string.hpp"
|
|
|
|
|
|
|
|
|
|
#include <functional>
|
2016-03-06 02:26:42 +01:00
|
|
|
#include <utility> ////TODO
|
2015-04-02 03:30:20 +02:00
|
|
|
#include <string>
|
|
|
|
|
//#include <vector>
|
|
|
|
|
//#include <map>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
2016-03-06 02:26:42 +01:00
|
|
|
/////////////////////////////TODO move over into opaque-holder.hpp
|
|
|
|
|
/**
|
|
|
|
|
* handle to allow for safe _»remote implantation«_
|
|
|
|
|
* of an unknown subclass into a given OpaqueHolder buffer,
|
|
|
|
|
* without having to disclose the concrete buffer type or size.
|
|
|
|
|
* @remarks this is especially geared towards use in APIs, allowing
|
|
|
|
|
* a not yet known implementation to implant an agent or collaboration
|
|
|
|
|
* partner into the likewise undisclosed innards of the exposed service.
|
|
|
|
|
* @warning the type BA must expose a virtual dtor, since the targetted
|
|
|
|
|
* OpaqueHolder has to take ownership of the implanted object.
|
|
|
|
|
*/
|
|
|
|
|
template<class BA>
|
|
|
|
|
class PlantingHandle
|
|
|
|
|
{
|
|
|
|
|
void* buffer_;
|
|
|
|
|
size_t maxSiz_;
|
|
|
|
|
|
|
|
|
|
///////TODO static assert to virtual dtor??
|
|
|
|
|
public:
|
|
|
|
|
template<size_t maxSiz>
|
2016-03-06 03:55:31 +01:00
|
|
|
PlantingHandle (InPlaceBuffer<BA, maxSiz>& targetBuffer)
|
2016-03-06 02:26:42 +01:00
|
|
|
: buffer_(&targetBuffer)
|
2016-03-10 20:15:19 +01:00
|
|
|
, maxSiz_(maxSiz)
|
2016-03-06 02:26:42 +01:00
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
2016-03-11 21:30:25 +01:00
|
|
|
template<class SUB>
|
2016-03-06 02:26:42 +01:00
|
|
|
BA&
|
2016-03-11 21:30:25 +01:00
|
|
|
create (SUB&& subMutator)
|
2016-03-06 02:26:42 +01:00
|
|
|
{
|
|
|
|
|
if (sizeof(SUB) > maxSiz_)
|
|
|
|
|
throw error::Fatal("Unable to implant implementation object of size "
|
|
|
|
|
"exceeding the pre-established storage buffer capacity."
|
|
|
|
|
,error::LUMIERA_ERROR_CAPACITY);
|
|
|
|
|
|
2016-03-06 03:55:31 +01:00
|
|
|
using Holder = InPlaceBuffer<BA, sizeof(SUB)>;
|
2016-03-06 02:26:42 +01:00
|
|
|
Holder& holder = *static_cast<Holder*> (buffer_);
|
|
|
|
|
|
2016-03-11 21:30:25 +01:00
|
|
|
return holder.create<SUB> (std::forward<SUB> (subMutator));
|
2016-03-06 02:26:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class SUB>
|
|
|
|
|
bool
|
|
|
|
|
canCreate() const
|
|
|
|
|
{
|
|
|
|
|
return sizeof(SUB) <= maxSiz_;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
/////////////////////////////TODO move over into opaque-holder.hpp
|
2015-04-02 03:30:20 +02:00
|
|
|
namespace diff{
|
|
|
|
|
|
|
|
|
|
namespace error = lumiera::error;
|
|
|
|
|
|
|
|
|
|
//using util::_Fmt;
|
|
|
|
|
using lib::Literal;
|
|
|
|
|
using std::function;
|
|
|
|
|
using std::string;
|
|
|
|
|
|
2016-02-27 01:47:33 +01:00
|
|
|
|
2016-03-06 02:26:42 +01:00
|
|
|
|
|
|
|
|
|
2016-02-27 01:47:33 +01:00
|
|
|
class TestMutationTarget; // for unit testing
|
|
|
|
|
|
|
|
|
|
|
2015-04-02 03:30:20 +02:00
|
|
|
namespace {
|
settle on a concrete implementation approach based on inheritance chain
After some reconsideration, I decide to stick to the approach with the closures,
but to use a metaprotramming technique to build an inheritance chain.
While I can not decide on the real world impact of storing all those closures,
in theory this approach should enable the compiler to remove all of the
storage overhead. Since, when storing the result into an auto variable
right within scope (as demonstrated in the test), the compiler
sees the concrete type and might be able to boil down the actual
generated virtual function implementations, thereby inlining the
given closures.
Whereas, on the other hand, if we'd go the obvious conventional route
and place the closures into a Map allocated on the stack, I wouldn't
expect the compiler to do data flow analysis to prove this allocation
is not necessary and inline it away.
NOTE: there is now guarantee this inlining trick will ever work.
And, moreover, we don't know anything regarding the runtime effect.
The whole picture is way more involved as it might seem at first sight.
Even if we go the completely conventional route and require every
participating object to supply an implementation of some kind of
"Serializable" interface, we'll end up with a (hand written!)
implementation class for each participating setup, which takes
up space in the code segment of the executable. While the closure
based approach chosen here, consumes data segment (or heap) space
per instance for the functors (or function pointers) representing
the closures, plus code segment space for the closures, but the
latter with a way higher potential for inlining, since the closure
code and the generated virtual functions are necessarily emitted
within the same compilation unit and within a local (inline, not
publickly exposed) scope.
2015-04-05 18:26:49 +02:00
|
|
|
template<class PAR>
|
2015-08-16 00:16:30 +02:00
|
|
|
struct Builder;
|
2015-04-03 20:10:22 +02:00
|
|
|
|
2015-05-09 20:23:11 +02:00
|
|
|
using ID = Literal;
|
2015-05-02 01:11:39 +02:00
|
|
|
using Attribute = DataCap;
|
2015-04-02 03:30:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2016-03-03 23:11:36 +01:00
|
|
|
* Customisable intermediary to abstract mutating operations
|
|
|
|
|
* on arbitrary, hierarchical object-like data.
|
2015-04-03 20:10:22 +02:00
|
|
|
* The TreeMutator exposes two distinct interfaces
|
|
|
|
|
* - the \em operation API -- similar to what a container exposes --
|
|
|
|
|
* is the entirety of abstract operations that can be done to the
|
|
|
|
|
* subsumed, tree like target structure
|
|
|
|
|
* - the \em binding API allows to link some or all of these generic
|
|
|
|
|
* activities to concrete manipulations known within target scope.
|
2015-04-02 03:30:20 +02:00
|
|
|
*/
|
|
|
|
|
class TreeMutator
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
public:
|
2015-04-03 20:10:22 +02:00
|
|
|
|
|
|
|
|
/* ==== operation API ==== */
|
|
|
|
|
|
2016-03-04 23:18:25 +01:00
|
|
|
virtual bool
|
|
|
|
|
emptySrc ()
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
// do nothing by default
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-04 21:26:25 +01:00
|
|
|
/** skip next src element and advance abstract source position */
|
2016-03-04 21:13:49 +01:00
|
|
|
virtual void
|
|
|
|
|
skipSrc ()
|
|
|
|
|
{
|
2016-03-04 21:26:25 +01:00
|
|
|
// do nothing by default
|
2016-03-04 21:13:49 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-04 21:26:25 +01:00
|
|
|
/** establish new element at current position */
|
settle on a concrete implementation approach based on inheritance chain
After some reconsideration, I decide to stick to the approach with the closures,
but to use a metaprotramming technique to build an inheritance chain.
While I can not decide on the real world impact of storing all those closures,
in theory this approach should enable the compiler to remove all of the
storage overhead. Since, when storing the result into an auto variable
right within scope (as demonstrated in the test), the compiler
sees the concrete type and might be able to boil down the actual
generated virtual function implementations, thereby inlining the
given closures.
Whereas, on the other hand, if we'd go the obvious conventional route
and place the closures into a Map allocated on the stack, I wouldn't
expect the compiler to do data flow analysis to prove this allocation
is not necessary and inline it away.
NOTE: there is now guarantee this inlining trick will ever work.
And, moreover, we don't know anything regarding the runtime effect.
The whole picture is way more involved as it might seem at first sight.
Even if we go the completely conventional route and require every
participating object to supply an implementation of some kind of
"Serializable" interface, we'll end up with a (hand written!)
implementation class for each participating setup, which takes
up space in the code segment of the executable. While the closure
based approach chosen here, consumes data segment (or heap) space
per instance for the functors (or function pointers) representing
the closures, plus code segment space for the closures, but the
latter with a way higher potential for inlining, since the closure
code and the generated virtual functions are necessarily emitted
within the same compilation unit and within a local (inline, not
publickly exposed) scope.
2015-04-05 18:26:49 +02:00
|
|
|
virtual void
|
2016-03-04 21:26:25 +01:00
|
|
|
injectNew (GenNode const&)
|
2015-04-03 20:10:22 +02:00
|
|
|
{
|
2016-03-04 21:26:25 +01:00
|
|
|
// do nothing by default
|
2015-04-03 20:10:22 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-04 21:26:25 +01:00
|
|
|
/** ensure the next source element matches with given spec */
|
2016-03-04 21:13:49 +01:00
|
|
|
virtual bool
|
2016-03-04 21:26:25 +01:00
|
|
|
matchSrc (GenNode const&)
|
2015-04-03 20:10:22 +02:00
|
|
|
{
|
2016-03-04 21:26:25 +01:00
|
|
|
// do nothing by default
|
|
|
|
|
return false;
|
2015-04-03 20:10:22 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-04 21:26:25 +01:00
|
|
|
/** accept existing element, when matching the given spec */
|
2016-03-04 21:13:49 +01:00
|
|
|
virtual bool
|
2016-03-04 21:26:25 +01:00
|
|
|
acceptSrc (GenNode const&)
|
2016-03-04 21:13:49 +01:00
|
|
|
{
|
2016-03-04 21:26:25 +01:00
|
|
|
// do nothing by default
|
|
|
|
|
return false;
|
2016-03-04 21:13:49 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-06 03:55:31 +01:00
|
|
|
/** repeatedly accept, until after the designated location */
|
|
|
|
|
virtual bool
|
|
|
|
|
accept_until (GenNode const&)
|
|
|
|
|
{
|
|
|
|
|
// do nothing by default
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-04 21:26:25 +01:00
|
|
|
/** locate designated element and accept it at current position */
|
2016-03-04 21:13:49 +01:00
|
|
|
virtual bool
|
2016-03-04 21:26:25 +01:00
|
|
|
findSrc (GenNode const&)
|
2015-04-03 20:10:22 +02:00
|
|
|
{
|
2016-03-04 21:26:25 +01:00
|
|
|
// do nothing by default
|
|
|
|
|
return false;
|
2015-04-03 20:10:22 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-06 02:26:42 +01:00
|
|
|
/** locate the designated target element
|
|
|
|
|
* (must be already accepted into the target sequence).
|
|
|
|
|
* Perform an assignement with the given payload value
|
|
|
|
|
* @throw when assignement fails (typically error::Logic)
|
|
|
|
|
* @return false when unable to locate the target */
|
|
|
|
|
virtual bool
|
|
|
|
|
assignElm (GenNode const&)
|
2015-04-03 20:10:22 +02:00
|
|
|
{
|
2016-03-06 02:26:42 +01:00
|
|
|
// do nothing by default
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
using MutatorBuffer = PlantingHandle<TreeMutator>;
|
|
|
|
|
|
|
|
|
|
/** locate the designated target element
|
|
|
|
|
* and build a suittable sub-mutator for this element
|
|
|
|
|
* into the provided target buffer
|
|
|
|
|
* @throw error::Fatal when buffer is insufficient
|
|
|
|
|
* @return false when unable to locate the target */
|
|
|
|
|
virtual bool
|
|
|
|
|
mutateChild (GenNode const&, MutatorBuffer)
|
|
|
|
|
{
|
|
|
|
|
// do nothing by default
|
|
|
|
|
return false;
|
2015-04-03 20:10:22 +02:00
|
|
|
}
|
|
|
|
|
|
2015-05-02 01:39:58 +02:00
|
|
|
virtual void setAttribute (ID, Attribute&) { /* do nothing by default */ }
|
2015-04-03 20:10:22 +02:00
|
|
|
|
settle on a concrete implementation approach based on inheritance chain
After some reconsideration, I decide to stick to the approach with the closures,
but to use a metaprotramming technique to build an inheritance chain.
While I can not decide on the real world impact of storing all those closures,
in theory this approach should enable the compiler to remove all of the
storage overhead. Since, when storing the result into an auto variable
right within scope (as demonstrated in the test), the compiler
sees the concrete type and might be able to boil down the actual
generated virtual function implementations, thereby inlining the
given closures.
Whereas, on the other hand, if we'd go the obvious conventional route
and place the closures into a Map allocated on the stack, I wouldn't
expect the compiler to do data flow analysis to prove this allocation
is not necessary and inline it away.
NOTE: there is now guarantee this inlining trick will ever work.
And, moreover, we don't know anything regarding the runtime effect.
The whole picture is way more involved as it might seem at first sight.
Even if we go the completely conventional route and require every
participating object to supply an implementation of some kind of
"Serializable" interface, we'll end up with a (hand written!)
implementation class for each participating setup, which takes
up space in the code segment of the executable. While the closure
based approach chosen here, consumes data segment (or heap) space
per instance for the functors (or function pointers) representing
the closures, plus code segment space for the closures, but the
latter with a way higher potential for inlining, since the closure
code and the generated virtual functions are necessarily emitted
within the same compilation unit and within a local (inline, not
publickly exposed) scope.
2015-04-05 18:26:49 +02:00
|
|
|
/**
|
|
|
|
|
* start building a custom adapted tree mutator,
|
|
|
|
|
* where the operations are tied by closures or
|
|
|
|
|
* wrappers into the current implementation context.
|
|
|
|
|
*/
|
|
|
|
|
static Builder<TreeMutator> build();
|
2015-04-02 03:30:20 +02:00
|
|
|
};
|
|
|
|
|
|
2016-03-03 23:11:36 +01:00
|
|
|
|
2016-02-27 01:47:33 +01:00
|
|
|
namespace { // Mutator-Builder decorator components...
|
settle on a concrete implementation approach based on inheritance chain
After some reconsideration, I decide to stick to the approach with the closures,
but to use a metaprotramming technique to build an inheritance chain.
While I can not decide on the real world impact of storing all those closures,
in theory this approach should enable the compiler to remove all of the
storage overhead. Since, when storing the result into an auto variable
right within scope (as demonstrated in the test), the compiler
sees the concrete type and might be able to boil down the actual
generated virtual function implementations, thereby inlining the
given closures.
Whereas, on the other hand, if we'd go the obvious conventional route
and place the closures into a Map allocated on the stack, I wouldn't
expect the compiler to do data flow analysis to prove this allocation
is not necessary and inline it away.
NOTE: there is now guarantee this inlining trick will ever work.
And, moreover, we don't know anything regarding the runtime effect.
The whole picture is way more involved as it might seem at first sight.
Even if we go the completely conventional route and require every
participating object to supply an implementation of some kind of
"Serializable" interface, we'll end up with a (hand written!)
implementation class for each participating setup, which takes
up space in the code segment of the executable. While the closure
based approach chosen here, consumes data segment (or heap) space
per instance for the functors (or function pointers) representing
the closures, plus code segment space for the closures, but the
latter with a way higher potential for inlining, since the closure
code and the generated virtual functions are necessarily emitted
within the same compilation unit and within a local (inline, not
publickly exposed) scope.
2015-04-05 18:26:49 +02:00
|
|
|
|
2016-03-18 00:31:04 +01:00
|
|
|
using lib::meta::Strip;
|
2016-03-25 02:25:51 +01:00
|
|
|
using lib::meta::Types;
|
2016-03-18 00:31:04 +01:00
|
|
|
|
2015-05-03 05:24:06 +02:00
|
|
|
/**
|
|
|
|
|
* Type rebinding helper to pick up the actual argument type.
|
|
|
|
|
* Works both for functors and for lambda expressions
|
|
|
|
|
* @remarks Solution proposed 10/2011 by \link http://stackoverflow.com/users/224671/kennytm user "kennytm" \endlink
|
|
|
|
|
* in this \link http://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda/7943765#7943765
|
|
|
|
|
* answer on stackoverflow \endlink
|
2016-03-25 19:58:09 +01:00
|
|
|
* @todo this should be integrated into (\ref _Fun) //////////////////////////////////////TICKET #994
|
2015-05-03 05:24:06 +02:00
|
|
|
*/
|
|
|
|
|
template<typename FUN>
|
|
|
|
|
struct _ClosureType
|
|
|
|
|
: _ClosureType<decltype(&FUN::operator())>
|
|
|
|
|
{ };
|
|
|
|
|
|
2016-03-25 02:25:51 +01:00
|
|
|
template<class C, class RET, typename...ARGS>
|
|
|
|
|
struct _ClosureType<RET (C::*)(ARGS...) const>
|
2015-05-03 05:24:06 +02:00
|
|
|
{
|
2016-03-25 02:25:51 +01:00
|
|
|
using Args = typename Types<ARGS...>::Seq;
|
|
|
|
|
using Ret = RET;
|
|
|
|
|
using Sig = RET(ARGS...);
|
2015-05-03 05:24:06 +02:00
|
|
|
};
|
2016-03-25 02:25:51 +01:00
|
|
|
|
|
|
|
|
template<class RET, typename...ARGS>
|
|
|
|
|
struct _ClosureType<RET (*)(ARGS...)>
|
|
|
|
|
{
|
|
|
|
|
using Args = typename Types<ARGS...>::Seq;
|
|
|
|
|
using Ret = RET;
|
|
|
|
|
using Sig = RET(ARGS...);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<typename FUN, typename SIG>
|
|
|
|
|
struct has_Sig
|
|
|
|
|
: std::is_same<SIG, typename _ClosureType<FUN>::Sig>
|
|
|
|
|
{ };
|
2015-05-03 05:24:06 +02:00
|
|
|
|
|
|
|
|
|
2016-03-18 20:52:35 +01:00
|
|
|
|
|
|
|
|
/* == implementation detail headers == */
|
2016-03-18 19:35:48 +01:00
|
|
|
|
2016-03-18 20:52:35 +01:00
|
|
|
#include "lib/diff/tree-mutator-attribute-binding.hpp"
|
|
|
|
|
#include "lib/diff/tree-mutator-collection-binding.hpp"
|
2016-03-18 19:35:48 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-27 01:47:33 +01:00
|
|
|
template<class PAR>
|
|
|
|
|
struct TestWireTap;
|
|
|
|
|
|
|
|
|
|
|
2016-03-18 20:52:35 +01:00
|
|
|
/**
|
|
|
|
|
* Builder-DSL to create and configure a concrete TreeMutator
|
|
|
|
|
* @remarks all generated follow-up builders are chained and
|
|
|
|
|
* derive from the implementation of the preceding
|
|
|
|
|
* "binding layer" and the TreeMutator interface.
|
|
|
|
|
*/
|
settle on a concrete implementation approach based on inheritance chain
After some reconsideration, I decide to stick to the approach with the closures,
but to use a metaprotramming technique to build an inheritance chain.
While I can not decide on the real world impact of storing all those closures,
in theory this approach should enable the compiler to remove all of the
storage overhead. Since, when storing the result into an auto variable
right within scope (as demonstrated in the test), the compiler
sees the concrete type and might be able to boil down the actual
generated virtual function implementations, thereby inlining the
given closures.
Whereas, on the other hand, if we'd go the obvious conventional route
and place the closures into a Map allocated on the stack, I wouldn't
expect the compiler to do data flow analysis to prove this allocation
is not necessary and inline it away.
NOTE: there is now guarantee this inlining trick will ever work.
And, moreover, we don't know anything regarding the runtime effect.
The whole picture is way more involved as it might seem at first sight.
Even if we go the completely conventional route and require every
participating object to supply an implementation of some kind of
"Serializable" interface, we'll end up with a (hand written!)
implementation class for each participating setup, which takes
up space in the code segment of the executable. While the closure
based approach chosen here, consumes data segment (or heap) space
per instance for the functors (or function pointers) representing
the closures, plus code segment space for the closures, but the
latter with a way higher potential for inlining, since the closure
code and the generated virtual functions are necessarily emitted
within the same compilation unit and within a local (inline, not
publickly exposed) scope.
2015-04-05 18:26:49 +02:00
|
|
|
template<class PAR>
|
|
|
|
|
struct Builder
|
|
|
|
|
: PAR
|
|
|
|
|
{
|
|
|
|
|
Builder(PAR par)
|
|
|
|
|
: PAR(par)
|
|
|
|
|
{ }
|
|
|
|
|
|
2015-05-03 05:24:06 +02:00
|
|
|
template<class CLO>
|
|
|
|
|
using Change = ChangeOperation<PAR,CLO>;
|
2015-05-02 02:02:48 +02:00
|
|
|
|
2016-03-18 19:35:48 +01:00
|
|
|
template<class BIN>
|
|
|
|
|
using Collection = ChildCollectionMutator<PAR,BIN>;
|
|
|
|
|
|
2016-02-27 01:47:33 +01:00
|
|
|
using WireTap = TestWireTap<PAR>;
|
|
|
|
|
|
settle on a concrete implementation approach based on inheritance chain
After some reconsideration, I decide to stick to the approach with the closures,
but to use a metaprotramming technique to build an inheritance chain.
While I can not decide on the real world impact of storing all those closures,
in theory this approach should enable the compiler to remove all of the
storage overhead. Since, when storing the result into an auto variable
right within scope (as demonstrated in the test), the compiler
sees the concrete type and might be able to boil down the actual
generated virtual function implementations, thereby inlining the
given closures.
Whereas, on the other hand, if we'd go the obvious conventional route
and place the closures into a Map allocated on the stack, I wouldn't
expect the compiler to do data flow analysis to prove this allocation
is not necessary and inline it away.
NOTE: there is now guarantee this inlining trick will ever work.
And, moreover, we don't know anything regarding the runtime effect.
The whole picture is way more involved as it might seem at first sight.
Even if we go the completely conventional route and require every
participating object to supply an implementation of some kind of
"Serializable" interface, we'll end up with a (hand written!)
implementation class for each participating setup, which takes
up space in the code segment of the executable. While the closure
based approach chosen here, consumes data segment (or heap) space
per instance for the functors (or function pointers) representing
the closures, plus code segment space for the closures, but the
latter with a way higher potential for inlining, since the closure
code and the generated virtual functions are necessarily emitted
within the same compilation unit and within a local (inline, not
publickly exposed) scope.
2015-04-05 18:26:49 +02:00
|
|
|
|
|
|
|
|
/* ==== binding API ==== */
|
|
|
|
|
|
2015-05-03 05:24:06 +02:00
|
|
|
template<typename CLO>
|
|
|
|
|
Builder<Change<CLO>>
|
|
|
|
|
change (Literal attributeID, CLO closure)
|
settle on a concrete implementation approach based on inheritance chain
After some reconsideration, I decide to stick to the approach with the closures,
but to use a metaprotramming technique to build an inheritance chain.
While I can not decide on the real world impact of storing all those closures,
in theory this approach should enable the compiler to remove all of the
storage overhead. Since, when storing the result into an auto variable
right within scope (as demonstrated in the test), the compiler
sees the concrete type and might be able to boil down the actual
generated virtual function implementations, thereby inlining the
given closures.
Whereas, on the other hand, if we'd go the obvious conventional route
and place the closures into a Map allocated on the stack, I wouldn't
expect the compiler to do data flow analysis to prove this allocation
is not necessary and inline it away.
NOTE: there is now guarantee this inlining trick will ever work.
And, moreover, we don't know anything regarding the runtime effect.
The whole picture is way more involved as it might seem at first sight.
Even if we go the completely conventional route and require every
participating object to supply an implementation of some kind of
"Serializable" interface, we'll end up with a (hand written!)
implementation class for each participating setup, which takes
up space in the code segment of the executable. While the closure
based approach chosen here, consumes data segment (or heap) space
per instance for the functors (or function pointers) representing
the closures, plus code segment space for the closures, but the
latter with a way higher potential for inlining, since the closure
code and the generated virtual functions are necessarily emitted
within the same compilation unit and within a local (inline, not
publickly exposed) scope.
2015-04-05 18:26:49 +02:00
|
|
|
{
|
2015-05-03 05:24:06 +02:00
|
|
|
return Change<CLO> (attributeID, closure, *this);
|
settle on a concrete implementation approach based on inheritance chain
After some reconsideration, I decide to stick to the approach with the closures,
but to use a metaprotramming technique to build an inheritance chain.
While I can not decide on the real world impact of storing all those closures,
in theory this approach should enable the compiler to remove all of the
storage overhead. Since, when storing the result into an auto variable
right within scope (as demonstrated in the test), the compiler
sees the concrete type and might be able to boil down the actual
generated virtual function implementations, thereby inlining the
given closures.
Whereas, on the other hand, if we'd go the obvious conventional route
and place the closures into a Map allocated on the stack, I wouldn't
expect the compiler to do data flow analysis to prove this allocation
is not necessary and inline it away.
NOTE: there is now guarantee this inlining trick will ever work.
And, moreover, we don't know anything regarding the runtime effect.
The whole picture is way more involved as it might seem at first sight.
Even if we go the completely conventional route and require every
participating object to supply an implementation of some kind of
"Serializable" interface, we'll end up with a (hand written!)
implementation class for each participating setup, which takes
up space in the code segment of the executable. While the closure
based approach chosen here, consumes data segment (or heap) space
per instance for the functors (or function pointers) representing
the closures, plus code segment space for the closures, but the
latter with a way higher potential for inlining, since the closure
code and the generated virtual functions are necessarily emitted
within the same compilation unit and within a local (inline, not
publickly exposed) scope.
2015-04-05 18:26:49 +02:00
|
|
|
}
|
2016-02-27 01:47:33 +01:00
|
|
|
|
2016-03-18 20:52:35 +01:00
|
|
|
/** set up a binding to a structure of "child objects",
|
|
|
|
|
* implemented through a typical STL container
|
|
|
|
|
* @param collectionBindingSetup as created by invoking a nested DSL,
|
|
|
|
|
* initiated by a builder function `collection(implRef)`, where `implRef`
|
|
|
|
|
* is a (language) reference to a STL compliant container existing somewhere
|
|
|
|
|
* within the otherwise opaque implementation. The type of the container and
|
|
|
|
|
* thus the type of the elements will be picked up, and the returned builder
|
|
|
|
|
* can be further outfitted with the builder methods, which take labmdas as
|
|
|
|
|
* callback into the implementation.
|
|
|
|
|
*/
|
2016-03-18 19:35:48 +01:00
|
|
|
template<typename BIN>
|
|
|
|
|
Builder<Collection<BIN>>
|
|
|
|
|
attach (BIN&& collectionBindingSetup)
|
|
|
|
|
{
|
2016-03-19 16:47:40 +01:00
|
|
|
return Collection<BIN> (std::forward<BIN>(collectionBindingSetup), *this);
|
2016-03-18 19:35:48 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-18 20:52:35 +01:00
|
|
|
/** set up a diagnostic layer, binding to TestMutationTarget.
|
|
|
|
|
* This can be used to monitor the behaviour of the resulting TreeMutator for tests.
|
|
|
|
|
*/
|
2016-02-27 01:47:33 +01:00
|
|
|
Builder<WireTap>
|
|
|
|
|
attachDummy (TestMutationTarget& dummy);
|
|
|
|
|
|
settle on a concrete implementation approach based on inheritance chain
After some reconsideration, I decide to stick to the approach with the closures,
but to use a metaprotramming technique to build an inheritance chain.
While I can not decide on the real world impact of storing all those closures,
in theory this approach should enable the compiler to remove all of the
storage overhead. Since, when storing the result into an auto variable
right within scope (as demonstrated in the test), the compiler
sees the concrete type and might be able to boil down the actual
generated virtual function implementations, thereby inlining the
given closures.
Whereas, on the other hand, if we'd go the obvious conventional route
and place the closures into a Map allocated on the stack, I wouldn't
expect the compiler to do data flow analysis to prove this allocation
is not necessary and inline it away.
NOTE: there is now guarantee this inlining trick will ever work.
And, moreover, we don't know anything regarding the runtime effect.
The whole picture is way more involved as it might seem at first sight.
Even if we go the completely conventional route and require every
participating object to supply an implementation of some kind of
"Serializable" interface, we'll end up with a (hand written!)
implementation class for each participating setup, which takes
up space in the code segment of the executable. While the closure
based approach chosen here, consumes data segment (or heap) space
per instance for the functors (or function pointers) representing
the closures, plus code segment space for the closures, but the
latter with a way higher potential for inlining, since the closure
code and the generated virtual functions are necessarily emitted
within the same compilation unit and within a local (inline, not
publickly exposed) scope.
2015-04-05 18:26:49 +02:00
|
|
|
};
|
2016-02-27 01:47:33 +01:00
|
|
|
|
2016-03-18 20:52:35 +01:00
|
|
|
}//(END) Mutator-Builder...
|
2016-02-27 01:47:33 +01:00
|
|
|
|
2015-04-02 03:30:20 +02:00
|
|
|
|
settle on a concrete implementation approach based on inheritance chain
After some reconsideration, I decide to stick to the approach with the closures,
but to use a metaprotramming technique to build an inheritance chain.
While I can not decide on the real world impact of storing all those closures,
in theory this approach should enable the compiler to remove all of the
storage overhead. Since, when storing the result into an auto variable
right within scope (as demonstrated in the test), the compiler
sees the concrete type and might be able to boil down the actual
generated virtual function implementations, thereby inlining the
given closures.
Whereas, on the other hand, if we'd go the obvious conventional route
and place the closures into a Map allocated on the stack, I wouldn't
expect the compiler to do data flow analysis to prove this allocation
is not necessary and inline it away.
NOTE: there is now guarantee this inlining trick will ever work.
And, moreover, we don't know anything regarding the runtime effect.
The whole picture is way more involved as it might seem at first sight.
Even if we go the completely conventional route and require every
participating object to supply an implementation of some kind of
"Serializable" interface, we'll end up with a (hand written!)
implementation class for each participating setup, which takes
up space in the code segment of the executable. While the closure
based approach chosen here, consumes data segment (or heap) space
per instance for the functors (or function pointers) representing
the closures, plus code segment space for the closures, but the
latter with a way higher potential for inlining, since the closure
code and the generated virtual functions are necessarily emitted
within the same compilation unit and within a local (inline, not
publickly exposed) scope.
2015-04-05 18:26:49 +02:00
|
|
|
Builder<TreeMutator>
|
|
|
|
|
TreeMutator::build ()
|
|
|
|
|
{
|
2015-04-13 15:49:38 +02:00
|
|
|
return TreeMutator();
|
settle on a concrete implementation approach based on inheritance chain
After some reconsideration, I decide to stick to the approach with the closures,
but to use a metaprotramming technique to build an inheritance chain.
While I can not decide on the real world impact of storing all those closures,
in theory this approach should enable the compiler to remove all of the
storage overhead. Since, when storing the result into an auto variable
right within scope (as demonstrated in the test), the compiler
sees the concrete type and might be able to boil down the actual
generated virtual function implementations, thereby inlining the
given closures.
Whereas, on the other hand, if we'd go the obvious conventional route
and place the closures into a Map allocated on the stack, I wouldn't
expect the compiler to do data flow analysis to prove this allocation
is not necessary and inline it away.
NOTE: there is now guarantee this inlining trick will ever work.
And, moreover, we don't know anything regarding the runtime effect.
The whole picture is way more involved as it might seem at first sight.
Even if we go the completely conventional route and require every
participating object to supply an implementation of some kind of
"Serializable" interface, we'll end up with a (hand written!)
implementation class for each participating setup, which takes
up space in the code segment of the executable. While the closure
based approach chosen here, consumes data segment (or heap) space
per instance for the functors (or function pointers) representing
the closures, plus code segment space for the closures, but the
latter with a way higher potential for inlining, since the closure
code and the generated virtual functions are necessarily emitted
within the same compilation unit and within a local (inline, not
publickly exposed) scope.
2015-04-05 18:26:49 +02:00
|
|
|
}
|
2015-04-02 03:30:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
}} // namespace lib::diff
|
|
|
|
|
#endif /*LIB_DIFF_TREE_MUTATOR_H*/
|