Yet another chainsaw massacre. One of the most obnoxious annoyances with C++ metaprogramming is the need to insert `typename` and `template` qualifiers into most definitions, to help the compiler to cope with the syntax, which is not context-free. The recent standards adds several clarifications, so that most of these qualifiers are redundant now, at least at places where it is unambiguously clear that only a type can be given. GCC already supports most of these relaxing rules (Clang unfortunately lags way behind with support of newer language features...)
183 lines
5.7 KiB
C++
183 lines
5.7 KiB
C++
/*
|
||
LIST-DIFF-APPLICATION.hpp - consume and apply a list diff
|
||
|
||
Copyright (C)
|
||
2014, Hermann Vosseler <Ichthyostega@web.de>
|
||
|
||
**Lumiera** 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. See the file COPYING for further details.
|
||
|
||
*/
|
||
|
||
|
||
/** @file list-diff-application.hpp
|
||
** 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 lib::diff::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 ListDiffLanguage
|
||
**
|
||
*/
|
||
|
||
|
||
#ifndef LIB_DIFF_LIST_DIFF_APPLICATION_H
|
||
#define LIB_DIFF_LIST_DIFF_APPLICATION_H
|
||
|
||
|
||
#include "lib/diff/list-diff.hpp"
|
||
#include "lib/format-string.hpp"
|
||
|
||
#include <algorithm>
|
||
#include <vector>
|
||
#include <tuple>
|
||
|
||
|
||
namespace lib {
|
||
namespace diff{
|
||
|
||
using util::_Fmt;
|
||
using std::vector;
|
||
using std::move;
|
||
|
||
/**
|
||
* 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
|
||
* @see #ListDiffInterpreter explanation of the verbs
|
||
*/
|
||
template<typename E, typename...ARGS>
|
||
class DiffApplicationStrategy<vector<E,ARGS...>>
|
||
: public ListDiffInterpreter<E>
|
||
{
|
||
using Vec = vector<E,ARGS...>;
|
||
using Iter = 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
|
||
, LERR_(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_
|
||
, LERR_(DIFF_CONFLICT));
|
||
}
|
||
|
||
void
|
||
__expect_further_elements (E const& elm)
|
||
{
|
||
if (end_of_target())
|
||
throw error::State(_Fmt("Premature end of target sequence, still expecting element %s; "
|
||
"unable to apply diff further.") % elm
|
||
, LERR_(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 in the remainder of the target.") % elm
|
||
, LERR_(DIFF_CONFLICT));
|
||
}
|
||
|
||
|
||
/* == Implementation of the diff application primitives == */
|
||
|
||
void
|
||
ins (E const& elm) override
|
||
{
|
||
seq_.push_back(elm);
|
||
}
|
||
|
||
void
|
||
del (E const& elm) override
|
||
{
|
||
__expect_in_target(elm, "remove");
|
||
++pos_;
|
||
}
|
||
|
||
void
|
||
pick (E const& elm) override
|
||
{
|
||
__expect_in_target(elm, "pick");
|
||
seq_.push_back (move(*pos_));
|
||
++pos_;
|
||
}
|
||
|
||
void
|
||
skip (E const& elm) override
|
||
{
|
||
__expect_further_elements (elm);
|
||
++pos_;
|
||
} // assume the actual content has been moved away by a previous find()
|
||
|
||
void
|
||
find (E const& elm) override
|
||
{
|
||
__expect_further_elements (elm);
|
||
Iter found = std::find(pos_, orig_.end(), elm);
|
||
__expect_found (elm, found);
|
||
seq_.push_back (move(*found));
|
||
} // consume and leave waste, expected to be cleaned-up by skip() later
|
||
|
||
|
||
public:
|
||
explicit
|
||
DiffApplicationStrategy(vector<E>& targetVector)
|
||
: seq_(targetVector)
|
||
{ }
|
||
|
||
void
|
||
initDiffApplication()
|
||
{
|
||
swap (seq_, orig_);
|
||
seq_.reserve (orig_.size() * 120 / 100); // heuristics for storage pre-allocation
|
||
pos_ = orig_.begin();
|
||
}
|
||
|
||
void
|
||
completeDiffApplication()
|
||
{
|
||
if (not end_of_target())
|
||
throw error::State(_Fmt("Not all source data consumed after diff application. "
|
||
"Element %s waiting to be consumed") % *pos_
|
||
, LERR_(DIFF_STRUCTURE));
|
||
// discard storage
|
||
orig_.clear();
|
||
}
|
||
};
|
||
|
||
|
||
|
||
|
||
|
||
}} // namespace lib::diff
|
||
#endif /*LIB_DIFF_LIST_DIFF_APPLICATION_H*/
|