Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ type based caching without using static storage

Tags:

c++

c++11

c++14

I'm using something like:

struct VectorCache
{
    template<typename T>
    std::vector<T>& GetTs()
    {
        static std::vector<T> ts;
        return ts;
    }
};

to create/access some vectors based on the contained type. This works fine as long as I have only one object of type VectorCache, but when I use multiple objects I will get same vectors from all instances of VectorCache as the vectors are static variables.

I tried to move the vectors as member variables using something similar to boost::any and access them using std::type_index of T, but this is somehow slower than the direct access I used before.

Another options is to transform struct VectorCache to something like template<int index> struct VectorCache, but the problem is still there - I will have to be careful to have only one instance/index to have correct behaviour.

Is it possible to access the vectors directly based on T and also have the caching instance based instead of class based?

like image 311
Mircea Ispas Avatar asked Oct 14 '14 11:10

Mircea Ispas


2 Answers

You could try an unchecked analogue of Boost.Any. See if that's fast enough for you (though I don't believe it would make a big difference):

#include <memory>
#include <type_traits>
#include <typeindex>
#include <unordered_map>
#include <vector>

class AnyCache
{
    struct TEBase
    {
        virtual ~TEBase() {}
        virtual void * get() = 0;
    };

    template <typename T> struct TEObject : TEBase
    {
        T obj;
        virtual void * get() override { return static_cast<void *>(&obj); }
    };

    std::unordered_map<std::type_index, std::unique_ptr<TEBase>> cache;

public:
    AnyCache(AnyCache const &) = delete;
    AnyCache & Operator=(AnyCache const &) = delete;

    template <typename T> decltype(auto) get()
    {
        using U = std::decay_t<T>;
        using C = std::vector<U>;

        std::unique_ptr<TEBase> & p = cache[typeid(U)];
        if (!p) { p = std::make_unique<TEObject<C>>(); }
        return *static_cast<C *>(p->get());
    }
};

Usage:

AnyCache ac;
ac.get<int>().push_back(20);
ac.get<std::string>().push_back("Hello");
for (auto const & x : ac.get<Foo>()) { std::cout << x << '\n'; }
like image 95
Kerrek SB Avatar answered Oct 20 '22 01:10

Kerrek SB


If - and it's a big if - your VectorCache-using code isn't threaded, you can do this:

struct VectorCache
{
    VectorCache() : instance_counter_(++s_instance_counter_) { }

    template<typename T>
    std::vector<T>& GetTs()
    {
        static std::vector<std::vector<T>> tss;
        if (tss.size() <= instance_counter_)
            tss.resize(instance_counter_);
        return tss[instance_counter_];
    }

    size_t instance_counter_;
    static size_t s_instance_counter_;
};

// and define size_t VectorCache::s_instance_counter_;

implementation on ideone.com

With a little synchronisation you can make it thread safe, or even thread specific if that suits. Add deletion of copy construction / assignment etc. if that makes sense in your intended usage.

like image 2
Tony Delroy Avatar answered Oct 20 '22 02:10

Tony Delroy