MichaelList, LazyList, MichaelMap:
[libcds.git] / cds / container / impl / lazy_list.h
1 //$$CDS-header$$
2
3 #ifndef CDSLIB_CONTAINER_IMPL_LAZY_LIST_H
4 #define CDSLIB_CONTAINER_IMPL_LAZY_LIST_H
5
6 #include <memory>
7 #include <cds/container/details/guarded_ptr_cast.h>
8
9 namespace cds { namespace container {
10
11     /// Lazy ordered list
12     /** @ingroup cds_nonintrusive_list
13         @anchor cds_nonintrusive_LazyList_gc
14
15         Usually, ordered single-linked list is used as a building block for the hash table implementation.
16         The complexity of searching is <tt>O(N)</tt>.
17
18         Source:
19         - [2005] Steve Heller, Maurice Herlihy, Victor Luchangco, Mark Moir, William N. Scherer III, and Nir Shavit
20                  "A Lazy Concurrent List-Based Set Algorithm"
21
22         The lazy list is based on an optimistic locking scheme for inserts and removes,
23         eliminating the need to use the equivalent of an atomically markable
24         reference. It also has a novel wait-free membership \p find() operation
25         that does not need to perform cleanup operations and is more efficient.
26
27         It is non-intrusive version of \p cds::intrusive::LazyList class.
28
29         Template arguments:
30         - \p GC - garbage collector: \p gc::HP, \p gp::DHP
31         - \p T - type to be stored in the list.
32         - \p Traits - type traits, default is \p lazy_list::traits.
33             It is possible to declare option-based list with \p lazy_list::make_traits metafunction istead of \p Traits template
34             argument. For example, the following traits-based declaration of \p gc::HP lazy list
35             \code
36             #include <cds/container/lazy_list_hp.h>
37             // Declare comparator for the item
38             struct my_compare {
39                 int operator ()( int i1, int i2 )
40                 {
41                     return i1 - i2;
42                 }
43             };
44
45             // Declare traits
46             struct my_traits: public cds::container::lazy_list::traits
47             {
48                 typedef my_compare compare;
49             };
50
51             // Declare traits-based list
52             typedef cds::container::LazyList< cds::gc::HP, int, my_traits > traits_based_list;
53             \endcode
54             is equal to  the following option-based list:
55             \code
56             #include <cds/container/lazy_list_hp.h>
57
58             // my_compare is the same
59
60             // Declare option-based list
61             typedef cds::container::LazyList< cds::gc::HP, int,
62                 typename cds::container::lazy_list::make_traits<
63                     cds::container::opt::compare< my_compare >     // item comparator option
64                 >::type
65             >     option_based_list;
66             \endcode
67
68         Unlike standard container, this implementation does not divide type \p T into key and value part and
69         may be used as main building block for hash set algorithms.
70
71         The key is a function (or a part) of type \p T, and the comparing function is specified by \p Traits::compare functor
72         or \p Traits::less predicate.
73
74         \p LazyKVList is a key-value version of lazy non-intrusive list that is closer to the C++ std library approach.
75
76         \par Usage
77         There are different specializations of this template for each garbage collecting schema used.
78         You should include appropriate .h-file depending on GC you are using:
79         - for gc::HP: <tt> <cds/container/lazy_list_hp.h> </tt>
80         - for gc::DHP: <tt> <cds/container/lazy_list_dhp.h> </tt>
81         - for \ref cds_urcu_desc "RCU": <tt> <cds/container/lazy_list_rcu.h> </tt>
82         - for gc::nogc: <tt> <cds/container/lazy_list_nogc.h> </tt>
83     */
84     template <
85         typename GC,
86         typename T,
87 #ifdef CDS_DOXYGEN_INVOKED
88         typename Traits = lazy_list::traits
89 #else
90         typename Traits
91 #endif
92     >
93     class LazyList:
94 #ifdef CDS_DOXYGEN_INVOKED
95         protected intrusive::LazyList< GC, T, Traits >
96 #else
97         protected details::make_lazy_list< GC, T, Traits >::type
98 #endif
99     {
100         //@cond
101         typedef details::make_lazy_list< GC, T, Traits > maker;
102         typedef typename maker::type  base_class;
103         //@endcond
104
105     public:
106         typedef GC gc;           ///< Garbage collector used
107         typedef T  value_type;   ///< Type of value stored in the list
108         typedef Traits traits;   ///< List traits
109
110         typedef typename base_class::back_off     back_off;       ///< Back-off strategy used
111         typedef typename maker::allocator_type    allocator_type; ///< Allocator type used for allocate/deallocate the nodes
112         typedef typename base_class::item_counter item_counter;   ///< Item counting policy used
113         typedef typename maker::key_comparator    key_comparator; ///< key comparison functor
114         typedef typename base_class::memory_model memory_model;   ///< Memory ordering. See cds::opt::memory_model option
115
116     protected:
117         //@cond
118         typedef typename base_class::value_type   node_type;
119         typedef typename maker::cxx_allocator     cxx_allocator;
120         typedef typename maker::node_deallocator  node_deallocator;
121         typedef typename maker::intrusive_traits::compare  intrusive_key_comparator;
122
123         typedef typename base_class::node_type head_type;
124         //@endcond
125
126     public:
127         /// Guarded pointer
128         typedef typename gc::template guarded_ptr< node_type, value_type, details::guarded_ptr_cast_set<node_type, value_type> > guarded_ptr;
129
130     private:
131         //@cond
132         static value_type& node_to_value( node_type& n )
133         {
134             return n.m_Value;
135         }
136         static value_type const& node_to_value( node_type const& n )
137         {
138             return n.m_Value;
139         }
140         //@endcond
141
142     protected:
143         //@cond
144         template <typename Q>
145         static node_type * alloc_node( Q const& v )
146         {
147             return cxx_allocator().New( v );
148         }
149
150         template <typename... Args>
151         static node_type * alloc_node( Args&&... args )
152         {
153             return cxx_allocator().MoveNew( std::forward<Args>(args)... );
154         }
155
156         static void free_node( node_type * pNode )
157         {
158             cxx_allocator().Delete( pNode );
159         }
160
161         struct node_disposer {
162             void operator()( node_type * pNode )
163             {
164                 free_node( pNode );
165             }
166         };
167         typedef std::unique_ptr< node_type, node_disposer >     scoped_node_ptr;
168
169         head_type& head()
170         {
171             return base_class::m_Head;
172         }
173
174         head_type const& head() const
175         {
176             return base_class::m_Head;
177         }
178
179         head_type& tail()
180         {
181             return base_class::m_Tail;
182         }
183
184         head_type const&  tail() const
185         {
186             return base_class::m_Tail;
187         }
188         //@endcond
189
190     protected:
191                 //@cond
192         template <bool IsConst>
193         class iterator_type: protected base_class::template iterator_type<IsConst>
194         {
195             typedef typename base_class::template iterator_type<IsConst>    iterator_base;
196
197             iterator_type( head_type const& pNode )
198                 : iterator_base( const_cast<head_type *>( &pNode ))
199             {}
200
201             iterator_type( head_type const * pNode )
202                 : iterator_base( const_cast<head_type *>( pNode ))
203             {}
204
205             friend class LazyList;
206
207         public:
208             typedef typename cds::details::make_const_type<value_type, IsConst>::pointer   value_ptr;
209             typedef typename cds::details::make_const_type<value_type, IsConst>::reference value_ref;
210
211             iterator_type()
212             {}
213
214             iterator_type( const iterator_type& src )
215                 : iterator_base( src )
216             {}
217
218             value_ptr operator ->() const
219             {
220                 typename iterator_base::value_ptr p = iterator_base::operator ->();
221                 return p ? &(p->m_Value) : nullptr;
222             }
223
224             value_ref operator *() const
225             {
226                 return (iterator_base::operator *()).m_Value;
227             }
228
229             /// Pre-increment
230             iterator_type& operator ++()
231             {
232                 iterator_base::operator ++();
233                 return *this;
234             }
235
236             template <bool C>
237             bool operator ==(iterator_type<C> const& i ) const
238             {
239                 return iterator_base::operator ==(i);
240             }
241             template <bool C>
242             bool operator !=(iterator_type<C> const& i ) const
243             {
244                 return iterator_base::operator !=(i);
245             }
246         };
247         //@endcond
248
249     public:
250         /// Forward iterator
251         /**
252             The forward iterator for lazy list has some features:
253             - it has no post-increment operator
254             - to protect the value, the iterator contains a GC-specific guard + another guard is required locally for increment operator.
255               For some GC (\p gc::HP), a guard is limited resource per thread, so an exception (or assertion) "no free guard"
256               may be thrown if a limit of guard count per thread is exceeded.
257             - The iterator cannot be moved across thread boundary since it contains GC's guard that is thread-private GC data.
258             - Iterator ensures thread-safety even if you delete the item that iterator points to. However, in case of concurrent
259               deleting operations it is no guarantee that you iterate all item in the list.
260
261             Therefore, the use of iterators in concurrent environment is not good idea. Use the iterator on the concurrent container
262             for debug purpose only.
263         */
264         typedef iterator_type<false>    iterator;
265
266         /// Const forward iterator
267         /**
268             For iterator's features and requirements see \ref iterator
269         */
270         typedef iterator_type<true>     const_iterator;
271
272         /// Returns a forward iterator addressing the first element in a list
273         /**
274             For empty list \code begin() == end() \endcode
275         */
276         iterator begin()
277         {
278             iterator it( head() );
279             ++it        ;   // skip dummy head node
280             return it;
281         }
282
283         /// Returns an iterator that addresses the location succeeding the last element in a list
284         /**
285             Do not use the value returned by <tt>end</tt> function to access any item.
286
287             The returned value can be used only to control reaching the end of the list.
288             For empty list \code begin() == end() \endcode
289         */
290         iterator end()
291         {
292             return iterator( tail() );
293         }
294
295         /// Returns a forward const iterator addressing the first element in a list
296         //@{
297         const_iterator begin() const
298         {
299             const_iterator it( head() );
300             ++it        ;   // skip dummy head node
301             return it;
302         }
303         const_iterator cbegin() const
304         {
305             const_iterator it( head() );
306             ++it        ;   // skip dummy head node
307             return it;
308         }
309         //@}
310
311         /// Returns an const iterator that addresses the location succeeding the last element in a list
312         //@{
313         const_iterator end() const
314         {
315             return const_iterator( tail() );
316         }
317         const_iterator cend() const
318         {
319             return const_iterator( tail() );
320         }
321         //@}
322
323     public:
324         /// Default constructor
325         LazyList()
326         {}
327
328         /// Destructor clears the list
329         ~LazyList()
330         {
331             clear();
332         }
333
334         /// Inserts new node
335         /**
336             The function creates a node with copy of \p val value
337             and then inserts the node created into the list.
338
339             The type \p Q should contain as minimum the complete key of the node.
340             The object of \ref value_type should be constructible from \p val of type \p Q.
341             In trivial case, \p Q is equal to \ref value_type.
342
343             Returns \p true if inserting successful, \p false otherwise.
344         */
345         template <typename Q>
346         bool insert( Q const& val )
347         {
348             return insert_at( head(), val );
349         }
350
351         /// Inserts new node
352         /**
353             This function inserts new node with default-constructed value and then it calls
354             \p func functor with signature
355             \code void func( value_type& item ) ;\endcode
356
357             The argument \p item of user-defined functor \p func is the reference
358             to the list's item inserted.
359             When \p func is called it has exclusive access to the item.
360             The user-defined functor is called only if the inserting is success.
361
362             The type \p Q should contain the complete key of the node.
363             The object of \p value_type should be constructible from \p key of type \p Q.
364
365             The function allows to split creating of new item into two part:
366             - create item from \p key with initializing key-fields only;
367             - insert new item into the list;
368             - if inserting is successful, initialize non-key fields of item by calling \p func functor
369
370             This can be useful if complete initialization of object of \p value_type is heavyweight and
371             it is preferable that the initialization should be completed only if inserting is successful.
372         */
373         template <typename Q, typename Func>
374         bool insert( Q const& key, Func func )
375         {
376             return insert_at( head(), key, func );
377         }
378
379         /// Inserts data of type \p value_type constructed from \p args
380         /**
381             Returns \p true if inserting successful, \p false otherwise.
382         */
383         template <typename... Args>
384         bool emplace( Args&&... args )
385         {
386             return emplace_at( head(), std::forward<Args>(args)... );
387         }
388
389         /// Updates data by \p key
390         /**
391             The operation performs inserting or replacing the element with lock-free manner.
392
393             If the \p key not found in the list, then the new item created from \p key
394             will be inserted iff \p bAllowInsert is \p true.
395             Otherwise, if \p key is found, the functor \p func is called with item found.
396
397             The functor \p Func signature is:
398             \code
399                 struct my_functor {
400                     void operator()( bool bNew, value_type& item, Q const& val );
401                 };
402             \endcode
403
404             with arguments:
405             - \p bNew - \p true if the item has been inserted, \p false otherwise
406             - \p item - item of the list
407             - \p val - argument \p key passed into the \p %update() function
408
409             The functor may change non-key fields of the \p item;
410             during \p func call \p item is locked so it is safe to modify the item in
411             multi-threaded environment.
412
413             Returns <tt> std::pair<bool, bool> </tt> where \p first is true if operation is successfull,
414             \p second is true if new item has been added or \p false if the item with \p key
415             already exists.
416         */
417         template <typename Q, typename Func>
418         std::pair<bool, bool> update( Q const& key, Func func, bool bAllowInsert = true )
419         {
420             return update_at( head(), key, func, bAllowInsert );
421         }
422         //@cond
423         template <typename Q, typename Func>
424         std::pair<bool, bool> ensure( Q const& key, Func f )
425         {
426             return update( key, f, true );
427         }
428         //@endcond
429
430         /// Deletes \p key from the list
431         /** \anchor cds_nonintrusive_LazyList_hp_erase_val
432             Since the key of LazyList's item type \p T is not explicitly specified,
433             template parameter \p Q defines the key type searching in the list.
434             The list item comparator should be able to compare the type \p T of list item
435             and the type \p Q.
436
437             Return \p true if key is found and deleted, \p false otherwise
438         */
439         template <typename Q>
440         bool erase( Q const& key )
441         {
442             return erase_at( head(), key, intrusive_key_comparator(), [](value_type const&){} );
443         }
444
445         /// Deletes the item from the list using \p pred predicate for searching
446         /**
447             The function is an analog of \ref cds_nonintrusive_LazyList_hp_erase_val "erase(Q const&)"
448             but \p pred is used for key comparing.
449             \p Less functor has the interface like \p std::less.
450             \p pred must imply the same element order as the comparator used for building the list.
451         */
452         template <typename Q, typename Less>
453         bool erase_with( Q const& key, Less pred )
454         {
455             CDS_UNUSED( pred );
456             return erase_at( head(), key, typename maker::template less_wrapper<Less>::type(), [](value_type const&){} );
457         }
458
459         /// Deletes \p key from the list
460         /** \anchor cds_nonintrusive_LazyList_hp_erase_func
461             The function searches an item with key \p key, calls \p f functor with item found
462             and deletes the item. If \p key is not found, the functor is not called.
463
464             The functor \p Func interface:
465             \code
466             struct extractor {
467                 void operator()(const value_type& val) { ... }
468             };
469             \endcode
470
471             Since the key of LazyList's item type \p T is not explicitly specified,
472             template parameter \p Q defines the key type searching in the list.
473             The list item comparator should be able to compare the type \p T of list item
474             and the type \p Q.
475
476             Return \p true if key is found and deleted, \p false otherwise
477
478             See also: \ref erase
479         */
480         template <typename Q, typename Func>
481         bool erase( Q const& key, Func f )
482         {
483             return erase_at( head(), key, intrusive_key_comparator(), f );
484         }
485
486         /// Deletes the item from the list using \p pred predicate for searching
487         /**
488             The function is an analog of \ref cds_nonintrusive_LazyList_hp_erase_func "erase(Q const&, Func)"
489             but \p pred is used for key comparing.
490             \p Less functor has the interface like \p std::less.
491             \p pred must imply the same element order as the comparator used for building the list.
492         */
493         template <typename Q, typename Less, typename Func>
494         bool erase_with( Q const& key, Less pred, Func f )
495         {
496             CDS_UNUSED( pred );
497             return erase_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
498         }
499
500         /// Extracts the item from the list with specified \p key
501         /** \anchor cds_nonintrusive_LazyList_hp_extract
502             The function searches an item with key equal to \p key,
503             unlinks it from the list, and returns it as \p guarded_ptr.
504             If \p key is not found the function returns an empty guarded pointer.
505
506             Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
507
508             @note Each \p guarded_ptr object uses the GC's guard that can be limited resource.
509
510             Usage:
511             \code
512             typedef cds::container::LazyList< cds::gc::HP, foo, my_traits >  ord_list;
513             ord_list theList;
514             // ...
515             {
516                 ord_list::guarded_ptr gp(theList.extract( 5 ));
517                 if ( gp ) {
518                     // Deal with gp
519                     // ...
520                 }
521                 // Destructor of gp releases internal HP guard and frees the item
522             }
523             \endcode
524         */
525         template <typename Q>
526         guarded_ptr extract( Q const& key )
527         {
528             guarded_ptr gp;
529             extract_at( head(), gp.guard(), key, intrusive_key_comparator() );
530             return gp;
531         }
532
533         /// Extracts the item from the list with comparing functor \p pred
534         /**
535             The function is an analog of \ref cds_nonintrusive_LazyList_hp_extract "extract(Q const&)"
536             but \p pred predicate is used for key comparing.
537
538             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
539             in any order.
540             \p pred must imply the same element order as the comparator used for building the list.
541         */
542         template <typename Q, typename Less>
543         guarded_ptr extract_with( Q const& key, Less pred )
544         {
545             CDS_UNUSED( pred );
546             guarded_ptr gp;
547             extract_at( head(), gp.guard(), key, typename maker::template less_wrapper<Less>::type() );
548             return gp;
549         }
550
551         /// Checks whether the list contains \p key
552         /**
553             The function searches the item with key equal to \p key
554             and returns \p true if it is found, and \p false otherwise.
555         */
556         template <typename Q>
557         bool contains( Q const& key )
558         {
559             return find_at( head(), key, intrusive_key_comparator() );
560         }
561         //@cond
562         // Deprecated, use contains()
563         template <typename Q>
564         bool find( Q const& key )
565         {
566             return contains( key );
567         }
568         //@endcond
569
570         /// Checks whether the list contains \p key using \p pred predicate for searching
571         /**
572             The function is an analog of <tt>contains( key )</tt> but \p pred is used for key comparing.
573             \p Less functor has the interface like \p std::less.
574             \p pred must imply the same element order as the comparator used for building the list.
575         */
576         template <typename Q, typename Less>
577         bool contains( Q const& key, Less pred )
578         {
579             CDS_UNUSED( pred );
580             return find_at( head(), key, typename maker::template less_wrapper<Less>::type() );
581         }
582         //@cond
583         // Deprecated, use contains()
584         template <typename Q, typename Less>
585         bool find_with( Q const& key, Less pred )
586         {
587             return contains( key, pred );
588         }
589         //@endcond
590         /// Finds the key \p key and performs an action with it
591         /** \anchor cds_nonintrusive_LazyList_hp_find_func
592             The function searches an item with key equal to \p key and calls the functor \p f for the item found.
593             The interface of \p Func functor is:
594             \code
595             struct functor {
596                 void operator()( value_type& item, Q& key );
597             };
598             \endcode
599             where \p item is the item found, \p key is the <tt>find</tt> function argument.
600
601             The functor may change non-key fields of \p item. Note that the function is only guarantee
602             that \p item cannot be deleted during functor is executing.
603             The function does not serialize simultaneous access to the list \p item. If such access is
604             possible you must provide your own synchronization schema to exclude unsafe item modifications.
605
606             The \p key argument is non-const since it can be used as \p f functor destination i.e., the functor
607             may modify both arguments.
608
609             The function returns \p true if \p key is found, \p false otherwise.
610         */
611         template <typename Q, typename Func>
612         bool find( Q& key, Func f )
613         {
614             return find_at( head(), key, intrusive_key_comparator(), f );
615         }
616         //@cond
617         template <typename Q, typename Func>
618         bool find( Q const& key, Func f )
619         {
620             return find_at( head(), key, intrusive_key_comparator(), f );
621         }
622         //@endcond
623
624         /// Finds the key \p key using \p pred predicate for searching
625         /**
626             The function is an analog of \ref cds_nonintrusive_LazyList_hp_find_func "find(Q&, Func)"
627             but \p pred is used for key comparing.
628             \p Less functor has the interface like \p std::less.
629             \p pred must imply the same element order as the comparator used for building the list.
630         */
631         template <typename Q, typename Less, typename Func>
632         bool find_with( Q& key, Less pred, Func f )
633         {
634             CDS_UNUSED( pred );
635             return find_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
636         }
637         //@cond
638         template <typename Q, typename Less, typename Func>
639         bool find_with( Q const& key, Less pred, Func f )
640         {
641             CDS_UNUSED( pred );
642             return find_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
643         }
644         //@endcond
645
646         /// Finds the key \p key and return the item found
647         /** \anchor cds_nonintrusive_LazyList_hp_get
648             The function searches the item with key equal to \p key
649             and returns the item found as \p guarded_ptr.
650             If \p key is not found the function returns an empty guarded pointer.
651
652             @note Each \p guarded_ptr object uses one GC's guard which can be limited resource.
653
654             Usage:
655             \code
656             typedef cds::container::LazyList< cds::gc::HP, foo, my_traits >  ord_list;
657             ord_list theList;
658             // ...
659             {
660                 ord_list::guarded_ptr gp( theList.get( 5 ));
661                 if ( gp ) {
662                     // Deal with gp
663                     //...
664                 }
665                 // Destructor of guarded_ptr releases internal HP guard and frees the item
666             }
667             \endcode
668
669             Note the compare functor specified for class \p Traits template parameter
670             should accept a parameter of type \p Q that can be not the same as \p value_type.
671         */
672         template <typename Q>
673         guarded_ptr get( Q const& key )
674         {
675             guarded_ptr gp;
676             get_at( head(), gp.guard(), key, intrusive_key_comparator() );
677             return gp;
678         }
679
680         /// Finds the key \p key and return the item found
681         /**
682             The function is an analog of \ref cds_nonintrusive_LazyList_hp_get "get( Q const&)"
683             but \p pred is used for comparing the keys.
684
685             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
686             in any order.
687             \p pred must imply the same element order as the comparator used for building the list.
688         */
689         template <typename Q, typename Less>
690         guarded_ptr get_with( Q const& key, Less pred )
691         {
692             CDS_UNUSED( pred );
693             guarded_ptr gp;
694             get_at( head(), gp.guard(), key, typename maker::template less_wrapper<Less>::type() );
695             return gp;
696         }
697
698         /// Checks whether the list is empty
699         bool empty() const
700         {
701             return base_class::empty();
702         }
703
704         /// Returns list's item count
705         /**
706             The value returned depends on \p Traits::item_counter type. For \p atomicity::empty_item_counter,
707             this function always returns 0.
708
709             @note Even if you use real item counter and it returns 0, this fact is not mean that the list
710             is empty. To check list emptyness use \ref empty() method.
711         */
712         size_t size() const
713         {
714             return base_class::size();
715         }
716
717         /// Clears the list
718         void clear()
719         {
720             base_class::clear();
721         }
722
723     protected:
724         //@cond
725         bool insert_node_at( head_type& refHead, node_type * pNode )
726         {
727             assert( pNode != nullptr );
728             scoped_node_ptr p( pNode );
729
730             if ( base_class::insert_at( &refHead, *pNode )) {
731                 p.release();
732                 return true;
733             }
734
735             return false;
736         }
737
738         template <typename Q>
739         bool insert_at( head_type& refHead, const Q& val )
740         {
741             return insert_node_at( refHead, alloc_node( val ));
742         }
743
744         template <typename... Args>
745         bool emplace_at( head_type& refHead, Args&&... args )
746         {
747             return insert_node_at( refHead, alloc_node( std::forward<Args>(args)... ));
748         }
749
750         template <typename Q, typename Func>
751         bool insert_at( head_type& refHead, const Q& key, Func f )
752         {
753             scoped_node_ptr pNode( alloc_node( key ));
754
755             if ( base_class::insert_at( &refHead, *pNode, [&f](node_type& node){ f( node_to_value(node) ); } )) {
756                 pNode.release();
757                 return true;
758             }
759             return false;
760         }
761
762         template <typename Q, typename Compare, typename Func>
763         bool erase_at( head_type& refHead, const Q& key, Compare cmp, Func f )
764         {
765             return base_class::erase_at( &refHead, key, cmp, [&f](node_type const& node){ f( node_to_value(node) ); } );
766         }
767
768         template <typename Q, typename Compare>
769         bool extract_at( head_type& refHead, typename guarded_ptr::native_guard& guard, Q const& key, Compare cmp )
770         {
771             return base_class::extract_at( &refHead, guard, key, cmp );
772         }
773
774         template <typename Q, typename Func>
775         std::pair<bool, bool> update_at( head_type& refHead, const Q& key, Func f, bool bAllowInsert )
776         {
777             scoped_node_ptr pNode( alloc_node( key ));
778
779             std::pair<bool, bool> ret = base_class::update_at( &refHead, *pNode,
780                 [&f, &key](bool bNew, node_type& node, node_type&){f( bNew, node_to_value(node), key );},
781                 bAllowInsert );
782             if ( ret.first && ret.second )
783                 pNode.release();
784
785             return ret;
786         }
787
788         template <typename Q, typename Compare>
789         bool find_at( head_type& refHead, Q const& key, Compare cmp )
790         {
791             return base_class::find_at( &refHead, key, cmp );
792         }
793
794         template <typename Q, typename Compare, typename Func>
795         bool find_at( head_type& refHead, Q& val, Compare cmp, Func f )
796         {
797             return base_class::find_at( &refHead, val, cmp, [&f](node_type& node, Q& val){ f( node_to_value(node), val ); });
798         }
799
800         template <typename Q, typename Compare>
801         bool get_at( head_type& refHead, typename guarded_ptr::native_guard& guard, Q const& key, Compare cmp )
802         {
803             return base_class::get_at( &refHead, guard, key, cmp );
804         }
805
806         //@endcond
807     };
808
809 }} // namespace cds::container
810
811 #endif // #ifndef CDSLIB_CONTAINER_IMPL_LAZY_LIST_H