Diff Handling and Diff Application: framework and definitions

factored out of the concept test built last week.
This commit is contained in:
Fischlurch 2014-12-15 03:21:19 +01:00
parent 658698407e
commit 9707a8982c
6 changed files with 166 additions and 1610 deletions

View file

@ -23,7 +23,57 @@
/** @file diff-language.hpp
** Fundamental definitions for a representation of changes.
** We describe differences in data structures or changes to be applied
** in the form of a "linearised diff language". Such a diff can be represented
** as a sequence of tokens of constant size. Using a linearised constant size
** representation allows to process diff generation and diff application in
** a pipeline, enabling maximum decoupling of sender and receiver.
** Changes sent by diff serve as a generic meta-representation to keep separate
** and different representations of the same logical structure in sync. This allows
** for tight cooperation between strictly separated components, without the need
** of a fixed, predefined and shared data structure.
**
** \par Basic Assumptions
** While the \em linearisation folds knowledge about the underlying data structure
** down into the actual diff, we deliberately assume that the data to be diffed is
** \em structured data. Moreover, we'll assume implicitly that this data is \em typed,
** and we'll assume explicitly that the atomic elements in the data structure have a
** well-defined identity and can be compared with the \c == operator. We treat those
** elements as values, which can be copied and moved cheaply. We include a copy of
** all content elements right within the tokens of the diff language, either to
** send the actual content data this way, or to serve as redundancy to verify
** proper application of the changes at the diff receiver.
**
** \par Solution Pattern
** The representation of this linearised diff language relies on a specialised form
** of the <b>visitor pattern</b>: We assume the vocabulary of the diff language to be
** relatively fixed, while the actual effect when consuming the stream of diff tokens
** is provided as a private detail of the receiver, implemented as a concrete "Interpreter"
** (visitor) of the specific diff language flavour in use. Thus, our implementation relies
** on \em double-dispatch, based both on the type of the individual diff tokens and on the
** concrete implementation of the Interpreter. The typical usage will employ an DiffApplicator,
** so the "interpretation" of the language means to apply it to a target data structure in
** this standard case.
**
** Due to the nature of double-dispatch, the interpretation of each token requires two
** indirections. The first indirection forwards to a handler function corresponding to the
** token, while the second indirection uses the VTable of the concrete Interpreter to pick
** the actual implementation of this handler function for this specific case. Basically
** the individual token ("verb") in the language is characterised by the handler function
** it corresponds to (thus the meaning of a \em verb, an operation). To support diagnostics,
** each token also bears a string id. And in addition, each token carries a single data
** content element as argument. The idea is, that the "verbs", the handler functions and
** the symbolic IDs are named alike (use the macro DiffStep_CTOR to define the tokens
** in according to that rule). Such a combination of verb and data argument is called
** a DiffStep, since it represents a single step in the process of describing changes
** or transforming a data structure. For example, a list diff language can be built
** using the following four verbs:
** - pick-next
** - insert-new
** - delete-next
** - push-back-next
**
** @see list-diff-application.hpp
** @see diff-list-application-test.cpp
** @see VerbToken
**
@ -34,32 +84,36 @@
#define LIB_DIFF_DIFF_LANGUAGE_H
#include "lib/test/run.hpp"
#include "lib/error.hpp"
#include "lib/verb-token.hpp"
#include "lib/util.hpp"
#include "lib/iter-adapter-stl.hpp"
#include "lib/format-string.hpp"
#include <boost/noncopyable.hpp>
#include <functional>
#include <algorithm>
#include <string>
#include <vector>
#include <tuple>
using util::isnil;
using std::string;
using util::_Fmt;
using std::vector;
using std::move;
namespace lib {
namespace test{
namespace diff{
namespace error = lumiera::error;
LUMIERA_ERROR_DEFINE(DIFF_CONFLICT, "Collision in diff application: contents of target not as expected.");
/**
* Definition frame for a language to describe differences in data structures.
* We use a \em linearised representation as a sequence of DiffStep messages
* of constant size. The actual verbs of the diff language in use are defined
* through the operations of the \em Interpreter; each #VerbToken corresponds
* to a handler function on the Interpreter interface. In addition to the verb,
* each DiffStep also carries an content data element, like e.g. "insert elm
* at next position".
* @param I Interpreter interface of the actual language to use
* @param E type of the elementary data elements.
* @remarks recommendation is to set up a builder function for each distinct
* kind of verb to be used in the actual language: this #diffTokenBuilder
* takes the data element as argument and wraps a copy in the created
* DiffStep of the specific kind it is configured for.
*/
template< class I, typename E>
struct DiffLanguage
{
@ -94,6 +148,7 @@ namespace test{
using HandlerFun = void (I::*) (E);
template<typename SIG_HANDLER>
struct DiffStepBuilder;
@ -143,141 +198,27 @@ namespace test{
const auto _ID_ = diffTokenBuilder (&Interpreter::_ID_, STRINGIFY(_ID_));
template<typename E>
class ListDiffInterpreter
{
public:
virtual ~ListDiffInterpreter() { } ///< this is an interface
virtual void ins(E e) =0;
virtual void del(E e) =0;
virtual void pick(E e) =0;
virtual void push(E e) =0;
};
template<typename E>
using ListDiffLanguage = DiffLanguage<ListDiffInterpreter<E>, E>;
template<class CON>
class DiffApplicationStrategy;
/* ==== Implementation Pattern for Diff Application ==== */
/**
* concrete strategy to apply a list diff to a target sequence given as vector.
* The implementation swaps aside the existing content of the target sequence
* and then consumes it step by step, while building up the altered content
* within the previously emptied target vector. Whenever possible, elements
* are moved directly to the target location.
* @throws lumiera::error::State when diff application fails due to the
* target sequence being different than assumed by the given diff.
* @warning behaves only EX_SANE in case of diff application errors,
* i.e. only partially modified / rebuilt sequence might be
* in the target when diff application is aborted
* Extension point: define how a specific diff language
* can be applied to elements in a concrete container
* @remarks the actual diff fed to the DiffApplicator
* assumes that this DiffApplicationStrategy is
* an Interpreter for the given diff language.
* @warning the actual language remains unspecified;
* it is picked from the visible context.
*/
template<typename E, typename...ARGS>
class DiffApplicationStrategy<vector<E,ARGS...>>
: public ListDiffInterpreter<E>
{
using Vec = vector<E,ARGS...>;
using Iter = typename Vec::iterator;
Vec orig_;
Vec& seq_;
Iter pos_;
bool
end_of_target()
{
return pos_ == orig_.end();
}
void
__expect_in_target (E const& elm, Literal oper)
{
if (end_of_target())
throw error::State(_Fmt("Unable to %s element %s from target as demanded; "
"no (further) elements in target sequence") % oper % elm
, LUMIERA_ERROR_DIFF_CONFLICT);
if (*pos_ != elm)
throw error::State(_Fmt("Unable to %s element %s from target as demanded; "
"found element %s on current target position instead")
% oper % elm % *pos_
, LUMIERA_ERROR_DIFF_CONFLICT);
}
void
__expect_further_elements()
{
if (end_of_target())
throw error::State("Premature end of target sequence; unable to apply diff further."
, LUMIERA_ERROR_DIFF_CONFLICT);
}
void
__expect_found (E const& elm, Iter const& targetPos)
{
if (targetPos == orig_.end())
throw error::State(_Fmt("Premature end of sequence; unable to locate "
"element %s as reference point in target.") % elm
, LUMIERA_ERROR_DIFF_CONFLICT);
}
/* == Implementation of the diff application primitives == */
void
ins (E elm)
{
seq_.push_back(elm);
}
void
del (E elm)
{
__expect_in_target(elm, "remove");
++pos_;
}
void
pick (E elm)
{
__expect_in_target(elm, "pick");
seq_.push_back (move(*pos_));
++pos_;
}
void
push (E anchor)
{
__expect_further_elements();
E elm(move(*pos_)); // consume current source element
++pos_;
// locate the insert position behind the given reference anchor
Iter insertPos = std::find(pos_, orig_.end(), anchor);
__expect_found (anchor, insertPos);
// inserting the "pushed back" element behind the found position
// this might lead to reallocation and thus invalidate the iterators
auto currIdx = pos_ - orig_.begin();
orig_.insert (++insertPos, move(elm));
pos_ = orig_.begin() + currIdx;
}
public:
explicit
DiffApplicationStrategy(vector<E>& targetVector)
: seq_(targetVector)
, pos_(seq_.begin())
{
swap (seq_, orig_); // pos_ still refers to original input sequence, which has been moved to orig_
seq_.reserve (targetVector.size() * 120 / 100); // heuristics for storage pre-allocation
}
};
template<class CON>
class DiffApplicationStrategy;
/**
@ -285,7 +226,7 @@ namespace test{
* The usage pattern is as follows
* #. construct a DiffApplicator instance, wrapping the target sequence
* #. feed the list diff (sequence of diff verbs) to the #consume function
* #. the wrapped target sequence has been altered, to conform to the given diff
* #. the wrapped target sequence has been altered, to conform to the given diff
* @note a suitable DiffApplicationStrategy will be picked, based on the type
* of the concrete target sequence given at construction. (Effectively
* this means you need a suitable DiffApplicationStrategy specialisation,
@ -295,9 +236,9 @@ namespace test{
class DiffApplicator
: boost::noncopyable
{
using Receiver = DiffApplicationStrategy<SEQ>;
using Interpreter = DiffApplicationStrategy<SEQ>;
Receiver target_;
Interpreter target_;
public:
explicit
@ -314,105 +255,7 @@ namespace test{
}
};
namespace {
template<class CON>
using ContentSnapshot = iter_stl::IterSnapshot<typename CON::value_type>;
}
template<class CON>
inline ContentSnapshot<CON>
snapshot(CON const& con)
{
return ContentSnapshot<CON>(begin(con), end(con));
}
template<class VAL>
inline iter_stl::IterSnapshot<VAL>
snapshot(std::initializer_list<VAL> const&& ili)
{
using OnceIter = iter_stl::IterSnapshot<VAL>;
return OnceIter(begin(ili), end(ili));
}
namespace {//Test fixture....
using DataSeq = vector<string>;
#define TOK(id) id(STRINGIFY(id))
string TOK(a1), TOK(a2), TOK(a3), TOK(a4), TOK(a5);
string TOK(b1), TOK(b2), TOK(b3), TOK(b4);
using Interpreter = ListDiffInterpreter<string>;
using DiffStep = ListDiffLanguage<string>::DiffStep;
using DiffSeq = iter_stl::IterSnapshot<DiffStep>;
DiffStep_CTOR(ins);
DiffStep_CTOR(del);
DiffStep_CTOR(pick);
DiffStep_CTOR(push);
inline DiffSeq
generateTestDiff()
{
return snapshot({del(a1)
, del(a2)
, ins(b1)
, pick(a3)
, push(a5)
, pick(a5)
, ins(b2)
, ins(b3)
, pick(a4)
, ins(b4)
});
}
}//(End)Test fixture
/***********************************************************************//**
* @test Demonstration/Concept: a description language for list differences.
* The representation is given as a linearised sequence of verb tokens.
* This test demonstrates the application of such a diff representation
* to a given source list, transforming this list to hold the intended
* target list contents.
*
* @see session-structure-mapping-test.cpp
*/
class DiffListApplication_test : public Test
{
virtual void
run (Arg)
{
DataSeq src({a1,a2,a3,a4,a5});
auto diff = generateTestDiff();
CHECK (!isnil (diff));
DataSeq target = src;
DiffApplicator<DataSeq> application(target);
application.consume(diff);
CHECK (isnil (diff));
CHECK (!isnil (target));
CHECK (src != target);
CHECK (target == DataSeq({b1,a3,a5,b2,b3,a4,b4}));
}
};
/** Register this test class... */
LAUNCHER (DiffListApplication_test, "unit common");
}} // namespace lib::test
}} // namespace lib::diff
#endif /*LIB_DIFF_DIFF_LANGUAGE_H*/

