C++11: improve moving and forwarding of iterators

this becomes more relevant now, since the actual MutationMessage iterators
are implemented in terms of a shared_ptr to IterSource. Thus, when building
processing pipelines, we most definitively want to move that smart-ptr into
the destination, since this avoids touching the shared count and thus avoids
generating unnecessary memory barriers.
This commit is contained in:
Fischlurch 2017-08-11 23:52:13 +02:00
parent 4a2384e242
commit d4ac2d78e2
7 changed files with 184 additions and 78 deletions

View file

@ -12,7 +12,7 @@ language and compiler support wasn't ready for what we consider _state of the cr
amended deficiencies by rolling our own helper facilities, with a little help from Boost.
Thus there was no urge for us to adopt the new language standard; we could simply wait for
the compiler support to mature. In spring 2014, finally, we were able to switch our codebase
to C++11 with minimal effort.footnote[since 8/2015 -- after the switch to Debian/Jessie
to C++11 with minimal effort.footnote:[since 8/2015 -- after the switch to Debian/Jessie
as a »reference platform«, we even compile with `-std=gnu++14`]
Following this switch, we're now able to reap the benefits of
this approach; we may now gradually replace our sometimes clunky helpers and workarounds
@ -112,7 +112,9 @@ were thinking since ages; it is more like an official affirmation of that style
********************************************************************************************
The core idea is that at times you need to 'move' a value due to a change of ownership. Now,
the explicit support for 'move semantics' allows to decouple this 'conceptual move' from actually
moving memory contents on the raw implementation level. The whole idea behind C++ seems to be
moving memory contents on the raw implementation level. If we use a _rvalue reference on a signature,_
we express that an entiy is or can be moved on a conceptual level; typically the actual implementation
of this moving is _delegated_ and done by ``someone else''. The whole idea behind C++ seems to be
allowing people to think on a conceptual level, while 'retaining' awareness of the gory details
below the hood. Such is achieved by 'removing' the need to worry about details, confident that
there is a way to deal with those concerns in an orthogonal fashion.
@ -137,6 +139,40 @@ CAUTION: as soon as there is an explicitly defined copy operation, or even just
compromise decision of the C++ committee -- instead of either breaking no code at all or
boldly breaking code, they settled upon ``somewhat'' breaking existing code...
Perfect forwarding
^^^^^^^^^^^^^^^^^^
The ``perfect forwarding'' technique is how we actually pass on and delegate move semantics.
In conjunction with variadic templates, this technique promises to obsolete a lot of template trickery
when it comes to implementing custom containers, allocators or similar kinds of managing wrappers.
.a typical example
[source,c]
--------------------------------------------------------------------------
template<class TY, typename...ARGS>
TY&
create (ARGS&& ...args)
{
return *new(&buf_) TY {std::forward<ARGS> (args)...};
}
--------------------------------------------------------------------------
The result is a `create()` function with the ability to _forward_ an arbitrary number of arguments
of arbitrary type to the constructor of type `TY`. Note the following details
- the template parameter `ARGS&&` leads to deducing the actual parameters _sans_ rvalue reference
- we pass each argument through `std::forward<ARGS>`. This is necessary to overcome the basic limitation
that a rvalue reference can only bind to _something unnamed_. This is a safety feature; moving destroys
the contents at the source location. Thus if we really want to move something known by name (like e.g.
the function arguments in this example), we need to indicate so by an explicit call.
- `std::forward` needs an explicit type parameter to be able to deduce the right target type in any case
- note how we're allowed to ``wrap'' a function call around the unpacking of the _argument pack_ (`args`):
the three dots `...` are _outside_ the parenthesis, and thus the `std::forward` is applied to each of
the arguments individually
NOTE: forwarding calls can be chained, but at some point you get to acutally _consuming_ the value passed through.
To support the maximum flexibility at this point, you typically need to write two flavours of the receiving
function or constructor: one version taking a rvalue reference, and one version taking `const&`. Moving is
_destructive_, while the `const&` variant deals with all those cases where we copy without affecting the
source object.
@ -159,13 +195,11 @@ September 2014::
requirement level_ soon.
August 2015::
our »reference system« (platform) is Debian/Jessie from now on.
We have switched to **C++14** and use (even require) GCC-4.9 or CLang 3.5 -- we can expect solid support
for all C++11 features and most C++14 features.
We have switched to **C\+\+14** and use (even require) GCC-4.9 or CLang 3.5 -- we can expect solid support
for all C\+\+11 features and most C++14 features.
Perfect forwarding
~~~~~~~~~~~~~~~~~~
The ``perfect forwarding'' technique in conjunction with variadic templates promises to obsolete a lot of
template trickery when it comes to implementing custom containers, allocators or similar kinds of managing wrappers.
Unfortunately, we ran into nasty problems with both GCC-4.7 and CLang 3.0 here, when chaining several forwarding calls.
- the new _reference collapsing rules_ seem to be unreliably still. Note that even the standard library uses an

View file

@ -97,6 +97,7 @@ namespace diff{
DiffMessage() = default;
// default copy operations acceptable
/**
* DiffMessage builder:
@ -132,8 +133,8 @@ namespace diff{
* @note source iterator is copied into a heap allocated IterSource
*/
template<class IT>
DiffMessage(IT ii, enable_if< can_IterForEach<IT>, void*> =nullptr)
: _FrontEnd{iter_source::wrapIter(ii)}
DiffMessage(IT&& ii, enable_if< can_IterForEach<IT>, void*> =nullptr)
: _FrontEnd{iter_source::wrapIter (std::forward<IT>(ii))}
{ }
/**
@ -142,8 +143,8 @@ namespace diff{
* @warning like with any classical iterators, the container must stay alive and accessible
*/
template<class CON>
DiffMessage(CON& container, enable_if< __and_< can_STL_ForEach<CON>
,__not_<can_IterForEach<CON>>>, void*> =nullptr)
DiffMessage(CON& container, enable_if< __and_< can_STL_ForEach<CON>
,__not_<can_IterForEach<CON>>>, void*> =nullptr)
: _FrontEnd{iter_source::eachEntry(container)}
{ }
};

View file

@ -381,9 +381,9 @@ namespace diff{
}
friend ChildDataIter
childData (Rec::scopeIter const& scopeIter)
childData (Rec::scopeIter&& scopeIter)
{
return ChildDataIter{ scopeIter
return ChildDataIter{ std::forward<Rec::scopeIter>(scopeIter)
, [](GenNode const& child) ->DataCap const&
{
return child.data;

View file

@ -55,6 +55,7 @@ namespace util {
using lib::meta::can_IterForEach;
using std::string;
using std::forward;
using std::move;
@ -127,12 +128,12 @@ namespace util {
* @see FormatHelper_test::checkStringify()
*/
template<class IT>
inline lib::TransformIter<IT, string>
stringify (IT const& src)
inline auto
stringify (IT&& src)
{
using Val = typename IT::value_type;
using Val = typename std::remove_reference<IT>::type::value_type;
return lib::transformIterator(src, util::toString<Val>);
return lib::transformIterator(forward<IT>(src), util::toString<Val>);
}

View file

@ -52,8 +52,9 @@
#include "lib/iter-adapter.hpp"
#include "lib/itertools.hpp"
#include <boost/type_traits/remove_const.hpp>
#include <boost/noncopyable.hpp>
#include <type_traits>
#include <utility>
#include <string>
#include <memory>
@ -64,6 +65,7 @@ namespace lib {
using std::string;
using std::shared_ptr;
using std::forward;
@ -255,6 +257,10 @@ namespace lib {
WrappedLumieraIter (IT const& orig)
: src_(orig)
{ }
WrappedLumieraIter (IT&& orig)
: src_(forward<IT>(orig))
{ }
};
@ -268,55 +274,58 @@ namespace lib {
template<class CON>
struct _SeqT
{
typedef typename CON::iterator::value_type Val;
typedef typename IterSource<Val>::iterator Iter;
using Val = typename CON::iterator::value_type;
using Iter = typename IterSource<Val>::iterator;
};
template<class IT>
struct _RangeT
{
typedef typename IT::value_type Val;
typedef typename IterSource<Val>::iterator Iter;
using Val = typename IT::value_type;
using Iter = typename IterSource<Val>::iterator;
};
template<class MAP>
struct _MapT
{
typedef typename MAP::key_type Key;
typedef typename MAP::value_type::second_type Val;
typedef typename IterSource<Key>::iterator KeyIter;
typedef typename IterSource<Val>::iterator ValIter;
using Key = typename MAP::key_type;
using Val = typename MAP::value_type::second_type;
using KeyIter = typename IterSource<Key>::iterator;
using ValIter = typename IterSource<Val>::iterator;
};
template<class IT>
struct _IterT
{
typedef typename IT::value_type Val;
typedef typename IterSource<Val>::iterator Iter;
using Src = typename std::remove_reference<IT>::type;
using Val = typename Src::value_type;
using Iter = typename IterSource<Val>::iterator;
};
template<class IT, class FUN>
struct _TransformIterT
{
typedef typename lib::meta::_Fun<FUN>::Ret ResVal;
typedef TransformIter<IT,ResVal> TransIter;
typedef typename IterSource<ResVal>::iterator Iter;
using Src = typename std::remove_reference<IT>::type;
using ResVal = typename lib::meta::_Fun<FUN>::Ret;
using TransIter = TransformIter<Src, ResVal>;
using Iter = typename IterSource<ResVal>::iterator;
};
template<class IT>
struct _PairIterT
{
typedef typename IT::value_type PairType;
typedef typename PairType::second_type ValType;
typedef typename PairType::first_type ConstKeyType;
using Src = typename std::remove_reference<IT>::type;
using PairType = typename Src::value_type;
using ValType = typename PairType::second_type;
using ConstKeyType = typename PairType::first_type;
// since we're returning the keys always by value,
// we can strip the const added by the STL map types
typedef typename boost::remove_const<ConstKeyType>::type KeyType;
using KeyType = typename std::remove_const<ConstKeyType>::type;
typedef TransformIter<IT,KeyType> KeyIter;
typedef TransformIter<IT,ValType> ValIter;
typedef TransformIter<Src, KeyType> KeyIter;
typedef TransformIter<Src, ValType> ValIter;
static KeyType takeFirst (PairType const& pair) { return pair.first; }
static ValType takeSecond(PairType const& pair) { return pair.second;}
@ -325,16 +334,16 @@ namespace lib {
template<class IT>
typename _PairIterT<IT>::KeyIter
takePairFirst (IT const& source)
takePairFirst (IT&& source)
{
return transformIterator(source, _PairIterT<IT>::takeFirst );
return transformIterator(forward<IT>(source), _PairIterT<IT>::takeFirst );
}
template<class IT>
typename _PairIterT<IT>::ValIter
takePairSecond (IT const& source)
takePairSecond (IT&& source)
{
return transformIterator(source, _PairIterT<IT>::takeSecond );
return transformIterator(forward<IT>(source), _PairIterT<IT>::takeSecond );
}
} //(END) type helpers...
@ -346,11 +355,12 @@ namespace lib {
*/
template<class IT>
typename _IterT<IT>::Iter
wrapIter (IT const& source)
wrapIter (IT&& source)
{
typedef typename _IterT<IT>::Val ValType;
using Src = typename _IterT<IT>::Src;
using Val = typename _IterT<IT>::Val;
return IterSource<ValType>::build (new WrappedLumieraIter<IT> (source));
return IterSource<Val>::build (new WrappedLumieraIter<Src> (forward<IT>(source)));
}
@ -365,14 +375,14 @@ namespace lib {
*/
template<class IT, class FUN>
typename _TransformIterT<IT,FUN>::Iter
transform (IT const& source, FUN processingFunc)
transform (IT&& source, FUN processingFunc)
{
typedef typename _TransformIterT<IT,FUN>::ResVal ValType;
typedef typename _TransformIterT<IT,FUN>::TransIter TransIT;
return IterSource<ValType>::build (
new WrappedLumieraIter<TransIT> (
transformIterator (source, processingFunc)));
transformIterator (forward<IT>(source), processingFunc)));
}
@ -383,7 +393,7 @@ namespace lib {
typename _MapT<MAP>::KeyIter
eachMapKey (MAP& map)
{
typedef RangeIter<typename MAP::iterator> Range;
using Range = RangeIter<typename MAP::iterator>;
Range contents (map.begin(), map.end());
return wrapIter (takePairFirst (contents));
@ -397,7 +407,7 @@ namespace lib {
typename _MapT<MAP>::ValIter
eachMapVal (MAP& map)
{
typedef RangeIter<typename MAP::iterator> Range;
using Range = RangeIter<typename MAP::iterator>;
Range contents (map.begin(), map.end());
return wrapIter (takePairSecond(contents));
@ -413,7 +423,7 @@ namespace lib {
typename _MapT<MAP>::KeyIter
eachDistinctKey (MAP& map)
{
typedef RangeIter<typename MAP::iterator> Range;
using Range = RangeIter<typename MAP::iterator>;
Range contents (map.begin(), map.end());
return wrapIter (filterRepetitions (takePairFirst(contents)));
@ -428,8 +438,8 @@ namespace lib {
typename _MapT<MAP>::ValIter
eachValForKey (MAP& map, typename _MapT<MAP>::Key key)
{
typedef typename MAP::iterator Pos;
typedef RangeIter<Pos> Range;
using Pos = typename MAP::iterator;
using Range = RangeIter<Pos>;
std::pair<Pos,Pos> valuesForKey = map.equal_range(key);
Range contents (valuesForKey.first, valuesForKey.second);
@ -447,8 +457,8 @@ namespace lib {
typename _SeqT<CON>::Iter
eachEntry (CON& container)
{
typedef typename _SeqT<CON>::Val ValType;
typedef RangeIter<typename CON::iterator> Range;
using ValType = typename _SeqT<CON>::Val;
using Range = RangeIter<typename CON::iterator>;
Range contents (container.begin(), container.end());
return IterSource<ValType>::build (new WrappedLumieraIter<Range>(contents));
@ -462,8 +472,8 @@ namespace lib {
typename _RangeT<IT>::Iter
eachEntry (IT const& begin, IT const& end)
{
typedef typename _RangeT<IT>::Val ValType;
typedef RangeIter<IT> Range;
using ValType = typename _RangeT<IT>::Val;
using Range = RangeIter<IT>;
Range contents (begin, end);
return IterSource<ValType>::build (new WrappedLumieraIter<Range>(contents));

View file

@ -74,11 +74,13 @@
#include "lib/util.hpp"
#include <functional>
#include <utility>
namespace lib {
using std::forward;
using std::function;
using util::unConst;
@ -102,8 +104,11 @@ namespace lib {
{
IT source_;
IdentityCore (IT&& orig)
: source_{forward<IT>(orig)}
{ }
IdentityCore (IT const& orig)
: source_(orig)
: source_{orig}
{ }
IT&
@ -184,8 +189,8 @@ namespace lib {
typedef typename CORE::value_type value_type;
IterTool (CORE const& setup)
: core_(setup)
IterTool (CORE&& setup)
: core_{std::move(setup)}
{
hasData();
}
@ -268,8 +273,8 @@ namespace lib {
struct FilterCore
: IdentityCore<IT>
{
typedef IdentityCore<IT> Raw;
typedef typename IT::reference Val;
using Raw = IdentityCore<IT>;
using Val = typename IT::reference;
function<bool(Val)> predicate_;
@ -302,12 +307,20 @@ namespace lib {
template<typename PRED>
FilterCore (IT const& source, PRED prediDef)
: Raw(source)
FilterCore (IT&& source, PRED prediDef)
: Raw{forward<IT>(source)}
, predicate_(prediDef) // induces a signature check
, cached_(false) // not yet cached
, isOK_() // some value
{ }
template<typename PRED>
FilterCore (IT const& source, PRED prediDef)
: Raw{source}
, predicate_(prediDef)
, cached_(false)
, isOK_()
{ }
};
@ -326,12 +339,17 @@ namespace lib {
FilterIter ()
: _Impl(FilterCore<IT>(IT(), acceptAll))
: _Impl{FilterCore<IT>(IT(), acceptAll)}
{ }
template<typename PRED>
FilterIter (IT const& src, PRED filterPredicate)
: _Impl(_Filter(src,filterPredicate))
: _Impl{_Filter(src, filterPredicate)}
{ }
template<typename PRED>
FilterIter (IT&& src, PRED filterPredicate)
: _Impl{_Filter(forward<IT>(src), filterPredicate)}
{ }
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (FilterIter)
@ -344,10 +362,18 @@ namespace lib {
* @return Iterator filtering contents of the source
*/
template<class IT, typename PRED>
inline FilterIter<IT>
inline auto
filterIterator (IT const& src, PRED filterPredicate)
{
return FilterIter<IT>(src,filterPredicate);
return FilterIter<IT>{src, filterPredicate};
}
template<class IT, typename PRED>
inline auto
filterIterator (IT&& src, PRED filterPredicate)
{
using SrcIT = typename std::remove_reference<IT>::type;
return FilterIter<SrcIT>{forward<SrcIT>(src), filterPredicate};
}
@ -378,8 +404,8 @@ namespace lib {
class ExtensibleFilterIter
: public FilterIter<IT>
{
typedef FilterCore<IT> _Filter;
typedef typename _Filter::Val Val;
using _Filter = FilterCore<IT>;
using Val = typename _Filter::Val;
void
reEvaluate()
@ -391,13 +417,17 @@ namespace lib {
public:
ExtensibleFilterIter() { }
template<typename PRED>
ExtensibleFilterIter (IT&& src, PRED initialFilterPredicate)
: FilterIter<IT>{forward<IT>(src), initialFilterPredicate}
{ }
template<typename PRED>
ExtensibleFilterIter (IT const& src, PRED initialFilterPredicate)
: FilterIter<IT>(src, initialFilterPredicate)
: FilterIter<IT>{src, initialFilterPredicate}
{ }
ExtensibleFilterIter (IT const& src)
: ExtensibleFilterIter(src, FilterIter<IT>::acceptAll)
ExtensibleFilterIter (IT&& src)
: ExtensibleFilterIter{forward<IT>(src), FilterIter<IT>::acceptAll}
{ }
// standard copy operations acceptable
@ -570,6 +600,14 @@ namespace lib {
, treated_()
{ }
template<typename FUN>
TransformingCore (IT&& orig, FUN processor)
: trafo_(processor) // induces a signature check
, source_(forward<IT> (orig))
{
processItem();
}
template<typename FUN>
TransformingCore (IT const& orig, FUN processor)
: trafo_(processor) // induces a signature check
@ -612,8 +650,8 @@ namespace lib {
class TransformIter
: public IterTool<TransformingCore<IT,VAL>>
{
typedef TransformingCore<IT,VAL> _Trafo;
typedef IterTool<_Trafo> _IteratorImpl;
using _Trafo = TransformingCore<IT,VAL>;
using _IteratorImpl = IterTool<_Trafo> ;
public:
TransformIter ()
@ -621,8 +659,12 @@ namespace lib {
{ }
template<typename FUN>
TransformIter (IT&& src, FUN trafoFunc)
: _IteratorImpl{_Trafo(forward<IT>(src), trafoFunc)}
{ }
template<typename FUN>
TransformIter (IT const& src, FUN trafoFunc)
: _IteratorImpl(_Trafo(src,trafoFunc))
: _IteratorImpl{_Trafo(src, trafoFunc)}
{ }
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (TransformIter)
@ -642,7 +684,16 @@ namespace lib {
transformIterator (IT const& src, FUN processingFunc)
{
using OutVal = typename lib::meta::_Fun<FUN>::Ret;
return TransformIter<IT,OutVal>(src,processingFunc);
return TransformIter<IT,OutVal>{src,processingFunc};
}
template<class IT, typename FUN>
inline auto
transformIterator (IT&& src, FUN processingFunc)
{
using SrcIT = typename std::remove_reference<IT>::type;
using OutVal = typename lib::meta::_Fun<FUN>::Ret;
return TransformIter<SrcIT,OutVal>{forward<SrcIT>(src), processingFunc};
}
@ -687,11 +738,20 @@ namespace lib {
/** filters away repeated values
* emitted by source iterator */
template<class IT>
inline FilterIter<IT>
inline auto
filterRepetitions (IT const& source)
{
typedef typename IT::value_type Val;
return filterIterator(source, SkipRepetition<Val>() );
using Val = typename IT::value_type;
return filterIterator (source, SkipRepetition<Val>());
}
template<class IT>
inline auto
filterRepetitions (IT&& source)
{
using SrcIT = typename std::remove_reference<IT>::type;
using Val = typename SrcIT::value_type;
return filterIterator (forward<SrcIT>(source), SkipRepetition<Val>() );
}

View file

@ -309,7 +309,7 @@ namespace session {
/* == access for self-test == */
typedef lib::IterSource<PID>::iterator IDIter;
using IDIter = lib::IterSource<PID>::iterator;
PlacementMO* _root_4check () { return root_.get(); }
PlacementMO* _element_4check (ID id){ return base_entry(id).element.get();}