typeid() Object-Oriented Design Alternative - c++

I have the following class using 3 different maps: keys are always strings, while values may be strings, integers or floats.
class MyMaps
{
public:
template<typename T> void addKey(const std::string& key);
void addValue(const std::string& key, const std::string& value);
void addValue(const std::string& key, int value);
void addValue(const std::string& key, float value);
private:
std::map<std::string, std::string> stringFields;
std::map<std::string, int> intFields;
std::map<std::string, float> floatFields;
};
The addValue() functions simply add a new pair to the related map. What I'm working on is the addKey() template function:
/** Add only a key, the related value is a default one and is specified by template parameter T. */
template<typename T>
void MyMaps::addKey(const string& key)
{
if (typeid(T) == typeid(string))
{
stringFields.insert(pair<string, string>(key, string()));
}
else if (typeid(T) == typeid(int))
{
intFields.insert(pair<string, int>(key, int()));;
}
else if (typeid(T) == typeid(float))
{
floatFields.insert(pair<string, float>(key, float()));
}
}
Basically, I'm using template and typeid() because I don't like this alternative that relies on type-within-function-name:
void MyMaps::addStringKey(const string& key)
{
stringFields.insert(pair<string, string>(key, string()));
}
void MyMaps::addIntKey(const string& key)
{
intFields.insert(pair<string, int>(key, int()));
}
void MyMaps::addFloatKey(const string& key)
{
floatFields.insert(pair<string, float>(key, float()));
}
The first addKey() version seems working, but I'm wondering if there is a more elegant solution. Maybe I'm missing some Object-Oriented design concept that could be helpful in this case?
Thanks in advance.

This is a perfect fit for template specialization:
template<>
void MyMaps::addKey<string>(const string& key)
{
stringFields.insert(pair<string, string>(key, string()));
}
template<>
void MyMaps::addKey<int>(const int& key)
{
intFields.insert(pair<string, int>(key, int()));;
}
template<>
void MyMaps::addKey<float>(const float& key)
{
floatFields.insert(pair<string, float>(key, float()));
}
EDIT: For syntax/more info about template specialization read: Template Specialization and Partial Template Specialization
Or better yet, if boost is an option and if the keys are unique for all 3 maps and you have 3 different maps just to be able to store them, then consider using boost::variant:
typedef boost::variant<string, int, float> ValueType;
class MyMap
{
public:
typedef std::map<std::string, ValueType> MapType;
template<typename T> void addKey(const std::string& key, T &val)
{
ValueType varVal= val;
allFields.insert(MapType::value_type(key, varVal));
}
private:
MapType allFields;
};

Your question inquires 2 things:
The real question, crafting a key value Map or Dictionary, using different types, for the values, in the same collection.
And, a potential solution, applying the "typeid" function.
More reference about "typeid":
http://en.cppreference.com/w/cpp/language/typeid
Object (and Class) Orientation is great, but, sometimes you may want to mix it with other paradigms.
What about "pointers" ?
Pointers allow different types, to be treated as the same simple type.
What about a Key Value Dictionary Collection, that stores a string key, and a pointer value, and the pointer may be integer, string, or object.
Or to be more specific. the "Value" may be a tuple, where the first field (maybe an enumerated type), indicates the real type of the value. And, the second field of the "Value" is a pointer or variant to the real field.
The first suggestion using "Unions" (a.k.a. "variants"), no pointers:
#include <string>
#include <typeinfo>
union ValueUnion
{
int AsInt,
float AsFloat,
std::string& AsStr
};
struct ValueType
{
std::type_info Id,
ValueUnion Value
};
class MyMaps
{
public:
template<typename T> void addKey(const std::string& key);
void addValue(const std::string& key, const std::string& value);
void addValue(const std::string& key, int value);
void addValue(const std::string& key, float value);
private:
std::map<std::string, ValueType> Fields;
};
Or, with pointers:
#include <string>
#include <typeinfo>
struct ValueType
{
std::type_info Id,
void* Value
};
class MyMaps
{
public:
template<typename T> void addKey(const std::string& key);
void addValue(const std::string& key, const std::string& value);
void addValue(const std::string& key, int value);
void addValue(const std::string& key, float value);
private:
std::map<std::string, ValueType> Fields;
};
I have seen this "pattern" several times, I called the "Key-Value-Type" collection.
Note: Not many experience with the STL, are you sure "std::map",
is the right collection ?
Just my 2 cents.

