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