Switch template type - c++

I want to make some storage for my game. Now the code looks like:
class WorldSettings
{
private:
std::map<std::string, int> mIntegerStorage;
std::map<std::string, float> mFloatStorage;
std::map<std::string, std::string> mStringStorage;
public:
template <typename T>
T Get(const std::string &key) const
{
// [?]
}
};
So, I have a few associative containers which stores the exact type of data. Now I want to add into settings some value: settings.Push<int>("WorldSize", 1000); and get it: settings.Get<int>("WorldSize");. But how to switch need map due to passed type into template?
Or, maybe, you know a better way, thanks.

If your compiler supports this1, you can use template function specialisations:
class WorldSettings
{
private:
std::map<std::string, int> mIntegerStorage;
std::map<std::string, float> mFloatStorage;
std::map<std::string, std::string> mStringStorage;
public:
template <typename T>
T Get(const std::string &key); // purposely left undefined
};
...
template<>
int WorldSettings::Get<int>(const std::string& key) {
return mIntegerStorage[key];
}
template<>
float WorldSettings::Get<float>(const std::string& key) {
return mFloatStorage[key];
}
// etc
Notice that the methods are not const because map<>::operator[] is not const.
Also, if someone tries to use the template with a type other than one you have provided a specialisation for, they will get linker errors, so your code won't misbehave or anything. Which is optimal.
1 If not, see #gwiazdorrr's answer

First of all, since prior to C++11 you can't specialise functions, your member functions must differ in signature - return type does not count. From my experience on some compilers you can do without it, but as usual - you should keep your code as close to standard as possible.
That said you can add a dummy paramater that won't affect performance and the way you call function:
public:
template <typename T>
T Get(const std::string &key) const
{
return GetInner(key, (T*)0);
}
private:
int GetInner(const std::string& key, int*) const
{
// return something from mIntegerStorage
}
float GetInner(const std::string& key, float*) const
{
// return something from mFloatStorage
}
And so on. You get the idea.

Seth's answer is ideal, but if you don't have access to a C++11 compiler, then you can use template class specialization and do this instead. It's much more verbose, but keeps the same functionality.
class WorldSettings
{
template<class T>
struct Selector;
template<class T>
friend struct Selector;
private:
std::map<std::string, int> mIntegerStorage;
std::map<std::string, float> mFloatStorage;
std::map<std::string, std::string> mStringStorage;
public:
template <typename T>
T Get(const std::string &key)
{
return Selector<T>::Get(*this)[key];
}
};
template<>
struct WorldSettings::Selector<int>
{
static std::map<std::string, int> & Get(WorldSettings &settings)
{
return settings.mIntegerStorage;
}
};
template<>
struct WorldSettings::Selector<float>
{
static std::map<std::string, float> & Get(WorldSettings &settings)
{
return settings.mFloatStorage;
}
};
// etc.

In C++03 I would recommend the use of ‘boost::any‘ in the type of the container, and the. You need a single accessor:
std::map<std::string,boost::any> storage;
template <typename T> getValue( std::string const & key ) {
return boost::any_cast<T>( storage[key] );
}
This is a rough sketch, as a member function It would be const, and it should use ‘map::find‘ not to modify the container when searching, it should deal with invalid joeys, and probably remap the boost exceptions into your own application exceptions.

Related

using a map with a comparator as a std::map parameter