View file

@ -22,10 +22,16 @@
/** @file list-diff-application.hpp
** Fundamental definitions for a representation of changes.
** Apply a "list diff" to a concrete sequence of elements in a container.
** This header provides specialisation(s) of the DiffApplicationStrategy to
** actual containers, choosing an implementation approach suitable for this
** specific kind of container. Together with a #DiffApplicator, this allows
** to receive the description of changes (as a linearised sequence of
** DiffStep tokens) and apply them to a given concrete sequence of data
** elements, thereby transforming the contents of this target sequence.
**
** @see diff-list-application-test.cpp
** @see VerbToken
** @see ListDiffLanguage
**
*/
@ -34,136 +40,20 @@
#define LIB_DIFF_LIST_DIFF_APPLICATION_H
#include "lib/test/run.hpp"
#include "lib/verb-token.hpp"
#include "lib/util.hpp"
#include "lib/iter-adapter-stl.hpp"
#include "lib/diff/list-diff.hpp"
#include "lib/format-string.hpp"
#include <boost/noncopyable.hpp>
#include <functional>
#include <algorithm>
#include <string>
#include <vector>
#include <tuple>
using util::isnil;
using std::string;
using util::_Fmt;
using std::vector;
using std::move;
namespace lib {
namespace test{
namespace error = lumiera::error;
namespace diff{
LUMIERA_ERROR_DEFINE(DIFF_CONFLICT, "Collision in diff application: contents of target not as expected.");
template< class I, typename E>
struct DiffLanguage
{
using DiffVerb = VerbToken<I, void(E)>;
using VerbTok = std::tuple<DiffVerb, E>;
struct DiffStep
: VerbTok
{
DiffVerb& verb() { return std::get<0>(*this); }
E elm() { return std::get<1>(*this); }
DiffStep(DiffVerb verb, E e)
: VerbTok(verb,e)
{ }
operator string() const
{
return string(verb()) + "("+string(elm())+")";
}
void
applyTo (I& interpreter)
{
verb().applyTo (interpreter, elm());
}
};
};
template<class I, typename E>
using HandlerFun = void (I::*) (E);
template<typename SIG_HANDLER>
struct DiffStepBuilder;
/** generator to produce specific language tokens */
template<class I, typename E>
struct DiffStepBuilder<HandlerFun<I,E>>
{
using Lang = DiffLanguage<I,E>;
using Step = typename Lang::DiffStep;
using Verb = typename Lang::DiffVerb;
HandlerFun<I,E> handler;
Literal id;
Step
operator() (E elm) const
{
return { Verb(handler,id), elm };
}
};
/** set up a diff language token generator,
* based on the specific handler function given.
* This generator will produce tokens, wrapping concrete content elements
* of type \c E. In the end, the purpose is to send a sequence of such tokens
* around, to feed them to a consumer, which implements the \em Interpreter
* interface of the diff language. E.g. this consumer might apply the diff.
*/
template<class H>
inline DiffStepBuilder<H>
diffTokenBuilder (H handlerFun, Literal id)
{
return { handlerFun, id };
}
/** shortcut to define tokens of the diff language.
* Use it to define namespace level function objects, which,
* when supplied with an argument value of type \c E, will generate
* a specific language token wrapping a copy of this element.
* @note need a typedef \c Interpreter at usage site
* to refer to the actual language interpreter interface;
* the template parameters of the Language and the element
* type will be picked up from the given member function pointer.
*/
#define DiffStep_CTOR(_ID_) \
const auto _ID_ = diffTokenBuilder (&Interpreter::_ID_, STRINGIFY(_ID_));
template<typename E>
class ListDiffInterpreter
{
public:
virtual ~ListDiffInterpreter() { } ///< this is an interface
virtual void ins(E e) =0;
virtual void del(E e) =0;
virtual void pick(E e) =0;
virtual void push(E e) =0;
};
template<typename E>
using ListDiffLanguage = DiffLanguage<ListDiffInterpreter<E>, E>;
template<class CON>
class DiffApplicationStrategy;
using util::_Fmt;
using std::vector;
using std::move;
/**
* concrete strategy to apply a list diff to a target sequence given as vector.
@ -280,139 +170,8 @@ namespace test{
};
/**
* generic builder to apply a list diff to a given target sequence.
* The usage pattern is as follows
* #. construct a DiffApplicator instance, wrapping the target sequence
* #. feed the list diff (sequence of diff verbs) to the #consume function
* #. the wrapped target sequence has been altered, to conform to the given diff
* @note a suitable DiffApplicationStrategy will be picked, based on the type
* of the concrete target sequence given at construction. (Effectively
* this means you need a suitable DiffApplicationStrategy specialisation,
* e.g. for a target sequence within a vector)
*/
template<class SEQ>
class DiffApplicator
: boost::noncopyable
{
using Receiver = DiffApplicationStrategy<SEQ>;
Receiver target_;
public:
explicit
DiffApplicator(SEQ& targetSeq)
: target_(targetSeq)
{ }
template<class DIFF>
void
consume (DIFF&& diff)
{
for ( ; diff; ++diff )
diff->applyTo(target_);
}
};
namespace {
template<class CON>
using ContentSnapshot = iter_stl::IterSnapshot<typename CON::value_type>;
}
template<class CON>
inline ContentSnapshot<CON>
snapshot(CON const& con)
{
return ContentSnapshot<CON>(begin(con), end(con));
}
template<class VAL>
inline iter_stl::IterSnapshot<VAL>
snapshot(std::initializer_list<VAL> const&& ili)
{
using OnceIter = iter_stl::IterSnapshot<VAL>;
return OnceIter(begin(ili), end(ili));
}
namespace {//Test fixture....
using DataSeq = vector<string>;
#define TOK(id) id(STRINGIFY(id))
string TOK(a1), TOK(a2), TOK(a3), TOK(a4), TOK(a5);
string TOK(b1), TOK(b2), TOK(b3), TOK(b4);
using Interpreter = ListDiffInterpreter<string>;
using DiffStep = ListDiffLanguage<string>::DiffStep;
using DiffSeq = iter_stl::IterSnapshot<DiffStep>;
DiffStep_CTOR(ins);
DiffStep_CTOR(del);
DiffStep_CTOR(pick);
DiffStep_CTOR(push);
inline DiffSeq
generateTestDiff()
{
return snapshot({del(a1)
, del(a2)
, ins(b1)
, pick(a3)
, push(a5)
, pick(a5)
, ins(b2)
, ins(b3)
, pick(a4)
, ins(b4)
});
}
}//(End)Test fixture
/***********************************************************************//**
* @test Demonstration/Concept: a description language for list differences.
* The representation is given as a linearised sequence of verb tokens.
* This test demonstrates the application of such a diff representation
* to a given source list, transforming this list to hold the intended
* target list contents.
*
* @see session-structure-mapping-test.cpp
*/
class DiffListApplication_test : public Test
{
virtual void
run (Arg)
{
DataSeq src({a1,a2,a3,a4,a5});
auto diff = generateTestDiff();
CHECK (!isnil (diff));
DataSeq target = src;
DiffApplicator<DataSeq> application(target);
application.consume(diff);
CHECK (isnil (diff));
CHECK (!isnil (target));
CHECK (src != target);
CHECK (target == DataSeq({b1,a3,a5,b2,b3,a4,b4}));
}
};
/** Register this test class... */
LAUNCHER (DiffListApplication_test, "unit common");
}} // namespace lib::test
}} // namespace lib::diff
#endif /*LIB_DIFF_LIST_DIFF_APPLICATION_H*/

