rewrote for C++14, much cleaner, no more type erasure