Xsy
Xsy

Reputation: 193

C++ 11/14 doing a different operation in a single function based on template type

So i've got a bunch of shared_ptr's as private members of a class called 'Handler' each pointing to a different template Cache classes. Like so:

shared_ptr< Cache<T1> > t1Cache;
shared_ptr< Cache<T2> > t2Cache;
shared_ptr< Cache<T3> > t3Cache;

Each Cache class simply holds a map with a string as a key and T as the item and functions to get/update and add an item to the map. I want to have a single member function in 'Handler' for an update,add and get operations. So I could call for example: ( Where ITEM_TYPE is an enum )

Handler handler;
auto something = handler.getItem( key1, TYPE_T1 );
handler.addItem( key2, TYPE_T2, newItem );
handler.updateItem( key3, TYPE_T3, newValue);

I tried at first just having a switch based on the enum like so:

template <typename T>
auto addItem( const std::string& key, ITEM_TYPE type, T nItem )
{
    switch( type )
    {
    case TYPE_T1:
       t1Cache->addItem( key, nItem );
    break;
    case TYPE_T2:
       t2Cache->addItem( key ,nItem );
    break;
    }
}

Unfortunately I have a basic knowledge of templates so I didn't realize that that will not work. At the moment as a temporary solution im just using a different function for each type, though i'd really like to consolidate it all into one. I thought that maybe 'constexpr if' will help but it's not supported, is this even possible?

EDIT: Sorry the title isn't really worded right, was not sure how to title this

Upvotes: 1

Views: 132

Answers (2)

Peter Ruderman
Peter Ruderman

Reputation: 12485

What about something like this?

#include <string>
#include <tuple>

template <class T> class Cache
{
public:
  void addItem(std::string const& key, T item)
  {
  }
};

template <class... T> class SuperCache
{
public:
  template <class U> void addItem(std::string const& key, U&& item)
  {
    std::get<Cache<std::decay_t<U>>>(caches_).addItem(key, std::forward<U>(item));
  }

private:
  std::tuple<Cache<T>...> caches_;
};

int main()
{
  SuperCache<int, double> cache;
  cache.addItem("a", 5);
  cache.addItem("b", 5.0);
}

If you can use C++17, then here's another approach that might work (depending on your needs):

#include <string>
#include <unordered_map>
#include <variant>

template <class... T> class VariantCache
{
public:
  template <class U> void addItem(std::string const& key, U&& item)
  {
    items_[key] = std::forward<U>(item);
  }

 private:
   std::unordered_map<std::string, std::variant<T...>> items_;
};

int main()
{
  VariantCache<int, double> cache;
  cache.addItem("a", 5);
  cache.addItem("b", 5.0);
}

Upvotes: 2

Pixelchemist
Pixelchemist

Reputation: 24946

I don't know why you would want to do this but you could encapsulate all caches into a tuple and use get<T> within your function to select the proper tuple element:

#include <memory>
#include <tuple>
#include <vector>
#include <iostream>

template<class T> struct Cache 
{ 
    std::vector<T> x; 
    void add(T const& y) { x.push_back(y); }
};

template<class T1, class T2, class T3>
struct test
{
    test() : _caches(std::make_tuple(std::make_shared<Cache<T1>>(), 
        std::make_shared<Cache<T2>>(), std::make_shared<Cache<T3>>())) {}
    std::tuple<std::shared_ptr<Cache<T1>>, std::shared_ptr<Cache<T2>>, 
        std::shared_ptr<Cache<T3>>> _caches;
    template<class T>
    void add_item(T const& v)
    {
        std::get<std::shared_ptr<Cache<T>>>(_caches)->add(v);
    }
};

int main()
{
    test<int, float, double> a;
    a.add_item(1);
    std::cout << std::get<std::shared_ptr<Cache<int>>>(a._caches)->x.size() << "\n";
    return 0;
}

Note that get<T> does not compile if the tuple contains more than one item of type T so this solution requires T1, T2 and T3 to be distinct.

Upvotes: 1

Related Questions