View file

@ -22,8 +22,13 @@
/** @file list-diff.hpp
** Fundamental definitions for a representation of changes.
** A token language to represent changes in a list of elements.
** In combination with the #DiffLanguage framework, this building block
** defines the set of operations to express changes in a given list of elements.
** By implementing the #ListDiffInterpreter interface (visitor), a concrete usage
** can receive such a diff description and e.g. apply it to a target data structure.
**
** @see diff-language.cpp
** @see diff-list-application-test.cpp
** @see VerbToken
**
@ -34,115 +39,30 @@
#define LIB_DIFF_LIST_DIFF_H
#include "lib/test/run.hpp"
#include "lib/verb-token.hpp"
#include "lib/util.hpp"
#include "lib/iter-adapter-stl.hpp"
#include "lib/format-string.hpp"
#include <boost/noncopyable.hpp>
#include <functional>
#include <algorithm>
#include <string>
#include <vector>
#include <tuple>
using util::isnil;
using std::string;
using util::_Fmt;
using std::vector;
using std::move;
#include "lib/diff/diff-language.hpp"
namespace lib {
namespace test{
namespace error = lumiera::error;
LUMIERA_ERROR_DEFINE(DIFF_CONFLICT, "Collision in diff application: contents of target not as expected.");
template< class I, typename E>
struct DiffLanguage
{
using DiffVerb = VerbToken<I, void(E)>;
using VerbTok = std::tuple<DiffVerb, E>;
struct DiffStep
: VerbTok
{
DiffVerb& verb() { return std::get<0>(*this); }
E elm() { return std::get<1>(*this); }
DiffStep(DiffVerb verb, E e)
: VerbTok(verb,e)
{ }
operator string() const
{
return string(verb()) + "("+string(elm())+")";
}
void
applyTo (I& interpreter)
{
verb().applyTo (interpreter, elm());
}
};
};
template<class I, typename E>
using HandlerFun = void (I::*) (E);
namespace diff{
template<typename SIG_HANDLER>
struct DiffStepBuilder;
/** generator to produce specific language tokens */
template<class I, typename E>
struct DiffStepBuilder<HandlerFun<I,E>>
{
using Lang = DiffLanguage<I,E>;
using Step = typename Lang::DiffStep;
using Verb = typename Lang::DiffVerb;
HandlerFun<I,E> handler;
Literal id;
Step
operator() (E elm) const
{
return { Verb(handler,id), elm };
}
};
/** set up a diff language token generator,
* based on the specific handler function given.
* This generator will produce tokens, wrapping concrete content elements
* of type \c E. In the end, the purpose is to send a sequence of such tokens
* around, to feed them to a consumer, which implements the \em Interpreter
* interface of the diff language. E.g. this consumer might apply the diff.
/**
* Interpreter interface to define the operations ("verbs"),
* which describe differences or changes in a given list of data elements.
* The meaning of the verbs is as follows:
* - \c ins prompts to insert the given argument element at the \em current
* processing position into the target sequence. This operation
* allows to inject new data
* - \c del requires to delete the \em next element at \em current position.
* For sake of verification, the element to be deleted is also
* included as argument (redundancy).
* - \c pick just accepts the \em next element at \em current position into
* the resulting altered sequence. The element is given redundantly.
* - \c push effects a re-ordering of the target list contents: it requires
* to \em push the \em next element at \em current processing position
* back further into the list, to be placed at a position \em behind
* the reference element given as argument.
*/
template<class H>
inline DiffStepBuilder<H>
diffTokenBuilder (H handlerFun, Literal id)
{
return { handlerFun, id };
}
/** shortcut to define tokens of the diff language.
* Use it to define namespace level function objects, which,
* when supplied with an argument value of type \c E, will generate
* a specific language token wrapping a copy of this element.
* @note need a typedef \c Interpreter at usage site
* to refer to the actual language interpreter interface;
* the template parameters of the Language and the element
* type will be picked up from the given member function pointer.
*/
#define DiffStep_CTOR(_ID_) \
const auto _ID_ = diffTokenBuilder (&Interpreter::_ID_, STRINGIFY(_ID_));
template<typename E>
class ListDiffInterpreter
{
@ -161,258 +81,5 @@ namespace test{
template<class CON>
class DiffApplicationStrategy;
/**
* concrete strategy to apply a list diff to a target sequence given as vector.
* The implementation swaps aside the existing content of the target sequence
* and then consumes it step by step, while building up the altered content
* within the previously emptied target vector. Whenever possible, elements
* are moved directly to the target location.
* @throws lumiera::error::State when diff application fails due to the
* target sequence being different than assumed by the given diff.
* @warning behaves only EX_SANE in case of diff application errors,
* i.e. only partially modified / rebuilt sequence might be
* in the target when diff application is aborted
*/
template<typename E, typename...ARGS>
class DiffApplicationStrategy<vector<E,ARGS...>>
: public ListDiffInterpreter<E>
{
using Vec = vector<E,ARGS...>;
using Iter = typename Vec::iterator;
Vec orig_;
Vec& seq_;
Iter pos_;
bool
end_of_target()
{
return pos_ == orig_.end();
}
void
__expect_in_target (E const& elm, Literal oper)
{
if (end_of_target())
throw error::State(_Fmt("Unable to %s element %s from target as demanded; "
"no (further) elements in target sequence") % oper % elm
, LUMIERA_ERROR_DIFF_CONFLICT);
if (*pos_ != elm)
throw error::State(_Fmt("Unable to %s element %s from target as demanded; "
"found element %s on current target position instead")
% oper % elm % *pos_
, LUMIERA_ERROR_DIFF_CONFLICT);
}
void
__expect_further_elements()
{
if (end_of_target())
throw error::State("Premature end of target sequence; unable to apply diff further."
, LUMIERA_ERROR_DIFF_CONFLICT);
}
void
__expect_found (E const& elm, Iter const& targetPos)
{
if (targetPos == orig_.end())
throw error::State(_Fmt("Premature end of sequence; unable to locate "
"element %s as reference point in target.") % elm
, LUMIERA_ERROR_DIFF_CONFLICT);
}
/* == Implementation of the diff application primitives == */
void
ins (E elm)
{
seq_.push_back(elm);
}
void
del (E elm)
{
__expect_in_target(elm, "remove");
++pos_;
}
void
pick (E elm)
{
__expect_in_target(elm, "pick");
seq_.push_back (move(*pos_));
++pos_;
}
void
push (E anchor)
{
__expect_further_elements();
E elm(move(*pos_)); // consume current source element
++pos_;
// locate the insert position behind the given reference anchor
Iter insertPos = std::find(pos_, orig_.end(), anchor);
__expect_found (anchor, insertPos);
// inserting the "pushed back" element behind the found position
// this might lead to reallocation and thus invalidate the iterators
auto currIdx = pos_ - orig_.begin();
orig_.insert (++insertPos, move(elm));
pos_ = orig_.begin() + currIdx;
}
public:
explicit
DiffApplicationStrategy(vector<E>& targetVector)
: seq_(targetVector)
, pos_(seq_.begin())
{
swap (seq_, orig_); // pos_ still refers to original input sequence, which has been moved to orig_
seq_.reserve (targetVector.size() * 120 / 100); // heuristics for storage pre-allocation
}
};
/**
* generic builder to apply a list diff to a given target sequence.
* The usage pattern is as follows
* #. construct a DiffApplicator instance, wrapping the target sequence
* #. feed the list diff (sequence of diff verbs) to the #consume function
* #. the wrapped target sequence has been altered, to conform to the given diff
* @note a suitable DiffApplicationStrategy will be picked, based on the type
* of the concrete target sequence given at construction. (Effectively
* this means you need a suitable DiffApplicationStrategy specialisation,
* e.g. for a target sequence within a vector)
*/
template<class SEQ>
class DiffApplicator
: boost::noncopyable
{
using Receiver = DiffApplicationStrategy<SEQ>;
Receiver target_;
public:
explicit
DiffApplicator(SEQ& targetSeq)
: target_(targetSeq)
{ }
template<class DIFF>
void
consume (DIFF&& diff)
{
for ( ; diff; ++diff )
diff->applyTo(target_);
}
};
namespace {
template<class CON>
using ContentSnapshot = iter_stl::IterSnapshot<typename CON::value_type>;
}
template<class CON>
inline ContentSnapshot<CON>
snapshot(CON const& con)
{
return ContentSnapshot<CON>(begin(con), end(con));
}
template<class VAL>
inline iter_stl::IterSnapshot<VAL>
snapshot(std::initializer_list<VAL> const&& ili)
{
using OnceIter = iter_stl::IterSnapshot<VAL>;
return OnceIter(begin(ili), end(ili));
}
namespace {//Test fixture....
using DataSeq = vector<string>;
#define TOK(id) id(STRINGIFY(id))
string TOK(a1), TOK(a2), TOK(a3), TOK(a4), TOK(a5);
string TOK(b1), TOK(b2), TOK(b3), TOK(b4);
using Interpreter = ListDiffInterpreter<string>;
using DiffStep = ListDiffLanguage<string>::DiffStep;
using DiffSeq = iter_stl::IterSnapshot<DiffStep>;
DiffStep_CTOR(ins);
DiffStep_CTOR(del);
DiffStep_CTOR(pick);
DiffStep_CTOR(push);
inline DiffSeq
generateTestDiff()
{
return snapshot({del(a1)
, del(a2)
, ins(b1)
, pick(a3)
, push(a5)
, pick(a5)
, ins(b2)
, ins(b3)
, pick(a4)
, ins(b4)
});
}
}//(End)Test fixture
/***********************************************************************//**
* @test Demonstration/Concept: a description language for list differences.
* The representation is given as a linearised sequence of verb tokens.
* This test demonstrates the application of such a diff representation
* to a given source list, transforming this list to hold the intended
* target list contents.
*
* @see session-structure-mapping-test.cpp
*/
class DiffListApplication_test : public Test
{
virtual void
run (Arg)
{
DataSeq src({a1,a2,a3,a4,a5});
auto diff = generateTestDiff();
CHECK (!isnil (diff));
DataSeq target = src;
DiffApplicator<DataSeq> application(target);
application.consume(diff);
CHECK (isnil (diff));
CHECK (!isnil (target));
CHECK (src != target);
CHECK (target == DataSeq({b1,a3,a5,b2,b3,a4,b4}));
}
};
/** Register this test class... */
LAUNCHER (DiffListApplication_test, "unit common");
}} // namespace lib::test
}} // namespace lib::diff
#endif /*LIB_DIFF_LIST_DIFF_H*/

