
Show HN: A header only C++11 LRU Cache template class, with no dependencies - mohaps
https://github.com/mohaps/lrucache11
======
en4bz
Not really sure why they reimplemented std::list. Also that NoCopy base class
adds a vptr for no reason. And so do all those other virtual destructors. I
personally wouldn't trust this code at first glance.

~~~
mohaps
the kv::List is not intended as a generic linked list implementation like
std::list.

The reason for the specific implementation is to get constant time removal and
remove-add_to_end operations. Truth be told, it is a hold-over from the
previous implementation. I shall be writing benchmarks for this next up and
shall revisit the decision on whether or not to use std::list soon-ish :)

The : private NoCopy is just to block the copy constructors.

~~~
en4bz
You could have used std::list iterators instead of raw pointers to a node in
the map to implement that.

~~~
mohaps
now i remember why I went with the old linked list implementation.

with the std::list, everytime I refreshed a node... i did a copy.

basically to the tune of list.push_back(*iter) where as I wanted to keep it as
a simple unlink and relink of the node

copy of the comment:
[https://news.ycombinator.com/item?id=12391069](https://news.ycombinator.com/item?id=12391069)

~~~
en4bz
To swap nodes in the list you need to use splice like this:

keys_.splice(keys_.begin(), keys_, iter->second);

Where iter is from the cache lookup.

------
quinnftw
Or if you want a non-obfuscated version

[https://gist.github.com/Quinny/09e34afb1d187e43dea2f3c3b0c04...](https://gist.github.com/Quinny/09e34afb1d187e43dea2f3c3b0c04b9f)

~~~
en4bz
Use splice to move items to the front of the list without copying in the find
method.

------
cjhanks
One of two (or both) things would need to be changed for it to be generally
useful:

\- The return value should be a WeakPtr, not a bool and a reference. This
allows for data to not be duplicated by all of the different `getter` threads.
A WeakPtr<Type> is kind of C++'s equivalent of a Maybe<Type>.

\- The getter function also need to be passed a functor capable of generating
the object, which then creates a `named lock` that /only/ other threads
requesting that key. This duplicates multiple logical copies of the same item
from being created (this is important when the creation is expensive,
presumably the core reason for using an LRU cache).

edit: formatting, I am not familiar with HN syntax.

------
psranga
This line bothered me more than it should :)

    
    
        typedef std::string String;
    

when

    
    
        using std::string;
    

would have done just as well.

Yes, I noticed the capitalization change. But why do you want to change the
name of a fundamental type?

------
tobz
Can someone explain how this is headers-only? C/C++ are not my forte, but my
limited interpretation of a "headers only" thing would be more along the lines
of: it's only type definitions and preprocessor macros.

It looks like there's plenty of actual method implementations being filled out
there, but they just happen to be in a .hpp file instead of a .cpp file.

~~~
socmag
It means all the code is defined in-line in the header file, inside the class
definition, instead of the more traditional method of using a header and an
associated cpp file.

A lot of the C++ standard library (STL) and Boost are header only (but not
all)

This means to use the code, all you need to do is #include the header file
wherever you need it, rather than including a header file and either building
a separate static/shared library for the cpp file, or including it in your own
build.

So that's really convenient.

Another thing that happens is that the by doing this, the compiler can
potentially do a lot better job of optimizing the code by inlining parts of
the library straight into your calling function.

With static libraries it is possible to get some optimizations like this made
at link time, since link time code generation is a requirement for templates,
but that's not possible with shared libraries since you are forced to call
through an exported function entry point by their very nature.

Header only libraries are typical for containers or light weight libraries
where the cost of inclusion and possible code duplication of code is worth the
performance gains, and certainly very popular where templates are involved.

tl;dr, header only is cool just because they are so easy to consume and don't
need a make file. Just #include and you are done.

~~~
tobz
Gotcha. Thanks for the detailed explanation.

------
aldanor
Benchmarks / comparison to using other container types would be nice to have

~~~
mohaps
yup! that's next up. :)

------
WesternStar
Looks like your insert function is missing an else . You're creating a new
node when each time you find an existing node in the map.

~~~
mohaps
thanks. fixed

------
speps
So it's C++11 but still uses naked pointers...

~~~
mohaps
the naked pointers are inside an internal kv namespace and are a
convenience/carry over from the previous implementation (I've covered the
justification for the way it's written in a comment above).

the TL;DR is that it is written the way it is to achieve constant time
remove/remove_and_add_to_end operations which is not an erase+append but more
like an unlink/relink operation.

