I am using std::unordered_map with a custom equality comparator class like so:
class KeyCompare {
private:
HelperClass* helper;
public:
KeyCompare(HelperClass* helper): helper(helper) {}
bool operator()(const Key& key1, const Key& key2) const {
return helper->doStuff(key1, key2);
}
}
At some point in my code I initialize my map like this:
HelperClass helper;
std::unordered_map<Key, Value, Hasher, KeyCompare> map;
I would like to pass helper to the map such that KeyCompare objects are created with this helper. Is such a thing possible? I can use some global variable if absolutely necessary, but I would really like to avoid that.
Since your KeyCompare needs a helper it isn't default constructible. You must therefore supply an instance to the unordered_map when you construct it.
Example:
HelperClass helper;
std::unordered_map<Key, Value, Hasher, KeyCompare> map{
1, // bucket count
Hasher{}, // hasher instance
KeyCompare{&helper} // your KeyCompare with the helper
};
Demo
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));
}
Lets say we have some pair like class:
class PairLike{
public:
string key;
int val;
PairLike(string Key, int Val) : key(Key), val(Val){};
//...other members
};
A few objects to go along with it:
PairLike p1("a",1);
PairLike p2("b",2);
PairLike p3("c",3);
PairLike p4("d",4);
Is there a way of automatically working with this object?
For example, something similar to:
std::map<PairLike> container = {p1,p2,p3};
container.insert(p4);
Instead of writing something like:
std::map<string, int> container = {{p1.key, p1.val}, {p2.key, p2.val}, ... }
container.insert({p4.key, p4.val})
I'm aware that using an std::set<PairLike> with a comparator using is_transparentcan achieve the result I'm looking for. However, I am curious if there is any way to approach this problem with a map.
You could provide a conversion operator for converting to std::pair:
class PairLike{
public:
// ...
operator std::pair<const std::string, int>() {
return {key, val};
}
};
And use it like:
std::map<string, int> container{p1,p2,p3};
container.insert(p4);
Edit: please see the #krisz's answer for viable alternative that I overlooked here.
That is not possible. std::pair is defined as part of the class definition, and cannot be altered to a custom class.
P.S. As noted by #UnholySheep, it's defined in the CPP standard:
namespace std {
template<class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key, T>>>
class map {
public:
// types
using key_type = Key;
using mapped_type = T;
using value_type = pair<const Key, T>;
...
I am implementing an optimization to an algorithm.
The optimization consists in stopping the search immediately when the value of a field in a struct is known to be unique. To get the point, just imagine that my struct is like a database table and the field with the unique value would be the equivalent of a "primary key" in a relation database.
I want to dispatch to an implementatio that stops eagerly when it finds the first occurrence of a value if I know that the value of that field is unique. I know this at compile-time by design.
So I want to detect that a given field value is unique at compile-time.
My function looks like this:
template <class Storage, class Getter, class Value>
vector<MyStruct> select_records(Storage const & s, Getter g, Value const & v);
This function will dispatch:
if Getter, which is a member pointer to data, is a "primary key", then it will dispatch to the optimized implementation.
otherwise, dispatch to the implementation that traverses all the Storage.
How could I achieve "primary key" detection? Constraint: The solution must be non-intrusive.
From the information you've given, there is no way to do what you want at compile time. Getter is only a type, and you said you cannot identify a primary key by type alone. Which means you're not doing the identification based on Getter (the type), but based on g, which is a runtime value. There is of course no compile-time access to a runtime value.
If possible, you could achieve this by turning g into a compile-time piece of information, something like this:
template <class Storage, class Value, Value Storage::*getter>
vector<MyStruct> select_records(Storage const & s, Value const & v);
And then specialise it for the known values of getter which correspond to the primary key.
Of course, the above requires you to specify all template arguments explicitly (because the one you want to specify as such, getter, is last), and doesn't really play nice because function templates cannot be partially specialised. Here's something that offers somewhat better syntax and specialisation options:
template <class Storage, class Value>
Selector<Storage, Value> record_selector(Storage const & s, Value const & v)
{
return Selector<Storage, Value>(s, v);
}
template <class Storage, class Value>
class Selector
{
Storage const & s;
Value const & v;
public:
Selector(Storage const & s, Value const & v) : s(s), v(v) {}
template <Value Storage::*getter>
vector<MyStruct> select()
{
return Select_Impl<Storage, Value, getter, IsPrimaryKey<Storage, Value, getter>::value>::call(s, v);
}
};
template <class Storage, class Value, Value Storage::*getter, bool primary>
struct Select_Impl
{
static vector<MyStruct> call(Storage const & s, Value const & v)
{
// Normal implementation.
}
};
template <class Storage, class Value, Value Storage::*getter>
struct Select_Impl<Storage, Value, getter, true>
{
static vector<MyStruct> call(Storage const & s, Value const & v)
{
// Optimised implementation
}
};
template <class Storage, class Value, Value Storage::*getter>
struct IsPrimaryKey
{
static const bool value = false;
};
// Specialise the above for each primary key with `value` set to `true`
// This should be possible, since you said you know the set of primary keys at compile-time
In code, you should then be able to use it like this:
vector<MyStruct> res = record_selector(s, v).select<&SomeStorage::someMember>();
In Python, there is a class called defaultdict which is essentially a dictionary that will construct elements on demand according to a function specified by the user at construction time..
Does a similar class already exists in C++, or would I have to create it myself by inheriting from map and overwriting the at method?
This is not directly an answer to your question, but if you want the same behavior as defaultdict for aggregation, you could use map.emplace to assign a default value if the key does not exist, and return an iterator to the new or existing item (which avoids a second lookup):
unordered_map<int, size_t> map = {{1, 1}, {2, 3}};
// later...
for (int i = 1; i < 4; i++) {
auto emplace_pair = map.emplace(i, 0);
emplace_pair.first->second += 1;
}
There's nothing in the standard library that would do exactly what you want, you'll have to provide such a class yourself.
However, please note that it's a bad idea to publically inherit from a standard library container (such as std::map); they are not designed for this, they don't have virtual functions and they don't have a virtual destructor. Consider this example to see why it's a bad idea:
template <class K, class V, class C, class A>
void foo(const std::map<K, V, C, A> &arg)
{
doSomething(arg.at(K()));
}
struct MyMap : std::map<int, int>
{
int at(int) { return 7; }
};
int main()
{
MyMap m;
foo(m); //this will call std::map::at, NOT MyMap::at
}
Instead, have your class store a std::map (or perhaps std::unordered_map, whichever is better for your implementation) by value. Or, if you think you could re-use a lot of the standard map's member functions and only override some, you could inherit from it non-publically and publish only the functions you need. Example:
template <
class Key,
class Value,
class Comparator = typename std::map<Key, Value>::key_compare,
class Allocator = typename std::map<Key, Value>::allocator_type
>
class DefaultDict : private std::map<Key, Value, Comparator, Allocator>
{
public:
// Publish the clear() function as is
using std::map<Key, Value, Comparator, Allocator>::clear;
// Provide my own at()
Value& at(const Key &key) {
return std::map<Key, Value, Comparator, Allocator>::operator[](key); //call the inherited function
}
// Etc.
};
If I have an object like this:
struct Bar {
std::string const& property();
};
I can create a multi-index container for it like this:
struct tag_prop {};
typedef boost::multi_index_container<
Bar,
boost::multi_index::indexed_by<
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<tag_prop>,
boost::multi_index::const_mem_fun<
Bar, const std::string&, &Bar::property
>
>
>
, ... other indexes
> BarContainer;
But if I have a class like this:
struct Foo {
Bar const& bar();
};
How can I construct an index on .bar().property() for a container of Foo objects?
Normally I would nest calls to boost::bind, but I can't figure out how to make it work in the context of a multi-index container.
Rather than providing a user-defined comparator, you can write a user-defined key extractor:
struct FooBarPropertyExtractor
{
typedef std::string result_type;
const result_type& oeprator()(const Foo& f)
{
return f.bar().property();
}
};
...
typedef boost::multi_index_container<
Bar,
boost::multi_index::indexed_by<
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<tag_prop>,
FooBarPropertyExtractor
>
>
, ... other indexes
> FooContainer;
See Advanced features of Boost.MultiIndex key extractors
I believe you need to create a predicate object that takes two instances of Foo and its operator() can call Foo::bar() on both instances.
Something like
struct MyPredicate
{
bool operator() (const Foo& obj1, const Foo& obj2) const
{
// fill in here
}
};
and then use
...
boost::multi_index::ordered_unique<boost::multi_index::tag<tag_prop>,
boost::multi_index::identity<Foo>, MyPredicate>,
...
Check out MultiIndex Ordered indices reference
As much as I like using lambdas to do simple things, this can quickly degenerate :)
In your case, since it's a bit more complicated, I would rely either on a free function or a predicate comparator.
The predicate has the advantage of defining types more clearly so it's usually easier to actually bring it in.
Also, for readability's sake, I usually typedef my indexes, which gives:
namespace mi = boost::multi_index;
struct FooComparator
{
bool operator()(Foo const& lhs, Foo const& rhs) const
{
return lhs.bar().property() < rhs.bar().property();
}
};
typedef mi::ordered_unique <
mi::tag<tag_prop>,
mi::identity<Foo>,
FooComparator
> foo_bar_index_t;
typedef boost::multi_index_container <
Foo,
mi::indexed_by <
foo_bar_index_t,
// ... other indexes
>
> foo_container_t;
The predicate approach requires more boilerplate code, but it allows to nicely separate the comparison logic from the index definition, which is itself separated from the container definition.
Clear separation makes it easier to view the structure at a glance.