Say I define a map with a custom comparator such as
struct Obj
{
int id;
std::string data;
std::vector<std::string> moreData;
};
struct Comparator
{
using is_transparent = std::true_type;
bool operator()(Obj const& obj1, Obj const& obj2) { return obj1.id < obj2.id; };
}
std::map<Obj,int,Comparator> compMap;
is there a good way to ensure that downstream users don't have to implement the comparator to use the map as a map?
for instance my compiler throws an error if I try to pass it to a function with a similar type.
template<class T>
inline void add(std::map<T, int>& theMap, T const & keyObj)
{
auto IT = theMap.find(keyObj);
if (IT != theMap.end())
IT->second++;
else
theMap[keyObj] = 1;
}
add(compMap,newObj); //type error here
EDIT:
I kinda over santitized this to make a generic case. and then overlooked the obvious
template<class T, class Comp, class Alloc>
inline void add(std::map<T, int, Comp, Alloc>& theMap, T const & keyObj)
still having issues with one use not being able to deduce T, but went from 80 erros to 1 so... progress
thanks everyone.
You can typedef the specialised type and use that type inplace of
std::map<...
typedef std::map<Obj,int,Comparator> compMap_t;
inline void add(compMap_t& theMap, Obj const & keyObj)
...
Downstream users either use the type declared by you
using my_important_map = std::map<Obj,int,Comparator>;
or better use functions which take a generic map type,
auto some_function(auto const& map_)
{
//do something with the map and don't care about the ordering
return map_.find(Obj(1));
}

How to pass reference type to std::hash

