I am trying to do the following:
boost::unordered_map<boost::flyweight<std::string>, boost::flyweight<std::string> > map;
boost::flyweight<std::string> foo(name);
map[foo] = foo;
But the compiler complains:
"error C2665: 'boost::hash_value' : none of the 17 overloads could convert all the argument types".
But I have defined the following function:
std::size_t hash_value(const boost::flyweight<std::string> & b)
{
boost::hash<std::string> hasher;
const std::string & str = b.get();
return hasher(str);
}
bool operator==(const boost::flyweight<std::string>& f, const boost::flyweight<std::string> & second)
{
return f.get() == second.get();
}
But it doesn´t compile.
What do I need to do to make boost unordered_map to support flyweight?
[EDIT]
I got it to work with the following code:
struct flyweight_hash
{
std::size_t operator()(const boost::flyweight<std::string> &elm) const
{
boost::hash<std::string> hasher;
const std::string & str = elm.get();
return hasher(str);
}
};
and passed it as a template parameter to the construction of the map:
boost::unordered_map<boost::flyweight<std::string>, boost::flyweight<std::string> , flyweight_hash > map;
In this case I don´t understand way overloading hash_value didn´t worked.
boost::hash calls hash_value through argument dependent lookup (ADL). You are trying to define a hash_value function for a class in namespace boost. Hence your hash_value function would need to go into this namespace as well for ADL to work. Unfortunately, adding functions to a foreign namespace is rather evil and should be avoided. Your solution of using a custom hasher seems fine.
A little example code to illustrate:
namespace boost {
// somewhere in boost
template<typename T>
std::size_t hash(const T& t) {
// call using ADL
// e.g. if called with object of class type foo::bar this will
// pick up foo::hash_value despite the lack of namespace
// qualification
return hash_value(t);
}
}
// your hash_value (presumably in the global namespace)
// not picked up by above call
std::size_t hash_value(boost::flyweight<T>...);
namespace boost {
// this would be picked up but is slightly evil
std::size_t hash_value(boost::flyweight<T>...);
}
It is a pity to hash something which has already been hashed. Flyweight keeps a single instance of equal objects, so it is more efficient to hash the address of this instance, instead of its content. I do as follows (in std, not in boost, as I'm using C++11, so I'm extending std::hash, not boost::hash):
namespace std
{
template <typename T>
struct hash<boost::flyweight<T, boost::flyweights::no_tracking>>
{
using value_type = boost::flyweight<T, boost::flyweights::no_tracking>;
size_t operator()(const value_type& ss) const
{
hash<const void*> hasher;
return hasher(&ss.get());
}
};
}
I have been confirmed that this works by design, not by accident: http://lists.boost.org/boost-users/2013/03/78007.php
Related
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));
}
I'm trying to use the following custom unordered_map
using pair = std::pair<char, QColor>;
using cache = std::unordered_map<pair, QPixmap, boost::hash<pair>>;
cache _cache;
I defined the hash function for QColor as follows
template<>
struct std::hash<QColor>
{
std::size_t operator()(const QColor &color) const noexcept
{
return std::hash<QRgb>{}(color.rgb());
}
};
but no matter where I place it, whether it is a header or source file, I get a verbose compile time error from boost
C:\boost_1_77_0\boost\container_hash\extensions.hpp:305: error: C2665: 'boost::hash_value': none of the 3 overloads could convert all the argument types
C:\boost_1_77_0\boost/container_hash/hash.hpp(550): note: could be 'size_t boost::hash_value(const std::error_condition &)'
C:\boost_1_77_0\boost/container_hash/hash.hpp(543): note: or 'size_t boost::hash_value(const std::error_code &)'
C:\boost_1_77_0\boost/container_hash/hash.hpp(536): note: or 'size_t boost::hash_value(std::type_index)'
C:\boost_1_77_0\boost/container_hash/extensions.hpp(305): note: while trying to match the argument list '(const T)'
It is the last message of all. I think that boost's hashing function for pair doesn't see the hash function I defined. Do I need define it in the boost's namespace? And in general, what's the rule for defining the specific versions of templates? Why the rule that the templates must be defined in header files only doesn't apply here?
UPD: The structure of my project is as follows
// foo.h
#include <QtWidgets>
#include <boost/functional/hash.hpp>
#include <unordered_map>
template<>
struct std::hash<QColor>
{
std::size_t operator()(const QColor &color) const noexcept
{
return std::hash<QRgb>{}(color.rgb());
}
};
class Foo
{
private:
using Pair = std::pair<char, QColor>;
const QPixmap &getPixmapForPair(Pair c);
using CharsCache = std::unordered_map<Pair, QPixmap, boost::hash<Pair>>;
CharsCache _cache;
}
// foo.cpp
const QPixmap &Foo::getPixmapForPair(Pair c)
{
auto it = _cache.find(c);
if (it != _cache.end())
return it->second;
}
Very oversimplified, but delivers the general idea.
boost::hash<> probably uses boost::hash_combine which uses hash_value overloads and it doesn't have one for QColor which could be a problem so I suggest that you create a specialization for std::hash<Pair> by moving the alias out of the class definition and then use boost::hash_combine directly in your operator():
using Pair = std::pair<char, QColor>;
namespace std {
template<>
struct hash<Pair> {
std::size_t operator()(const Pair &p) const noexcept {
std::size_t seed = 0;
boost::hash_combine(seed, p.first);
boost::hash_combine(seed, p.second.rgb());
return seed;
}
};
} // namespace std
You could probably make it std::size_t seed = p.first; instead of initializing with 0 and calling hash_combine afterwards.
You can then use the default hasher (std::hash<Pair>) when creating your map:
class Foo {
private:
const QPixmap &getPixmapForPair(const Pair &c) const;
using CharsCache = std::unordered_map<Pair, QPixmap>;
CharsCache _cache;
};
Note that the function must return a value. I suggest that you throw an exception if it can't find a match:
const QPixmap& Foo::getPixmapForPair(const Pair &c) const {
auto it = _cache.find(c);
if (it != _cache.end()) return it->second;
throw std::runtime_error("getPixmapForPair"); // must return a value
}
Another option is to provide an overload for hash_value(const QColor&) instead of a specialization for std::hash<Pair>:
std::size_t hash_value(const QColor& c) {
return std::hash<QRgb>{}(c.rgb());
}
class Foo {
private:
using Pair = std::pair<char, QColor>;
const QPixmap& getPixmapForPair(const Pair& p) const;
using CharsCache = std::unordered_map<Pair, QPixmap, boost::hash<Pair>>;
CharsCache _cache;
};
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.
Is it possible to create a template in C++(11) for a function to check whether an object is contained in either a std::vector, std::array or std::list (and possibly even more container types)?
What I have by now:
typedef std::shared_ptr<Tag> SharedTag;
typedef std::vector<SharedTag> TagList;
bool
Tag::isIn(const TagList& lst) {
return std::any_of(lst.begin(), lst.end(), [this](const SharedTag& t) {
return t->name == this->name;
});
}
Tag is a normal class. The comparison, of course, should be done t == this, which will be an operator== later on. I did not include this here for simplicity.
So, is it possible to write the upper code only once (without the typedef's though,) for std::vector, std::array, std::list(, maybe for std::set) and so on?
I couldn't find a base-type of all these classes,... which would be my first idea...
Option 1 (good): just use std::find directly:
std::vector<int> v; // populate v however you want
std::vector<int>::const_iterator i = std::find(v.cbegin(), v.cend(), 42);
if (i != v.end()) {
// Now you know 42 is in v
} else {
// Now you know 42 is not in v
}
Option 2 (better): wrap std::find in a helper function:
template <typename Container, typename Value>
bool contains(const Container& c, const Value& v)
{
return std::find(std::begin(c), std::end(c), v) != std::begin(c);
}
// Example usage:
std::vector<int> v; // populate v however you want
if (contains(v, 42)) {
// You now know v contains 42
}
Option 3 (best): use the find method of containers that provide one (which is faster for sorted containers, like set), and std::find for containers that don't provide one:
// If you want to know why I added the int and long parameter,
// see this answer here: http://stackoverflow.com/a/9154394/1287251
template <typename Container, typename Value>
inline auto contains(const Container& c, const Value& v, int) -> decltype(c.find(v), bool()) {
return c.find(v) != std::end(c);
}
template <typename Container, typename Value>
inline bool contains(const Container& c, const Value& v, long) {
return std::find(std::begin(c), std::end(c), v) != std::end(c);
}
template <typename Container, typename Value>
bool contains(const Container& c, const Value& v) {
return contains(c, v, 0);
}
// Example usage:
std::set<int> s; // populate s however you want
if (contains(s, 42)) {
// You now know s contains 42
}
Of course, you could write std::find yourself, but you might as well use it.
You may use template:
typedef std::shared_ptr<Tag> SharedTag;
template <typename Container>
bool Tag::isIn(const Container& lst) {
return std::any_of(lst.begin(), lst.end(), [this](const SharedTag& t) {
return t->name == this->name;
});
}
That requires that Container is a container of something convertible to SharedTag.
There is no common base-type between those containers. That's just not the way the STL library works, it is based on templates and generic programming principles.
So, if you want to implement the function once for all containers, you would have to make it a template. Here is a basic form:
template <typename TagContainer>
bool Tag::isIn(const TagContainer& lst) {
return std::any_of(lst.begin(), lst.end(), [this](const SharedTag& t) {
return t->name == this->name;
});
};
But this has the problem that you could technically pass anything to this function that isn't actually a container of SharedTag, so, to solve this issue, you could use a trick called Sfinae to enforce that rule:
template <typename TagContainer>
typename std::enable_if< std::is_same< SharedTag, typename TagContainer::value_type >::value,
bool >::type Tag::isIn(const TagContainer& lst) {
return std::any_of(lst.begin(), lst.end(), [this](const SharedTag& t) {
return t->name == this->name;
});
};
Which kind of ugly, but it works.
There is still one problem though. I suspect that your Tag class is a normal non-template class, which means that you are probably implementing it in a cpp file, but templates need to be implemented in the header file (because function templates need to have their implementation visible to the compiler to generate a new concrete version of it for each type that you call it with).
One way to avoid this problem is to provide a number of overloaded non-template functions for each container you want to support, and then, under-the-hood, you call a local function template, and in this case, you don't need the sfinae trick to constrain it, since it is already limited to the set of overloads that you provided. Something like this:
template <typename TagContainer>
bool Tag::isIn_impl(const TagContainer& lst) {
return std::any_of(lst.begin(), lst.end(), [this](const SharedTag& t) {
return t->name == this->name;
});
};
bool Tag::isIn(const std::list<SharedTag>& lst) {
return isIn_impl(lst);
};
bool Tag::isIn(const std::vector<SharedTag>& lst) {
return isIn_impl(lst);
};
bool Tag::isIn(const std::set<SharedTag>& lst) {
return isIn_impl(lst);
};
Note that the isIn_impl is a member function template that should be declared in the header file, in the private section of the class, and can safely be defined in the cpp file, because that cpp file is the only place where that function template is called from.
The obvious issue with that solution is that you have to manually provide every overload that you want to support, which means that it isn't very "scalable" in the future, but in real-life, there probably aren't that many containers that you'd want to support. If you want the full generality, you really have to use the template approach (unless you want to do type-erasure on the container... but that's a bit beyond the scope of what I'm willing to explain here).
You can use a nested variadic template to achieve this. Here is a handy demo: note the magic part, template <template <typename...> class V, typename E>. A variadic template is necessary because vector, list &co. all have a different number of template parameters (allocator, comparator etc.) for which a default value is supplied by the STL.
#include <vector>
#include <string>
#include <memory>
#include <algorithm>
#include <list>
#include <set>
#include <iostream>
class Tag {
public:
Tag(const std::string &n): name(n) {}
template <template <typename...> class V, typename E>
bool isIn(const V<E> &lst) {
return std::any_of(lst.begin(), lst.end(), [this](const E &t) {
return t.name == this->name;
});
}
private:
std::string name;
};
typedef std::shared_ptr<Tag> SharedTag;
typedef std::vector<SharedTag> TagList;
int main() {
Tag t("foo");
// Set needs some extra bits to work (a `<` operator etc.)
//std::set<Tag> a = {Tag("foo"), Tag("bar")};
std::vector<Tag> b = {Tag("foo"), Tag("bar")};
std::list<Tag> c = {Tag("foo"), Tag("bar")};
//std::cout << t.isIn(a) << std::endl;
std::cout << t.isIn(b) << std::endl;
std::cout << t.isIn(c) << std::endl;
}
How do i declare a hash function for my custom type so that i could use it in an unordered_map?
namespace std {
template<>
struct hash<my_custom_type>
{
using argument_type = my_custom_type;
using result_type = size_t;
size_t operator()(my_custom_type const& x) const
{
// Perform your hash algorithm here.
}
};
}
Probably the best way is to provide a specialization of std::hash< class Key > with std::hash< mytype >.