LUMIERA.clone/src/lib/diff/tree-diff-traits.hpp

223 lines
6.8 KiB
C++

/*
TREE-DIFF-TRAITS.hpp - definitions to control tree mutator binding
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-diff-traits.hpp
** Definitions and Properties to guide automated tree mutator binding.
** Decision how to access the target structure and how to construct
** a suitable TreeMutator as attached to this opaque target data.
**
** In a nutshell, if some private data structure wants to receive
** mutation diff messages...
** - it must either implement the interface DiffMutable
** - or provide the function point `void buildMutator(TreeMutator::Handle)`
** Additionally, when the size of the custom TreeMutator object exceeds some
** hard wired limit (200 bytes), then the target type also needs to define
** the extension point `size_t treeMutatorSize(TargetType)`
**
** All of this boils down to somewhere / somehow using the TreeMutator::Builder
** (a DSL API) to construct a custom binding, which allows to talk to our
** private data structure through the TreeMutator interface.
**
** @see DiffComplexApplication_test
** @see DiffTreeApplication_test
** @see DiffListApplication_test
** @see GenNodeBasic_test
** @see tree-diff.hpp
**
*/
#ifndef LIB_DIFF_TREE_DIFF_TRAITS_H
#define LIB_DIFF_TREE_DIFF_TRAITS_H
#include "lib/diff/tree-mutator.hpp"
#include "lib/diff/diff-mutable.hpp"
#include "lib/null-value.hpp"
#include "lib/util.hpp"
#include <utility>
#include <stack>
namespace lib {
namespace diff{
/* ======= Hints / Heuristics for the required TreeMutator buffer size ======= */
/**
* Heuristics to guide the allocation for nested TreeMutator.
* When applying a structural (tree) diff, the (otherwise undisclosed)
* target data structure needs to supply a TreeMutator implementation
* properly wired to the internal opaque data elements. Typically, this
* custom TreeMutator relies on several lambdas and closures, which
* require a variable and hard to guess amount of storage for back pointers
* and embedded parametrisation. More so, when the diff application opens
* nested scopes within the target data. The TreeDiffMutatorBinding relies
* on a (likewise opaque) ScopeManager implementation to maintain a stack
* of heap allocated buffers, where the mentioned nested TreeMutator
* implementations can be built and operated during the mutation process.
*
* The default for buffer dimensions includes a safety margin and is thus
* quite expensive -- even though this is just a temporary working buffer.
* Thus we offer a hook for explicit or partial specialisations to control
* the very common cases known to work with smaller buffer sizes.
*
* \par future extensions
* We might consider to make this system dynamic, in case buffer allocation
* for tree diff application becomes an issue in general. We might then guard
* the whole diff application location with try-catch blocks and allow thus
* for learning the right setting at runtime; obviously we'd then also have to
* memorise our findings somehow within the dynamic application configuration.
*
* @see tree-diff-application.hpp
* @see DiffComplexApplication_test
*/
template<class TAR, typename SEL =void>
struct TreeMutatorSizeTraits
{
enum { siz = 200 };
};
constexpr size_t
treeMutatorSize (...) ///< fallback to detect absence of a custom definition
{
return 0;
}
template<typename T>
constexpr T*
getSelector()
{
return static_cast<T*> (nullptr);
}
template<typename T>
struct defines_custom_BufferSize
{
enum { value = 0 < treeMutatorSize (getSelector<T>()) };
};
template<class TAR>
struct TreeMutatorSizeTraits<TAR, enable_if<defines_custom_BufferSize<TAR>> >
{
enum { siz = treeMutatorSize (getSelector<TAR>()) };
};
/* ======= derive TreeMutator binding for a given opaque data structure ======= */
using meta::enable_if;
using meta::Yes_t;
using meta::No_t;
using std::is_same;
/**
* helper to detect presence of a
* TreeMutator builder function
*/
template<typename T>
class exposes_MutatorBuilder
{
META_DETECT_FUNCTION (void, buildMutator, (TreeMutator::Handle));
public:
enum{ value = HasFunSig_buildMutator<T>::value
and not is_same<T, DiffMutable>::value};
};
template<class TAR, typename SEL =void>
struct TreeDiffTraits
: std::false_type
{
static_assert (!sizeof(TAR), "TreeDiffTraits: Unable to access or build a TreeMutator for this target data.");
};
template<class TAR>
struct TreeDiffTraits<TAR, enable_if<is_same<TAR, DiffMutable>>>
: std::true_type
{
using Ret = DiffMutable&;
};
template<class TAR>
struct TreeDiffTraits<TAR, enable_if<exposes_MutatorBuilder<TAR>>>
: std::true_type
{
class Wrapper
: public DiffMutable
{
TAR& subject_;
/** implement the TreeMutator interface,
* by forwarding to a known implementation function
* on the wrapped target data type */
virtual void
buildMutator (TreeMutator::Handle handle)
{
subject_.buildMutator (handle);
}
public:
Wrapper(TAR& subj)
: subject_(subj)
{ }
};
using Ret = Wrapper;
};
/**
* public access point to this configuration machinery
* @return depending on the circumstances, either a direct reference
* to DiffMutable, or a transient functor object implementing
* the DiffMutable interface
* @warning beware of dangling references! either use this call immediately
* inline, or (preferably) use an `auto` typed variable to hold
* the return value in local scope as long as necessary
*/
template<class TAR>
typename TreeDiffTraits<TAR>::Ret
mutatorBinding (TAR& subject)
{
using Wrapper = typename TreeDiffTraits<TAR>::Ret;
return Wrapper(subject);
}
}} // namespace lib::diff
#endif /*LIB_DIFF_TREE_DIFF_TRAITS_H*/