View file

@ -22,7 +22,10 @@
/** @file tree-diff-application.hpp
** Fundamental definitions for a representation of changes.
** Concrete implementation(s) to apply structural changes to hierarchical
** data structures. Together with the generic #DiffApplicator, this allows
** to receive linearised structural diff descriptions and apply them to
** a given target data structure, to effect the correspoinding changes.
**
** @see diff-list-application-test.cpp
** @see VerbToken
@ -34,385 +37,18 @@
#define LIB_DIFF_TREE_DIFF_APPLICATION_H
#include "lib/test/run.hpp"
#include "lib/verb-token.hpp"
#include "lib/util.hpp"
#include "lib/iter-adapter-stl.hpp"
#include "lib/format-string.hpp"
#include <boost/noncopyable.hpp>
#include <functional>
#include <algorithm>
#include <string>
#include <vector>
#include <tuple>
using util::isnil;
using std::string;
using util::_Fmt;
using std::vector;
using std::move;
#include "lib/diff/tree-diff.hpp"
namespace lib {
namespace test{
namespace error = lumiera::error;
namespace diff{
LUMIERA_ERROR_DEFINE(DIFF_CONFLICT, "Collision in diff application: contents of target not as expected.");
template< class I, typename E>
struct DiffLanguage
{
using DiffVerb = VerbToken<I, void(E)>;
using VerbTok = std::tuple<DiffVerb, E>;
struct DiffStep
: VerbTok
{
DiffVerb& verb() { return std::get<0>(*this); }
E elm() { return std::get<1>(*this); }
DiffStep(DiffVerb verb, E e)
: VerbTok(verb,e)
{ }
operator string() const
{
return string(verb()) + "("+string(elm())+")";
}
void
applyTo (I& interpreter)
{
verb().applyTo (interpreter, elm());
}
};
};
template<class I, typename E>
using HandlerFun = void (I::*) (E);
template<typename SIG_HANDLER>
struct DiffStepBuilder;
/** generator to produce specific language tokens */
template<class I, typename E>
struct DiffStepBuilder<HandlerFun<I,E>>
{
using Lang = DiffLanguage<I,E>;
using Step = typename Lang::DiffStep;
using Verb = typename Lang::DiffVerb;
HandlerFun<I,E> handler;
Literal id;
Step
operator() (E elm) const
{
return { Verb(handler,id), elm };
}
};
/** set up a diff language token generator,
* based on the specific handler function given.
* This generator will produce tokens, wrapping concrete content elements
* of type \c E. In the end, the purpose is to send a sequence of such tokens
* around, to feed them to a consumer, which implements the \em Interpreter
* interface of the diff language. E.g. this consumer might apply the diff.
*/
template<class H>
inline DiffStepBuilder<H>
diffTokenBuilder (H handlerFun, Literal id)
{
return { handlerFun, id };
}
/** shortcut to define tokens of the diff language.
* Use it to define namespace level function objects, which,
* when supplied with an argument value of type \c E, will generate
* a specific language token wrapping a copy of this element.
* @note need a typedef \c Interpreter at usage site
* to refer to the actual language interpreter interface;
* the template parameters of the Language and the element
* type will be picked up from the given member function pointer.
*/
#define DiffStep_CTOR(_ID_) \
const auto _ID_ = diffTokenBuilder (&Interpreter::_ID_, STRINGIFY(_ID_));
template<typename E>
class ListDiffInterpreter
{
public:
virtual ~ListDiffInterpreter() { } ///< this is an interface
virtual void ins(E e) =0;
virtual void del(E e) =0;
virtual void pick(E e) =0;
virtual void push(E e) =0;
};
template<typename E>
using ListDiffLanguage = DiffLanguage<ListDiffInterpreter<E>, E>;
template<class CON>
class DiffApplicationStrategy;
/**
* concrete strategy to apply a list diff to a target sequence given as vector.
* The implementation swaps aside the existing content of the target sequence
* and then consumes it step by step, while building up the altered content
* within the previously emptied target vector. Whenever possible, elements
* are moved directly to the target location.
* @throws lumiera::error::State when diff application fails due to the
* target sequence being different than assumed by the given diff.
* @warning behaves only EX_SANE in case of diff application errors,
* i.e. only partially modified / rebuilt sequence might be
* in the target when diff application is aborted
*/
template<typename E, typename...ARGS>
class DiffApplicationStrategy<vector<E,ARGS...>>
class DiffApplicationStrategy<wtf<E,ARGS...>>
: public ListDiffInterpreter<E>
{
using Vec = vector<E,ARGS...>;
using Iter = typename Vec::iterator;
Vec orig_;
Vec& seq_;
Iter pos_;
bool
end_of_target()
{
return pos_ == orig_.end();
}
void
__expect_in_target (E const& elm, Literal oper)
{
if (end_of_target())
throw error::State(_Fmt("Unable to %s element %s from target as demanded; "
"no (further) elements in target sequence") % oper % elm
, LUMIERA_ERROR_DIFF_CONFLICT);
if (*pos_ != elm)
throw error::State(_Fmt("Unable to %s element %s from target as demanded; "
"found element %s on current target position instead")
% oper % elm % *pos_
, LUMIERA_ERROR_DIFF_CONFLICT);
}
void
__expect_further_elements()
{
if (end_of_target())
throw error::State("Premature end of target sequence; unable to apply diff further."
, LUMIERA_ERROR_DIFF_CONFLICT);
}
void
__expect_found (E const& elm, Iter const& targetPos)
{
if (targetPos == orig_.end())
throw error::State(_Fmt("Premature end of sequence; unable to locate "
"element %s as reference point in target.") % elm
, LUMIERA_ERROR_DIFF_CONFLICT);
}
/* == Implementation of the diff application primitives == */
void
ins (E elm)
{
seq_.push_back(elm);
}
void
del (E elm)
{
__expect_in_target(elm, "remove");
++pos_;
}
void
pick (E elm)
{
__expect_in_target(elm, "pick");
seq_.push_back (move(*pos_));
++pos_;
}
void
push (E anchor)
{
__expect_further_elements();
E elm(move(*pos_)); // consume current source element
++pos_;
// locate the insert position behind the given reference anchor
Iter insertPos = std::find(pos_, orig_.end(), anchor);
__expect_found (anchor, insertPos);
// inserting the "pushed back" element behind the found position
// this might lead to reallocation and thus invalidate the iterators
auto currIdx = pos_ - orig_.begin();
orig_.insert (++insertPos, move(elm));
pos_ = orig_.begin() + currIdx;
}
public:
explicit
DiffApplicationStrategy(vector<E>& targetVector)
: seq_(targetVector)
, pos_(seq_.begin())
{
swap (seq_, orig_); // pos_ still refers to original input sequence, which has been moved to orig_
seq_.reserve (targetVector.size() * 120 / 100); // heuristics for storage pre-allocation
}
};
/**
* generic builder to apply a list diff to a given target sequence.
* The usage pattern is as follows
* #. construct a DiffApplicator instance, wrapping the target sequence
* #. feed the list diff (sequence of diff verbs) to the #consume function
* #. the wrapped target sequence has been altered, to conform to the given diff
* @note a suitable DiffApplicationStrategy will be picked, based on the type
* of the concrete target sequence given at construction. (Effectively
* this means you need a suitable DiffApplicationStrategy specialisation,
* e.g. for a target sequence within a vector)
*/
template<class SEQ>
class DiffApplicator
: boost::noncopyable
{
using Receiver = DiffApplicationStrategy<SEQ>;
Receiver target_;
public:
explicit
DiffApplicator(SEQ& targetSeq)
: target_(targetSeq)
{ }
template<class DIFF>
void
consume (DIFF&& diff)
{
for ( ; diff; ++diff )
diff->applyTo(target_);
}
};
namespace {
template<class CON>
using ContentSnapshot = iter_stl::IterSnapshot<typename CON::value_type>;
}
template<class CON>
inline ContentSnapshot<CON>
snapshot(CON const& con)
{
return ContentSnapshot<CON>(begin(con), end(con));
}
template<class VAL>
inline iter_stl::IterSnapshot<VAL>
snapshot(std::initializer_list<VAL> const&& ili)
{
using OnceIter = iter_stl::IterSnapshot<VAL>;
return OnceIter(begin(ili), end(ili));
}
namespace {//Test fixture....
using DataSeq = vector<string>;
#define TOK(id) id(STRINGIFY(id))
string TOK(a1), TOK(a2), TOK(a3), TOK(a4), TOK(a5);
string TOK(b1), TOK(b2), TOK(b3), TOK(b4);
using Interpreter = ListDiffInterpreter<string>;
using DiffStep = ListDiffLanguage<string>::DiffStep;
using DiffSeq = iter_stl::IterSnapshot<DiffStep>;
DiffStep_CTOR(ins);
DiffStep_CTOR(del);
DiffStep_CTOR(pick);
DiffStep_CTOR(push);
inline DiffSeq
generateTestDiff()
{
return snapshot({del(a1)
, del(a2)
, ins(b1)
, pick(a3)
, push(a5)
, pick(a5)
, ins(b2)
, ins(b3)
, pick(a4)
, ins(b4)
});
}
}//(End)Test fixture
/***********************************************************************//**
* @test Demonstration/Concept: a description language for list differences.
* The representation is given as a linearised sequence of verb tokens.
* This test demonstrates the application of such a diff representation
* to a given source list, transforming this list to hold the intended
* target list contents.
*
* @see session-structure-mapping-test.cpp
*/
class DiffListApplication_test : public Test
{
virtual void
run (Arg)
{
DataSeq src({a1,a2,a3,a4,a5});
auto diff = generateTestDiff();
CHECK (!isnil (diff));
DataSeq target = src;
DiffApplicator<DataSeq> application(target);
application.consume(diff);
CHECK (isnil (diff));
CHECK (!isnil (target));
CHECK (src != target);
CHECK (target == DataSeq({b1,a3,a5,b2,b3,a4,b4}));
}
};
/** Register this test class... */
LAUNCHER (DiffListApplication_test, "unit common");
}} // namespace lib::test
}} // namespace lib::diff
#endif /*LIB_DIFF_TREE_DIFF_APPLICATION_H*/