Might not be what you want because it's a different approach but you could use a map of variants. you can define a boost::variant to hold only string,int or float.
eladidan beat me to it and I don't know how to delete answers.

Related

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
}

c++ overwrite [] operator with std::string [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I'm basically trying to create a thread-safe wrapper class for std::map.
As I'm coming from C, I have quite a hard time figuring out all the nuances of C++.
I'm trying to overwrite the [] operator to take std::string arguments to just pass it to my std::map member.
By the reference of std::map::operator[] this should work fine:
T& operator[]( const Key& key );
Here is my class:
thread_map.hpp
#ifndef THREAD_MAP_H
#define THREAD_MAP_H
#include <map>
#include <functional>
#include <mutex>
template <class T>class Thread_map
{
private:
std::map<std::string, T> map;
std::mutex map_mutex;
public:
~Thread_map();
T& at(size_t pos);
T& operator[](std::string &key);
size_t size() const;
bool empty() const;
void clear();
void insert(std::pair<std::string, T> pair);
T& erase(const std::string &key);
bool for_each(std::function<bool (Thread_map, std::string&, T&)> fun);
};
template<class T> Thread_map<T>::~Thread_map()
{
this->map.clear();
}
template<class T> T& Thread_map<T>::at(size_t pos)
{
T *value;
this->map_mutex.lock();
value = this->map.at(pos);
this->map_mutex.unlock();
return value;
}
template<class T> T& Thread_map<T>::operator[](std::string &key)
{
this->map_mutex.lock();
T &value = this->map[key];
this->map_mutex.unlock();
return value;
}
template<class T> size_t Thread_map<T>::size() const
{
size_t size;
this->map_mutex.lock();
size = this->map.size();
this->map_mutex.unlock();
return size;
}
template<class T> bool Thread_map<T>::empty() const
{
bool empty;
this->map_mutex.lock();
empty = this->map.empty();
this->map_mutex.unlock();
return empty;
}
template<class T> void Thread_map<T>::clear()
{
this->map_mutex.lock();
this->map.clear();
this->map_mutex.unlock();
}
template<class T> void Thread_map<T>::insert(std::pair<std::string, T> pair)
{
this->map_mutex.lock();
this->map.insert(pair);
this->map_mutex.unlock();
}
template<class T> T& Thread_map<T>::erase(const std::string &key)
{
T *value;
this->map_mutex.lock();
value = this->map.erase(key);
this->map_mutex.unlock();
return value;
}
template<class T> bool Thread_map<T>::for_each(std::function<bool
(Thread_map, std::string&, T&)> fun)
{
}
#endif
I put the implementation into the header file, because I heard you do that with template classes. Am I right on that?
My Problem is, that when I try to call the operator
Thread_map<std::string> map;
map["mkey"] = "value";
g++ throws an invalid initialization error on map["mkey"].
As far as my understanding goes the problem is that mkey gets compiled to std::string("mkey") which is just the value, not a reference.
But why or how does the following work then?
std::map<std::string, std::string> map;
map["mkey"] = "value";
I mean I could just pass the string by value but that seems inefficient.
Reference requires a variable's address that cans be modified. For lvalue string ("some string") there is no address that can be value modified.
There are some ways that I know to solve this issue:
Remove Reference (Not recommended)
One way is to remove the '&' from the parameter. like this:
T& operator[](std::string key);
In this way you don't request lvalue, but rvalue. The problem is that when ever you'll send a value, you won't send 4 bytes of memory address, but sizeof("Your string") bytes. Heavy method..
Make a const lvalue (Recommended way)
The most beautiful way to solve this issue, is to make the parameter const lvalue (what that called rvalue reference), to make a promise to the compiler that you won't try to change the given address's value inside this function. It's looks like this:
T& operator[](const std::string &key);
Now you can send a lvalue string and rvalue string.
Give up
This way is not bad as the first one, but absolutely not good as the second. You can easily use your declaration:
T& operator[](std::string &key);
And when you pass a value, use another string variable to store the value, and use this variable at call time:
Thread_map<std::string> map;
string key = "mkey";
map[key] = "value";
(Don't do that.. Just as extension for the knowledge).

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.

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!

Switch template type

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.