/* reducer_list.h -*- C++ -*- * * Copyright (C) 2009-2016, Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * ********************************************************************* * * PLEASE NOTE: This file is a downstream copy of a file mainitained in * a repository at cilkplus.org. Changes made to this file that are not * submitted through the contribution process detailed at * http://www.cilkplus.org/submit-cilk-contribution will be lost the next * time that a new version is released. Changes only submitted to the * GNU compiler collection or posted to the git repository at * https://bitbucket.org/intelcilkruntime/intel-cilk-runtime.git are * not tracked. * * We welcome your contributions to this open source project. Thank you * for your assistance in helping us improve Cilk Plus. */ /** @file reducer_list.h * * @brief Defines classes for parallel list creation by appending or * prepending reducers. * * @ingroup ReducersList * * @see ReducersList */ #ifndef REDUCER_LIST_H_INCLUDED #define REDUCER_LIST_H_INCLUDED #include #include /** @defgroup ReducersList List Reducers * * List-append and list-prepend reducers create standard lists by * concatenating a set of lists or values in parallel. * * @ingroup Reducers * * You should be familiar with @ref pagereducers "Intel(R) Cilk(TM) Plus reducers" * (from file `reducers.md`) and particularly with @ref reducers_using, before * trying to use the information in this file. * * @section redlist_usage Usage Example * * // Create a list containing the labels of the nodes of a tree in * // "inorder" (left subtree, root, right subtree). * * struct Tree { Tree* left; Tree* right; string label; ... }; * * list x; * cilk::reducer< cilk::op_list_append > xr(cilk::move_in(x)); * collect_labels(tree, xr); * xr.move_out(x); * * void collect_labels(Tree* node, * cilk::reducer< cilk::op_list_append >& xr) * { * if (node) { * cilk_spawn collect_labels(node->left, xr); * xr->push_back(node->label); * collect_labels(node->right, xr); * cilk_sync; * } * } * * @section redlist_monoid The Monoid * * @subsection redlist_monoid_values Value Set * * The __value set__ of a list reducer is the set of values of the class * `std::list`, which we refer to as the reducer's _list * type_. * * @subsection redlist_monoid_operator Operator * * The operator of a list-append reducer is defined as * * x CAT y == (every element of x, followed by every element of y) * * The operator of a list-prepend reducer is defined as * * x RCAT y == (every element of y, followed by every element of x) * * @subsection redlist_monoid_identity Identity * * The identity value of a list reducer is the empty list, which is the value * of the expression `std::list([allocator])`. * * @section redlist_operations Operations * * In the operation descriptions below, the type name `List` refers to the * reducer's string type, `std::list`. * * @subsection redlist_constructors Constructors * * Any argument list which is valid for a `std::list` constructor is valid for * a list reducer constructor. The usual move-in constructor is also provided: * * reducer(move_in(List& variable)) * * A list reducer with no constructor arguments, or with only an allocator * argument, will initially contain the identity value, an empty list. * * @subsection redlist_get_set Set and Get * * r.set_value(const List& value) * const List& = r.get_value() const * r.move_in(List& variable) * r.move_out(List& variable) * * @subsection redlist_view_ops View Operations * * The view of a list-append reducer provides the following member functions: * * void push_back(const Type& element) * void insert_back(List::size_type n, const Type& element) * template void insert_back(Iter first, Iter last) * void splice_back(List& x) * void splice_back(List& x, List::iterator i) * void splice_back(List& x, List::iterator first, List::iterator last) * * The view of a list-prepend reducer provides the following member functions: * * void push_front(const Type& element) * void insert_front(List::size_type n, const Type& element) * template void insert_front(Iter first, Iter last) * void splice_front(List& x) * void splice_front(List& x, List::iterator i) * void splice_front(List& x, List::iterator first, List::iterator last) * * The `push_back` and `push_front` functions are the same as the * corresponding `std::list` functions. The `insert_back`, `splice_back`, * `insert_front`, and `splice_front` functions are the same as the * `std::list` `insert` and `splice` functions, with the first parameter * fixed to the end or beginning of the list, respectively. * * @section redlist_performance Performance Considerations * * An efficient reducer requires that combining the values of two views (using * the view `reduce()` function) be a constant-time operations. Two lists can * be merged in constant time using the `splice()` function if they have the * same allocator. Therefore, the lists for new views are created (by the view * identity constructor) using the same allocator as the list that was created * when the reducer was constructed. * * The performance of adding elements to a list reducer depends on the view * operations that are used: * * * The `push` functions add a single element to the list, and therefore * take constant time. * * An `insert` function that inserts _N_ elements adds each of them * individually, and therefore takes _O(N)_ time. * * A `splice` function that inserts _N_ elements just adjusts a couple of * pointers, and therefore takes constant time, _if the splice is from a * list with the same allocator as the reducer_. Otherwise, it is * equivalent to an `insert`, and takes _O(N)_ time. * * This means that for best performance, if you will be adding elements to a * list reducer in batches, you should `splice` them from a list having the * same allocator as the reducer. * * The reducer `move_in` and `move_out` functions do a constant-time `swap` if * the variable has the same allocator as the reducer, and a linear-time copy * otherwise. * * Note that the allocator of a list reducer is determined when the reducer is * constructed. The following two examples may have very different behavior: * * list a_list; * * reducer< list_append reducer1(move_in(a_list)); * ... parallel computation ... * reducer1.move_out(a_list); * * reducer< list_append reducer2; * reducer2.move_in(a_list); * ... parallel computation ... * reducer2.move_out(a_list); * * * `reducer1` will be constructed with the same allocator as `a_list`, * because the list was specified in the constructor. The `move_in` * and `move_out` can therefore be done with a `swap` in constant time. * * `reducer2` will be constructed with a _default_ allocator, * "`Allocator()`", which may or may not be the same as the allocator of * `a_list`. Therefore, the `move_in` and `move_out` may have to be done * with a copy in _O(N)_ time. * * (All instances of an allocator type with no internal state (like * `std::allocator`) are "the same". You only need to worry about the "same * allocator" issue when you create list reducers with custom allocator types.) * * @section redlist_types Type and Operator Requirements * * `std::list` must be a valid type. */ namespace cilk { namespace internal { /** @ingroup ReducersList */ //@{ /** Base class for list-append and prepend view classes. * * @note This class provides the definitions that are required for a class * that will be used as the parameter of a @ref list_monoid_base * specialization. * * @tparam Type The list element type (not the list type). * @tparam Allocator The list's allocator class. * * @see ReducersList * @see list_monoid_base */ template class list_view_base { protected: /// The type of the contained list. typedef std::list list_type; /// The list accumulator variable. list_type m_value; public: /** @name Monoid support. */ //@{ /// Required by @ref monoid_with_view typedef list_type value_type; /// Required by @ref list_monoid_base Allocator get_allocator() const { return m_value.get_allocator(); } //@} /** @name Constructors. */ //@{ /// Standard list constructor. explicit list_view_base(const Allocator& a = Allocator()) : m_value(a) {} explicit list_view_base( typename list_type::size_type n, const Type& value = Type(), const Allocator& a = Allocator() ) : m_value(n, value, a) {} template list_view_base(Iter first, Iter last, const Allocator& a = Allocator()) : m_value(first, last, a) {} list_view_base(const list_type& list) : m_value(list) {} /// Move-in constructor. explicit list_view_base(move_in_wrapper w) : m_value(w.value().get_allocator()) { m_value.swap(w.value()); } //@} /** @name Reducer support. */ //@{ /// Required by reducer::move_in() void view_move_in(value_type& v) { if (m_value.get_allocator() == v.get_allocator()) // Equal allocators. Do a (fast) swap. m_value.swap(v); else // Unequal allocators. Do a (slow) copy. m_value = v; v.clear(); } /// Required by reducer::move_out() void view_move_out(value_type& v) { if (m_value.get_allocator() == v.get_allocator()) // Equal allocators. Do a (fast) swap. m_value.swap(v); else // Unequal allocators. Do a (slow) copy. v = m_value; m_value.clear(); } /// Required by reducer::set_value() void view_set_value(const value_type& v) { m_value = v; } /// Required by reducer::get_value() value_type const& view_get_value() const { return m_value; } /// Type returned by view_get_value. typedef value_type const& return_type_for_get_value; // Required by legacy wrapper get_reference() value_type & view_get_reference() { return m_value; } value_type const& view_get_reference() const { return m_value; } //@} }; /** Base class for list-append and prepend monoid classes. * * The key to efficient reducers is that the `identity` operation, which * creates a new per-strand view, and the `reduce` operation, which combines * two per-strand views, must be constant-time operations. Two lists can be * concatenated in constant time only if they have the same allocator. * Therefore, all the per-strand list accumulator variables must be created * with the same allocator as the leftmost view list. * * This means that a list reduction monoid must have a copy of the allocator * of the leftmost view's list, so that it can use it in the `identity` * operation. This, in turn, requires that list reduction monoids have a * specialized `construct()` function, which constructs the leftmost view * before the monoid, and then passes the leftmost view's allocator to the * monoid constructor. * * @tparam View The list-append or prepend view class. * @tparam Align If `false` (the default), reducers instantiated on this * monoid will be naturally aligned (the Intel Cilk Plus library 1.0 * behavior). If `true`, reducers instantiated on this monoid * will be cache-aligned for binary compatibility with * reducers in Intel Cilk Plus library version 0.9. * * @see ReducersList * @see list_view_base */ template class list_monoid_base : public monoid_with_view { typedef typename View::value_type list_type; typedef typename list_type::allocator_type allocator_type; typedef provisional_guard view_guard; allocator_type m_allocator; public: /** Constructor. * * There is no default constructor for list monoids, because the allocator * must always be specified. * * @param allocator The list allocator to be used when * identity-constructing new views. */ list_monoid_base(const allocator_type& allocator = allocator_type()) : m_allocator(allocator) {} /** Creates an identity view. * * List view identity constructors take the list allocator as an argument. * * @param v The address of the uninitialized memory in which the view * will be constructed. */ void identity(View *v) const { ::new((void*) v) View(m_allocator); } /** @name construct functions * * All `construct()` functions first construct the leftmost view, using * the optional @a x1, @a x2, and @a x3 arguments that were passed in from * the reducer constructor. They then call the view's `get_allocator()` * function to get the list allocator from its contained list, and pass it * to the monoid constructor. */ //@{ template static void construct(Monoid* monoid, View* view) { view_guard vg( new((void*) view) View() ); vg.confirm_if( new((void*) monoid) Monoid(view->get_allocator()) ); } template static void construct(Monoid* monoid, View* view, const T1& x1) { view_guard vg( new((void*) view) View(x1) ); vg.confirm_if( new((void*) monoid) Monoid(view->get_allocator()) ); } template static void construct(Monoid* monoid, View* view, const T1& x1, const T2& x2) { view_guard vg( new((void*) view) View(x1, x2) ); vg.confirm_if( new((void*) monoid) Monoid(view->get_allocator()) ); } template static void construct(Monoid* monoid, View* view, const T1& x1, const T2& x2, const T3& x3) { view_guard vg( new((void*) view) View(x1, x2, x3) ); vg.confirm_if( new((void*) monoid) Monoid(view->get_allocator()) ); } //@} }; //@} } // namespace internal /** @ingroup ReducersList */ //@{ /** The list-append reducer view class. * * This is the view class for reducers created with * `cilk::reducer< cilk::op_list_append >`. It holds the * accumulator variable for the reduction, and allows only append operations * to be performed on it. * * @note The reducer "dereference" operation (`reducer::operator *()`) * yields a reference to the view. Thus, for example, the view class's * `push_back` operation would be used in an expression like * `r->push_back(a)`, where `r` is a list-append reducer variable. * * @tparam Type The list element type (not the list type). * @tparam Allocator The list allocator type. * * @see ReducersList * @see op_list_append */ template ::allocator_type> class op_list_append_view : public internal::list_view_base { typedef internal::list_view_base base; typedef std::list list_type; typedef typename list_type::iterator iterator; iterator end() { return this->m_value.end(); } public: /** @name Constructors. * * All op_list_append_view constructors simply pass their arguments on to * the @ref internal::list_view_base base class constructor. * * @ref internal::list_view_base supports all the std::list constructor * forms, as well as the reducer move_in constructor form. */ //@{ op_list_append_view() : base() {} template op_list_append_view(const T1& x1) : base(x1) {} template op_list_append_view(const T1& x1, const T2& x2) : base(x1, x2) {} template op_list_append_view(const T1& x1, const T2& x2, const T3& x3) : base(x1, x2, x3) {} //@} /** @name View modifier operations. */ //@{ /** Adds an element at the end of the list. * * This is equivalent to `list.push_back(element)` */ void push_back(const Type& element) { this->m_value.push_back(element); } /** Inserts elements at the end of the list. * * This is equivalent to `list.insert(list.end(), n, element)` */ void insert_back(typename list_type::size_type n, const Type& element) { this->m_value.insert(end(), n, element); } /** Inserts elements at the end of the list. * * This is equivalent to `list.insert(list.end(), first, last)` */ template void insert_back(Iter first, Iter last) { this->m_value.insert(end(), first, last); } /** Splices elements at the end of the list. * * This is equivalent to `list.splice(list.end(), x)` */ void splice_back(list_type& x) { if (x.get_allocator() == this->m_value.get_allocator()) this->m_value.splice(end(), x); else { insert_back(x.begin(), x.end()); x.clear(); } } /** Splices elements at the end of the list. * * This is equivalent to `list.splice(list.end(), x, i)` */ void splice_back(list_type& x, iterator i) { if (x.get_allocator() == this->m_value.get_allocator()) this->m_value.splice(end(), x, i); else { push_back(*i); x.erase(i); } } /** Splices elements at the end of the list. * * This is equivalent to `list.splice(list.end(), x, first, last)` */ void splice_back(list_type& x, iterator first, iterator last) { if (x.get_allocator() == this->m_value.get_allocator()) this->m_value.splice(end(), x, first, last); else { insert_back(first, last); x.erase(first, last); } } //@} /** Reduces the views of two strands. * * This function is invoked by the @ref op_list_append monoid to combine * the views of two strands when the right strand merges with the left * one. It appends the value contained in the right-strand view to the * value contained in the left-strand view, and leaves the value in the * right-strand view undefined. * * @param right A pointer to the right-strand view. (`this` points to * the left-strand view.) * * @note Used only by the @ref op_list_append monoid to implement the * monoid reduce operation. */ void reduce(op_list_append_view* right) { __CILKRTS_ASSERT( this->m_value.get_allocator() == right->m_value.get_allocator()); this->m_value.splice(end(), right->m_value); } }; /** The list-prepend reducer view class. * * This is the view class for reducers created with * `cilk::reducer< cilk::op_list_prepend >`. It holds the * accumulator variable for the reduction, and allows only prepend operations * to be performed on it. * * @note The reducer "dereference" operation (`reducer::operator *()`) * yields a reference to the view. Thus, for example, the view class's * `push_front` operation would be used in an expression like * `r->push_front(a)`, where `r` is a list-prepend reducer variable. * * @tparam Type The list element type (not the list type). * @tparam Allocator The list allocator type. * * @see ReducersList * @see op_list_prepend */ template ::allocator_type> class op_list_prepend_view : public internal::list_view_base { typedef internal::list_view_base base; typedef std::list list_type; typedef typename list_type::iterator iterator; iterator begin() { return this->m_value.begin(); } public: /** @name Constructors. * * All op_list_prepend_view constructors simply pass their arguments on to * the @ref internal::list_view_base base class constructor. * * @ref internal::list_view_base supports all the std::list constructor * forms, as well as the reducer move_in constructor form. * */ //@{ op_list_prepend_view() : base() {} template op_list_prepend_view(const T1& x1) : base(x1) {} template op_list_prepend_view(const T1& x1, const T2& x2) : base(x1, x2) {} template op_list_prepend_view(const T1& x1, const T2& x2, const T3& x3) : base(x1, x2, x3) {} //@} /** @name View modifier operations. */ //@{ /** Adds an element at the beginning of the list. * * This is equivalent to `list.push_front(element)` */ void push_front(const Type& element) { this->m_value.push_front(element); } /** Inserts elements at the beginning of the list. * * This is equivalent to `list.insert(list.begin(), n, element)` */ void insert_front(typename list_type::size_type n, const Type& element) { this->m_value.insert(begin(), n, element); } /** Inserts elements at the beginning of the list. * * This is equivalent to `list.insert(list.begin(), first, last)` */ template void insert_front(Iter first, Iter last) { this->m_value.insert(begin(), first, last); } /** Splices elements at the beginning of the list. * * This is equivalent to `list.splice(list.begin(), x)` */ void splice_front(list_type& x) { if (x.get_allocator() == this->m_value.get_allocator()) this->m_value.splice(begin(), x); else { insert_front(x.begin(), x.begin()); x.clear(); } } /** Splices elements at the beginning of the list. * * This is equivalent to `list.splice(list.begin(), x, i)` */ void splice_front(list_type& x, iterator i) { if (x.get_allocator() == this->m_value.get_allocator()) this->m_value.splice(begin(), x, i); else { push_front(*i); x.erase(i); } } /** Splices elements at the beginning of the list. * * This is equivalent to `list.splice(list.begin(), x, first, last)` */ void splice_front(list_type& x, iterator first, iterator last) { if (x.get_allocator() == this->m_value.get_allocator()) this->m_value.splice(begin(), x, first, last); else { insert_front(first, last); x.erase(first, last); } } //@} /** Reduces the views of two strands. * * This function is invoked by the @ref op_list_prepend monoid to combine * the views of two strands when the right strand merges with the left * one. It prepends the value contained in the right-strand view to the * value contained in the left-strand view, and leaves the value in the * right-strand view undefined. * * @param right A pointer to the right-strand view. (`this` points to * the left-strand view.) * * @note Used only by the @ref op_list_prepend monoid to implement the * monoid reduce operation. */ /** Reduce operation. * * Required by @ref monoid_base. */ void reduce(op_list_prepend_view* right) { __CILKRTS_ASSERT( this->m_value.get_allocator() == right->m_value.get_allocator()); this->m_value.splice(begin(), right->m_value); } }; /** Monoid class for list-append reductions. Instantiate the cilk::reducer * template class with a op_list_append monoid to create a list-append reducer * class. For example, to create a list of strings: * * cilk::reducer< cilk::op_list_append > r; * * @tparam Type The list element type (not the list type). * @tparam Alloc The list allocator type. * @tparam Align If `false` (the default), reducers instantiated on this * monoid will be naturally aligned (the Intel Cilk Plus library 1.0 * behavior). If `true`, reducers instantiated on this monoid * will be cache-aligned for binary compatibility with * reducers in Intel Cilk Plus library version 0.9. * * @see ReducersList * @see op_list_append_view */ template ::allocator_type, bool Align = false> struct op_list_append : public internal::list_monoid_base, Align> { /// Construct with default allocator. op_list_append() {} /// Construct with specified allocator. op_list_append(const Allocator& alloc) : internal::list_monoid_base, Align>(alloc) {} }; /** Monoid class for list-prepend reductions. Instantiate the cilk::reducer * template class with a op_list_prepend monoid to create a list-prepend * reducer class. For example, to create a list of strings: * * cilk::reducer< cilk::op_list_prepend > r; * * @tparam Type The list element type (not the list type). * @tparam Alloc The list allocator type. * @tparam Align If `false` (the default), reducers instantiated on this * monoid will be naturally aligned (the Intel Cilk Plus library 1.0 * behavior). If `true`, reducers instantiated on this monoid * will be cache-aligned for binary compatibility with * reducers in Intel Cilk Plus library version 0.9. * * @see ReducersList * @see op_list_prepend_view */ template ::allocator_type, bool Align = false> struct op_list_prepend : public internal::list_monoid_base, Align> { /// Construct with default allocator. op_list_prepend() {} /// Construct with specified allocator. op_list_prepend(const Allocator& alloc) : internal::list_monoid_base, Align>(alloc) {} }; /** Deprecated list-append reducer wrapper class. * * reducer_list_append is the same as * @ref reducer<@ref op_list_append>, except that reducer_list_append is a * proxy for the contained view, so that accumulator variable update * operations can be applied directly to the reducer. For example, an element * is appended to a `reducer<%op_list_append>` with `r->push_back(a)`, but an * element can be appended to a `%reducer_list_append` with `r.push_back(a)`. * * @deprecated Users are strongly encouraged to use `reducer` * reducers rather than the old wrappers like reducer_list_append. * The `reducer` reducers show the reducer/monoid/view * architecture more clearly, are more consistent in their * implementation, and present a simpler model for new * user-implemented reducers. * * @note Implicit conversions are provided between `%reducer_list_append` * and `reducer<%op_list_append>`. This allows incremental code * conversion: old code that used `%reducer_list_append` can pass a * `%reducer_list_append` to a converted function that now expects a * pointer or reference to a `reducer<%op_list_append>`, and vice * versa. * * @tparam Type The value type of the list. * @tparam Allocator The allocator type of the list. * * @see op_list_append * @see reducer * @see ReducersList */ template > class reducer_list_append : public reducer > { typedef reducer > base; using base::view; public: /// The reducer's list type. typedef typename base::value_type list_type; /// The list's element type. typedef Type list_value_type; /// The reducer's primitive component type. typedef Type basic_value_type; /// The monoid type. typedef typename base::monoid_type Monoid; /** @name Constructors */ //@{ /** Constructs a reducer with an empty list. */ reducer_list_append() {} /** Constructs a reducer with a specified initial list value. */ reducer_list_append(const std::list &initial_value) : base(initial_value) {} //@} /** @name Forwarded functions * @details Functions that update the contained accumulator variable are * simply forwarded to the contained @ref op_and_view. */ //@{ /// @copydoc op_list_append_view::push_back(const Type&) void push_back(const Type& element) { view().push_back(element); } //@} /** Allows mutable access to the list within the current view. * * @warning If this method is called before the parallel calculation is * complete, the list returned by this method will be a partial * result. * * @returns A mutable reference to the list within the current view. */ list_type &get_reference() { return view().view_get_reference(); } /** Allows read-only access to the list within the current view. * * @warning If this method is called before the parallel calculation is * complete, the list returned by this method will be a partial * result. * * @returns A const reference to the list within the current view. */ list_type const &get_reference() const { return view().view_get_reference(); } /// @name Dereference //@{ /** Dereferencing a wrapper is a no-op. It simply returns the wrapper. * Combined with the rule that a wrapper forwards view operations to the * view, this means that view operations can be written the same way on * reducers and wrappers, which is convenient for incrementally * converting code using wrappers to code using reducers. That is: * * reducer< op_list_append > r; * r->push_back(a); // *r returns the view * // push_back is a view member function * * reducer_list_append w; * w->push_back(a); // *w returns the wrapper * // push_back is a wrapper member function that * // calls the corresponding view function */ //@{ reducer_list_append& operator*() { return *this; } reducer_list_append const& operator*() const { return *this; } reducer_list_append* operator->() { return this; } reducer_list_append const* operator->() const { return this; } //@} /** @name Upcast * @details In Intel Cilk Plus library 0.9, reducers were always cache-aligned. * In library 1.0, reducer cache alignment is optional. By default, * reducers are unaligned (i.e., just naturally aligned), but legacy * wrappers inherit from cache-aligned reducers for binary compatibility. * * This means that a wrapper will automatically be upcast to its aligned * reducer base class. The following conversion operators provide * pseudo-upcasts to the corresponding unaligned reducer class. */ //@{ operator reducer< op_list_append >& () { return *reinterpret_cast< reducer< op_list_append >* >(this); } operator const reducer< op_list_append >& () const { return *reinterpret_cast< const reducer< op_list_append >* >(this); } //@} }; /** Deprecated list-prepend reducer wrapper class. * * reducer_list_prepend is the same as * @ref reducer<@ref op_list_prepend>, except that reducer_list_prepend is a * proxy for the contained view, so that accumulator variable update operations * can be applied directly to the reducer. For example, an element is prepended * to a `reducer` with `r->push_back(a)`, but an element is * prepended to a `reducer_list_prepend` with `r.push_back(a)`. * * @deprecated Users are strongly encouraged to use `reducer` * reducers rather than the old wrappers like reducer_list_prepend. * The `reducer` reducers show the reducer/monoid/view * architecture more clearly, are more consistent in their * implementation, and present a simpler model for new * user-implemented reducers. * * @note Implicit conversions are provided between `%reducer_list_prepend` * and `reducer<%op_list_prepend>`. This allows incremental code * conversion: old code that used `%reducer_list_prepend` can pass a * `%reducer_list_prepend` to a converted function that now expects a * pointer or reference to a `reducer<%op_list_prepend>`, and vice * versa. * * @tparam Type The value type of the list. * @tparam Allocator The allocator type of the list. * * @see op_list_prepend * @see reducer * @see ReducersList */ template > class reducer_list_prepend : public reducer > { typedef reducer > base; using base::view; public: /** The reducer's list type. */ typedef typename base::value_type list_type; /** The list's element type. */ typedef Type list_value_type; /** The reducer's primitive component type. */ typedef Type basic_value_type; /** The monoid type. */ typedef typename base::monoid_type Monoid; /** @name Constructors */ //@{ /** Constructs a reducer with an empty list. */ reducer_list_prepend() {} /** Constructs a reducer with a specified initial list value. */ reducer_list_prepend(const std::list &initial_value) : base(initial_value) {} //@} /** @name Forwarded functions * @details Functions that update the contained accumulator variable are * simply forwarded to the contained @ref op_and_view. */ //@{ /// @copydoc op_list_prepend_view::push_front(const Type&) void push_front(const Type& element) { view().push_front(element); } //@} /** Allows mutable access to the list within the current view. * * @warning If this method is called before the parallel calculation is * complete, the list returned by this method will be a partial * result. * * @returns A mutable reference to the list within the current view. */ list_type &get_reference() { return view().view_get_reference(); } /** Allows read-only access to the list within the current view. * * @warning If this method is called before the parallel calculation is * complete, the list returned by this method will be a partial * result. * * @returns A const reference to the list within the current view. */ list_type const &get_reference() const { return view().view_get_reference(); } /// @name Dereference /** Dereferencing a wrapper is a no-op. It simply returns the wrapper. * Combined with the rule that a wrapper forwards view operations to the * view, this means that view operations can be written the same way on * reducers and wrappers, which is convenient for incrementally * converting code using wrappers to code using reducers. That is: * * reducer< op_list_prepend > r; * r->push_front(a); // *r returns the view * // push_front is a view member function * * reducer_list_prepend w; * w->push_front(a); // *w returns the wrapper * // push_front is a wrapper member function that * // calls the corresponding view function */ //@{ reducer_list_prepend& operator*() { return *this; } reducer_list_prepend const& operator*() const { return *this; } reducer_list_prepend* operator->() { return this; } reducer_list_prepend const* operator->() const { return this; } //@} /** @name Upcast * @details In Intel Cilk Plus library 0.9, reducers were always cache-aligned. * In library 1.0, reducer cache alignment is optional. By default, * reducers are unaligned (i.e., just naturally aligned), but legacy * wrappers inherit from cache-aligned reducers for binary compatibility. * * This means that a wrapper will automatically be upcast to its aligned * reducer base class. The following conversion operators provide * pseudo-upcasts to the corresponding unaligned reducer class. */ //@{ operator reducer< op_list_prepend >& () { return *reinterpret_cast< reducer< op_list_prepend >* >(this); } operator const reducer< op_list_prepend >& () const { return *reinterpret_cast< const reducer< op_list_prepend >* >(this); } //@} }; /// @cond internal /** Metafunction specialization for reducer conversion. * * This specialization of the @ref legacy_reducer_downcast template class * defined in reducer.h causes the `reducer< op_list_append >` * class to have an `operator reducer_list_append& ()` * conversion operator that statically downcasts the `reducer` * to the corresponding `reducer_list_append` type. (The reverse conversion, * from `reducer_list_append` to `reducer`, is just an upcast, * which is provided for free by the language.) */ template struct legacy_reducer_downcast > > { typedef reducer_list_append type; }; /** Metafunction specialization for reducer conversion. * * This specialization of the @ref legacy_reducer_downcast template class * defined in reducer.h causes the * `reducer< op_list_prepend >` class to have an * `operator reducer_list_prepend& ()` conversion operator * that statically downcasts the `reducer` to the * corresponding `reducer_list_prepend` type. (The reverse conversion, from * `reducer_list_prepend` to `reducer`, is just an upcast, * which is provided for free by the language.) */ template struct legacy_reducer_downcast > > { typedef reducer_list_prepend type; }; /// @endcond //@} } // Close namespace cilk #endif // REDUCER_LIST_H_INCLUDED