View file

@ -21,10 +21,17 @@
*/
/** @file tree-diff.hpp
** Fundamental definitions for a representation of changes.
/** @file list-diff.hpp
** A token language to represent structural changes in a tree like
** hierarchical data structure. In combination with the #DiffLanguage framework,
** this building block defines the set of operations to express both content
** and structural changes in a given data structure.
**
** @todo UNIMPLEMENTED as of 12/14
**
** @see diff-language.cpp
** @see diff-list-application-test.cpp
** @see list-diff.cpp
** @see VerbToken
**
*/
@ -34,385 +41,34 @@
#define LIB_DIFF_TREE_DIFF_H
#include "lib/test/run.hpp"
#include "lib/verb-token.hpp"
#include "lib/util.hpp"
#include "lib/iter-adapter-stl.hpp"
#include "lib/format-string.hpp"
#include <boost/noncopyable.hpp>
#include <functional>
#include <algorithm>
#include <string>
#include <vector>
#include <tuple>
using util::isnil;
using std::string;
using util::_Fmt;
using std::vector;
using std::move;
#include "lib/diff/diff-language.hpp"
namespace lib {
namespace test{
namespace error = lumiera::error;
LUMIERA_ERROR_DEFINE(DIFF_CONFLICT, "Collision in diff application: contents of target not as expected.");
template< class I, typename E>
struct DiffLanguage
{
using DiffVerb = VerbToken<I, void(E)>;
using VerbTok = std::tuple<DiffVerb, E>;
struct DiffStep
: VerbTok
{
DiffVerb& verb() { return std::get<0>(*this); }
E elm() { return std::get<1>(*this); }
DiffStep(DiffVerb verb, E e)
: VerbTok(verb,e)
{ }
operator string() const
{
return string(verb()) + "("+string(elm())+")";
}
void
applyTo (I& interpreter)
{
verb().applyTo (interpreter, elm());
}
};
};
template<class I, typename E>
using HandlerFun = void (I::*) (E);
template<typename SIG_HANDLER>
struct DiffStepBuilder;
/** generator to produce specific language tokens */
template<class I, typename E>
struct DiffStepBuilder<HandlerFun<I,E>>
{
using Lang = DiffLanguage<I,E>;
using Step = typename Lang::DiffStep;
using Verb = typename Lang::DiffVerb;
HandlerFun<I,E> handler;
Literal id;
Step
operator() (E elm) const
{
return { Verb(handler,id), elm };
}
};
/** set up a diff language token generator,
* based on the specific handler function given.
* This generator will produce tokens, wrapping concrete content elements
* of type \c E. In the end, the purpose is to send a sequence of such tokens
* around, to feed them to a consumer, which implements the \em Interpreter
* interface of the diff language. E.g. this consumer might apply the diff.
*/
template<class H>
inline DiffStepBuilder<H>
diffTokenBuilder (H handlerFun, Literal id)
{
return { handlerFun, id };
}
/** shortcut to define tokens of the diff language.
* Use it to define namespace level function objects, which,
* when supplied with an argument value of type \c E, will generate
* a specific language token wrapping a copy of this element.
* @note need a typedef \c Interpreter at usage site
* to refer to the actual language interpreter interface;
* the template parameters of the Language and the element
* type will be picked up from the given member function pointer.
*/
#define DiffStep_CTOR(_ID_) \
const auto _ID_ = diffTokenBuilder (&Interpreter::_ID_, STRINGIFY(_ID_));
template<typename E>
class ListDiffInterpreter
{
public:
virtual ~ListDiffInterpreter() { } ///< this is an interface
virtual void ins(E e) =0;
virtual void del(E e) =0;
virtual void pick(E e) =0;
virtual void push(E e) =0;
};
template<typename E>
using ListDiffLanguage = DiffLanguage<ListDiffInterpreter<E>, E>;
template<class CON>
class DiffApplicationStrategy;
/**
* concrete strategy to apply a list diff to a target sequence given as vector.
* The implementation swaps aside the existing content of the target sequence
* and then consumes it step by step, while building up the altered content
* within the previously emptied target vector. Whenever possible, elements
* are moved directly to the target location.
* @throws lumiera::error::State when diff application fails due to the
* target sequence being different than assumed by the given diff.
* @warning behaves only EX_SANE in case of diff application errors,
* i.e. only partially modified / rebuilt sequence might be
* in the target when diff application is aborted
*/
template<typename E, typename...ARGS>
class DiffApplicationStrategy<vector<E,ARGS...>>
: public ListDiffInterpreter<E>
{
using Vec = vector<E,ARGS...>;
using Iter = typename Vec::iterator;
Vec orig_;
Vec& seq_;
Iter pos_;
bool
end_of_target()
{
return pos_ == orig_.end();
}
void
__expect_in_target (E const& elm, Literal oper)
{
if (end_of_target())
throw error::State(_Fmt("Unable to %s element %s from target as demanded; "
"no (further) elements in target sequence") % oper % elm
, LUMIERA_ERROR_DIFF_CONFLICT);
if (*pos_ != elm)
throw error::State(_Fmt("Unable to %s element %s from target as demanded; "
"found element %s on current target position instead")
% oper % elm % *pos_
, LUMIERA_ERROR_DIFF_CONFLICT);
}
void
__expect_further_elements()
{
if (end_of_target())
throw error::State("Premature end of target sequence; unable to apply diff further."
, LUMIERA_ERROR_DIFF_CONFLICT);
}
void
__expect_found (E const& elm, Iter const& targetPos)
{
if (targetPos == orig_.end())
throw error::State(_Fmt("Premature end of sequence; unable to locate "
"element %s as reference point in target.") % elm
, LUMIERA_ERROR_DIFF_CONFLICT);
}
/* == Implementation of the diff application primitives == */
void
ins (E elm)
{
seq_.push_back(elm);
}
void
del (E elm)
{
__expect_in_target(elm, "remove");
++pos_;
}
void
pick (E elm)
{
__expect_in_target(elm, "pick");
seq_.push_back (move(*pos_));
++pos_;
}
void
push (E anchor)
{
__expect_further_elements();
E elm(move(*pos_)); // consume current source element
++pos_;
// locate the insert position behind the given reference anchor
Iter insertPos = std::find(pos_, orig_.end(), anchor);
__expect_found (anchor, insertPos);
// inserting the "pushed back" element behind the found position
// this might lead to reallocation and thus invalidate the iterators
auto currIdx = pos_ - orig_.begin();
orig_.insert (++insertPos, move(elm));
pos_ = orig_.begin() + currIdx;
}
public:
explicit
DiffApplicationStrategy(vector<E>& targetVector)
: seq_(targetVector)
, pos_(seq_.begin())
{
swap (seq_, orig_); // pos_ still refers to original input sequence, which has been moved to orig_
seq_.reserve (targetVector.size() * 120 / 100); // heuristics for storage pre-allocation
}
};
namespace diff{
/**
* generic builder to apply a list diff to a given target sequence.
* The usage pattern is as follows
* #. construct a DiffApplicator instance, wrapping the target sequence
* #. feed the list diff (sequence of diff verbs) to the #consume function
* #. the wrapped target sequence has been altered, to conform to the given diff
* @note a suitable DiffApplicationStrategy will be picked, based on the type
* of the concrete target sequence given at construction. (Effectively
* this means you need a suitable DiffApplicationStrategy specialisation,
* e.g. for a target sequence within a vector)
* Interpreter interface to define the operations ("verbs"),
* which describe differences or changes in hierarchical data structure.
* The meaning of the verbs is as follows:
* - \c TODO
*
* @todo to be defined
*/
template<class SEQ>
class DiffApplicator
: boost::noncopyable
template<typename E>
class TreeDiffInterpreter
{
using Receiver = DiffApplicationStrategy<SEQ>;
Receiver target_;
public:
explicit
DiffApplicator(SEQ& targetSeq)
: target_(targetSeq)
{ }
template<class DIFF>
void
consume (DIFF&& diff)
{
for ( ; diff; ++diff )
diff->applyTo(target_);
}
///////TODO actual operations go here
};
namespace {
template<class CON>
using ContentSnapshot = iter_stl::IterSnapshot<typename CON::value_type>;
}
template<class CON>
inline ContentSnapshot<CON>
snapshot(CON const& con)
{
return ContentSnapshot<CON>(begin(con), end(con));
}
template<class VAL>
inline iter_stl::IterSnapshot<VAL>
snapshot(std::initializer_list<VAL> const&& ili)
{
using OnceIter = iter_stl::IterSnapshot<VAL>;
return OnceIter(begin(ili), end(ili));
}
namespace {//Test fixture....
using DataSeq = vector<string>;
#define TOK(id) id(STRINGIFY(id))
string TOK(a1), TOK(a2), TOK(a3), TOK(a4), TOK(a5);
string TOK(b1), TOK(b2), TOK(b3), TOK(b4);
using Interpreter = ListDiffInterpreter<string>;
using DiffStep = ListDiffLanguage<string>::DiffStep;
using DiffSeq = iter_stl::IterSnapshot<DiffStep>;
DiffStep_CTOR(ins);
DiffStep_CTOR(del);
DiffStep_CTOR(pick);
DiffStep_CTOR(push);
inline DiffSeq
generateTestDiff()
{
return snapshot({del(a1)
, del(a2)
, ins(b1)
, pick(a3)
, push(a5)
, pick(a5)
, ins(b2)
, ins(b3)
, pick(a4)
, ins(b4)
});
}
}//(End)Test fixture
template<typename E>
using TreeDiffLanguage = DiffLanguage<TreeDiffInterpreter<E>, E>;
/***********************************************************************//**
* @test Demonstration/Concept: a description language for list differences.
* The representation is given as a linearised sequence of verb tokens.
* This test demonstrates the application of such a diff representation
* to a given source list, transforming this list to hold the intended
* target list contents.
*
* @see session-structure-mapping-test.cpp
*/
class DiffListApplication_test : public Test
{
virtual void
run (Arg)
{
DataSeq src({a1,a2,a3,a4,a5});
auto diff = generateTestDiff();
CHECK (!isnil (diff));
DataSeq target = src;
DiffApplicator<DataSeq> application(target);
application.consume(diff);
CHECK (isnil (diff));
CHECK (!isnil (target));
CHECK (src != target);
CHECK (target == DataSeq({b1,a3,a5,b2,b3,a4,b4}));
}
};
/** Register this test class... */
LAUNCHER (DiffListApplication_test, "unit common");
}} // namespace lib::test
}} // namespace lib::diff
#endif /*LIB_DIFF_TREE_DIFF_H*/

View file

@ -22,26 +22,21 @@
#include "lib/test/run.hpp"
#include "lib/verb-token.hpp"
#include "lib/util.hpp"
#include "lib/diff/list-diff-application.hpp"
#include "lib/iter-adapter-stl.hpp"
#include "lib/format-string.hpp"
#include "lib/util.hpp"
#include <boost/noncopyable.hpp>
#include <functional>
#include <algorithm>
#include <string>
#include <vector>
#include <tuple>
using lib::iter_stl::snapshot;
using util::isnil;
using std::string;
using util::_Fmt;
using std::vector;
using std::move;
namespace lib {
namespace diff{
namespace test{
namespace {//Test fixture....
@ -125,4 +120,4 @@ namespace test{
}} // namespace lib::test
}}} // namespace lib::diff::test