On Apr 6, 5:55 pm, "Leigh Johnston" <le...@i42.co.uk> wrote:
> "Kai-Uwe Bux" <jkherci...@gmx.net> wrote in message
> > Leigh Johnston wrote:
> >> "Leigh Johnston" <le...@i42.co.uk> wrote in message
> >>news: om...
>
> >>> "Kai-Uwe Bux" <jkherci...@gmx.net> wrote in message
> >>>news:hpg532$snl$...
> >>>> Leigh Johnston wrote:
>
> >>>>> "Keith H Duggar" <dug...@alum.mit.edu> wrote in message
> >>>>> news:601b47ca-0120-492b-ae63-
> > b93967460...@x7g2000vbc.googlegroups.com...
> >>>>>> On Apr 6, 6:46 am, Rui Maciel <rui.mac...@gmail.com> wrote:
> >>>>>>> In order to avoid wasting resources populating a map with
> >>>>>>> useless key:value pairs, is there a clean, unencumbered way
> >>>>>>> to get the value associated with a given key without being
> >>>>>>> forced to insert new elements or even resort to multiple
> >>>>>>> lines of code? The closest thing I saw was the map::find()
> >>>>>>> method, but I believe that ends up forcing to write code to
> >>>>>>> compare the given iterator to map::end() and, if it matches,
> >>>>>>> return a default value.
>
> >>>>>>> Is there a simpler way to do this?
>
> >>>>>> I find these two functions (including their general semantics)
> >>>>>> and variants of them exceedingly useful
>
> >>>>>> template < class K, class V, class C, class A>
> >>>>>> V
> >>>>>> getOrZero (
> >>>>>> std::map<K,V,C,A> const & m
> >>>>>> , K const & k
> >>>>>> ) {
> >>>>>> typename std::map<K,V,C,A>::const_iterator i = m.find(k) ;
> >>>>>> return i != m.end() ? i->second : V() ;
> >>>>>> }
>
> >>>>>> template < class K, class V, class C, class A>
> >>>>>> V &
> >>>>>> getOrMake (
> >>>>>> std::map<K,V,C,A> & m
> >>>>>> , K const & k
> >>>>>> ) {
> >>>>>> return m[k] ;
> >>>>>> }
>
> >>>>>> for all types of containers including STL and custom ones.
>
> >>>>> These free functions can have their uses (they certainly save typing
> >>>>> at
> >>>>> least) but I feel they may detract from good iterator based design.
>
> >>>> I am always eager to see new design ideas, but I have some trouble
> >>>> picturing
> >>>> an iterator to iterate over keys not in the table and providing default
> >>>> values for their un-tabulated value. Could you please flesh out what
> >>>> you
> >>>> have in mind?
>
> >>>> Best
>
> >>>> Kai-Uwe Bux
>
> >>> These free functions seem mainly good for saving some typing for the
> >>> use-case you describe (returning a default if element is not in a
> >>> container) and said use-case is not a particularly common use-case (not
> >>> in my code anyway)
>
> > Just a nit, but context should be given: it happens to be the use case,
> > about which the OP was asking.
>
> >>> and writing binary functions and using them all over
> >>> your code even though one of them only addresses an uncommon use-case is
> >>> not necessarily a good thing.
>
> > Now why would one use them "all over your code" if the use case is not
> > common? It's not the tools fault to addresses a specific need.
>
> >>> In other words 99% of the time you will be
> >>> calling getOrMake which on its own seems rather pointless and less
> >>> useful
> >>> for the unordered containers that offer more than one way of "making"
> >>> elements (push_front, push_back, insert) rendering the claim that such
> >>> functions are useful for all the STL containers false.
>
> > Agreed, but if that is the main criticism, maybe you could have said so in
> > the first place. The way you put it and given the context, I got my hopes
> > up
> > to see a new design idea for the OP's problem based on iterators 
>
> >>> /Leigh
>
> >> To be fair I should have said "less useful" rather than not useful at all
> >> but remember searching the unordered containers is usually linear
> >> complexity which should be borne in mind and increased iterator usage in
> >> a
> >> design my reduce the need for searching and increased iterator usage
> >> should lessen the need for these free functions.
>
> > True: using iterators, one can remember information already obtained. On
> > the
> > other hand, the containers differ vastly in their iterator-invalidation
> > rules. At the very least the free function from above can reduce
> > development
> > time for prototyping (and if profiling shows that there is no performance
> > problem, why change it).
>
> Not sure I can suggest an improvement using iterators however I can suggest
> one minor improvement to getOrZero:
>
> template < class K, class V, class C, class A>
> const V&
> getOrZero (
> std::map<K,V,C,A> const & m
> , K const & k
> ) {
> static const V default;
> typename std::map<K,V,C,A>::const_iterator i = m.find(k) ;
> return i != m.end() ? i->second : default ;
>
> }
>
> We no longer have to make a (possibly RVO optimized) copy of the container
> element if we don't need a copy and if getOrZero is called many times we
> only ever create one default element.
However it also makes thread-safety more difficult for
clients of getOrZero and therefore IMO is not appropriate
for such a library function. Instead I use an overload of
getOrZero which follows the Tao of POSIX reentrant support:
template < class K, class V, class C, class A >
V const &
getOrZero (
std::map<K,V,C,A> const & m
, K const & k
, V const & zero
) {
typename std::map<K,V,C,A>::const_iterator i = m.find(k) ;
return i != m.end() ? i->second : zero ;
}
That overload also provides some useful flexibility with
the "zero" value. For example with number I often find it
useful to use -1 or NaN instead of 0.
I've also experimented from time to time with versions of
them which take callbacks instead. For example
template < class K, class V, class C, class A, class F >
V &
getOrMake (
std::map<K,V,C,A> & m
, K const & k
, F const & f
) {
typename std::map<K,V,C,A>::iterator i = m.find(k) ;
if ( i != m.end() ) {
return i->second ;
} else {
i = m.insert(make_pair(k,f())) ;
return i->second ;
}
}
which allows for interesting things. But for the most part
I've found the simple value based versions the more useful.
KHD