I am creating a Template Cache library in C++-11 where I want to hash the keys. I want to use default std::hash for primitive/pre-defined types like int, std::string, etc. and user-defined hash functions for user-defined types. My code currently looks like this:
template<typename Key, typename Value>
class Cache
{
typedef std::function<size_t(const Key &)> HASHFUNCTION;
private:
std::list< Node<Key, Value>* > m_keys;
std::unordered_map<size_t, typename std::list< Node<Key, Value>* >::iterator> m_cache;
size_t m_Capacity;
HASHFUNCTION t_hash;
size_t getHash(const Key& key) {
if(t_hash == nullptr) {
return std::hash<Key>(key); //Error line
}
else
return t_hash(key);
}
public:
Cache(size_t size) : m_Capacity(size) {
t_hash = nullptr;
}
Cache(size_t size, HASHFUNCTION hash) : m_Capacity(size), t_hash(hash) {} void insert(const Key& key, const Value& value) {
size_t hash = getHash(key);
...
}
bool get(const Key& key, Value& val) {
size_t hash = getHash(key);
...
}
};
My main function looks like this:
int main() {
Cache<int, int> cache(3);
cache.insert(1, 0);
cache.insert(2, 0);
int res;
cache.get(2, &res);
}
On compiling the code above, I get the below error:
error: no matching function for call to ‘std::hash<int>::hash(const int&)’
return std::hash<Key>(key);
Can anyone please help me out here and point out what am I missing or doing it incorrectly?
In this call you provide the constructor of std::hash<Key> with key:
return std::hash<Key>(key);
You want to use it's member function, size_t operator()(const Key&) const;:
return std::hash<Key>{}(key);
Some notes: Hashing and caching are used to provide fast lookup and having a std::function object for this may slow it down a bit. It comes with some overhead:
typedef std::function<size_t(const Key &)> HASHFUNCTION;
The same goes for the check in getHash() that is done every time you use it:
if(t_hash == nullptr) {
It would probably be better to add a template parameter for what type of hasher that is needed. For types with std::hash specializations, that could be the default. Otherwise size_t(*)(const Key&) could be the default. A user could still override the default and demand that the hash function is a std::function<size_t(const Key &)> if that's really wanted.
If a hash function is to be used, I recommend requiring the user to supply it when a Cache is constructed. That way you can skip the if(t_hash == nullptr) check every time the hash function is used.
First a small type trait to check if std::hash<Key> exists:
#include <functional>
#include <type_traits>
#include <utility>
template <class T>
struct has_std_hash {
static std::false_type test(...); // all types for which the below test fails
template <class U>
static auto test(U u) -> decltype(std::declval<std::hash<U>>()(u),
std::true_type{});
static constexpr bool value = decltype(test(std::declval<T>()))::value;
};
The added template parameter in Cache could then be conditionally defaulted to either std::hash<Key> or size_t(*)(const Key&) and you could disallow constructing a Cache with only a size when a pointer to a hash function is needed:
template <
typename Key, typename Value,
class HASHFUNCTION = typename std::conditional<
has_std_hash<Key>::value, std::hash<Key>, size_t(*)(const Key&)>::type>
class Cache {
public:
// a bool that tells if HASHFUNCTION is a pointer type
static constexpr bool hash_fptr = std::is_pointer<HASHFUNCTION>::value;
Cache(size_t size) : m_Capacity(size) {
static_assert(!hash_fptr, "must supply hash function pointer");
}
Cache(size_t size, HASHFUNCTION hash) : m_Capacity(size), t_hash(hash) {}
private:
// No `if` is needed in getHash() - in fact, this function isn't needed.
// You could just do `t_hash(key)` where you need it.
size_t getHash(const Key& key) const { return t_hash(key); }
HASHFUNCTION t_hash;
};
The usage would be the same as before, except you can't construct a Cache without a hasher:
struct Foo {};
size_t hashit(const Foo&) { return 0; }
int main() {
Cache<int, int> cache(3); // ok, HASHFUNCTION is std::hash<int>
Cache<Foo, int> c2(3, hashit); // ok, HASHFUNCTION is size_t(*)(const Foo&)
// Cache<Foo, int> c3(3); // error: must supply hash function pointer
}

How to add use a pointer to member value as a template parameter by type (not value)

My situation:
I frequently need to have a vector of structures where one field can be thought of as a Key or ID, and rather than store it expensively in a map (memory usage is very important in this app) I want to store it in a flat vector but present a map-like interface for finding elements by key.
My first solution to this problem:
template <class T, class Key, class KeyFn>
class TKeyedVector : public std::vector<T>
{
public:
const_iterator find(const Key& key) const {return std::find_if(begin(), end(), [&](const T& entry) {return keyFn(entry)==key; }); }
KeyFn keyFn;
};
struct KeyedDataEntry
{
std::string key;
int value;
struct KeyExtractor {
const std::string& operator()(const KeyedDataEntry& e) const {return e.key; };
};
};
using KeyedDataArray = TKeyedVector<KeyedDataEntry, std::string, KeyedDataEntry::KeyExtractor>;
Now this all works, but I would like to be able to remove the need for the KeyExtractor type by using the pointer to the member variable embedded into the type:
template <class T, class Key, Key T::* keyFn>
class TKeyedVector : public std::vector<T>
{
public:
const_iterator find(const Key& key) const {return std::find_if(begin(), end(), [&](const T& entry) {return keyFn(entry)==key; }); }
};
using KeyedDataArray = TKeyedVector<KeyedDataEntry, std::string, &KeyedDataEntry::key>;
However I can't get this to work. I've been looking at the implementation of std::mem_fn for clues, but I can't work out how to do it. The error I get with is something like:
warning C4353: nonstandard extension used: constant 0 as function expression. Use '__noop' function intrinsic instead
Any clues?
EDIT: sample version at http://ideone.com/Qu6TEy
Here is the start of a working solution. You don't need a special extractor object.
Note that I have encapsulated the vector. In time, you'll regret not doing this.
#include <vector>
#include <string>
template <class T, class Key, const Key& (T::*Extractor)() const>
class TKeyedVector
{
using storage = std::vector<T>;
using const_iterator = typename storage::const_iterator;
public:
decltype(auto) begin() const
{
return storage_.begin();
}
decltype(auto) end() const
{
return storage_.end();
}
const_iterator find(const Key& key) const
{
return std::find_if(begin(),
end(),
[&](const T& entry)
{
return entry.*Extractor() == key;
});
}
storage storage_;
};
struct KeyedDataEntry
{
std::string key;
int value;
const std::string& get_key() const { return key; }
};
int main()
{
TKeyedVector<KeyedDataEntry, std::string, &KeyedDataEntry::get_key> mymap;
}
But there is a problem with this idea of yours.
In order for this structure to be a map, the keys must be immutable. This argues for only returning immutable objects. This then argues immediately for simply using an unordered_set or set.
If you're going to return references to mutable objects in the underlying vector, then you may as well simply use std::find_if with a predicate to find them.
A pointer to member requires the pointer to member call syntax. (entry.*keyFn)()
C++17 will come with a standard std::invoke function to make writing such templates a bit less tiresome (it will work for all callable objects). But in the meanwhile, this is how you need to do this.

Multidimensional map template accessed and assigned inside of class method

I was wondering if this was a valid way to assign a multidimensional map inside of a class method and if not how would I go about doing this.
template<typename T>
std::map<std::string, std::map<std::string, T>> MT;
template<typename T>
void MonsterTemplate(std::string name, std::string node, template T v) {
MT[name][node] = v;
}
Edit1: I compiled and it gave me many errors but I will just give a portion of the 1st.
error C3376: 'MonsterType::MT': only static data member templates are allowed
Edit2:
I tried creating a struct
template<typename T>
struct Wrapper
{
typedef std::map<std::string, std::map<std::string, T>> MT;
};
I then added this inside the class
template<typename T>
Wrapper<T>::MT mt;
template<typename T>
void MonsterTemplate(std::string name, std::string node, template T v) {
mt[name][node] = v;
}
Then got this error amongst many others.
warning C4346: 'MT': dependent name is not a type
The struct works outside the class without being multidimensional map, but I am unsure how to access it as multidimensional map.. just trying different things.
The idea is I want to store data of several objects data and index them by name, node and value.
Edit3:
So this is what I went with, and haven't gotten an error (just yet :p)
std::map<std::string, std::map<std::string, int>> MT;
void MonsterTemplate(std::string name, std::string node, int v) {
MT[name][node] = v;
}
Just updating this for anyone looking for something similar
So I figured out how to construct this.
template<typename T>
struct Test
{
std::map<std::string, std::map<std::string, std::map<size_t, std::map<std::string, T>>>> testmap;
void MonsterTemplate(std::string creatureName, std::string name, std::string node, T v) {
size_t i = testmap[creatureName][name].size();
testmap[creatureName][name][i][node] = v;
}
};
Test<std::string> str;
Since I needed various data types for T the structure worked much better than trying to find a way to assign a template to a static class, I am only using std::string as an example.
This can then be used inside of the class's method since str has a global scope.
str.MonsterTemplate(creatureName, name, node, value);

Can I use templates to store values of different types in a map?

I have a class that represents some application event. I want to be able to set (and later retrieve) various attributes to the event. These are identified by a unique std::string label.
I was able to write the code below, which works, but, as I'm not very experienced with templates, I can't shake the feeling that there should be a better way to do this with (more) templates, and get rid of that hideous void *. Ideally, this solution will also do the attribute type checking at compile-time - but I'm not sure if that's possible.
Do you have a better way to do this?
My code (ignoring the memory leak for now):
class Event final
{
public:
Event(EventType type) : type_(type) {}
template <typename T>
void addAttribute(std::string const &name, T value);
template <typename T>
void getAttribute(std::string const &name, T &value) const;
private:
EventType type_;
struct Attribute
{
std::type_index type;
void *ptr;
};
std::unordered_map<std::string, Attribute> attribs_;
};
template <typename T>
inline void Event::addAttribute(std::string const &name, T value)
{
Attribute atr = { typeid(T), new T(value) };
auto res = attribs_.insert({ name, atr });
if (std::get<1>(res) == false)
throw std::runtime_error("Event::addAttribute: unordered_map insertion failed.");
}
template <typename T>
inline void Event::getAttribute(std::string const &name, T &value) const
{
Attribute atr = attribs_.at(name);
if (atr.type != typeid(T))
throw std::runtime_error("Event::getAttribute: wrong attribute type requested.");
value = *(static_cast<T *>(atr.ptr));
}
You can (should) replace your "Attribute" class by a type safe, variant template, such as Boost.Any or Boost.variant.
Your map would be (for boost::any )
std::unordered_map<std::string, boost::any> attribs_;
And yes, you would get rid of the void*, as any C++ code should!