Is it possible to have a set(or unordered_set ) of functions in c++ like this?
set<function<void(int)>> funcSet;
I got something like this
error: invalid operands to binary expression
('const std::__1::function<void (int)>' and 'const std::__1::function<void (int)>')
{return __x < __y;}
How can I compare two functions?
A set has the requirement that its elements may be ordered using <. So to put functions in a set, you have to define an ordering for functions first. For example, this comparison considers all functions with the same type as equal:
#include <set>
#include <functional>
using namespace std;
typedef function<void(int)> fun;
bool operator<(const fun& f1, const fun& f2) {
return f2.target_type().name() < f2.target_type().name();
}
int main() {
set<fun> fset;
}
Likewise, for an unordered_set, you'd have to define a specialization of std::hash<fun>.
Edit: I've borrowed the target idea from another solution to make the comparison well-defined.
Edit2: The most meaningful comparison for arbitrary functions would probably look like this:
struct fun_comp {
template<typename Fun1, typename Fun2>
bool operator()(const Fun1& f1, const Fun2& f2) {
const char* c1 = f1._M_functor._M_pod_data;
const char* c2 = f2._M_functor._M_pod_data;
size_t sz = sizeof(f1._M_functor._M_pod_data);
return lexicographical_compare(c1, c1+sz, c2, c2+sz);
}
};
This is, obviously, completely unportable, depends on libstdc++-internals and will only compile with -fno-access-control, so you probably shouldn't actually do it like this.
Would a std::vector<std::function<void(int)>> work?
If you want to order your functions it seems like you'd be the only one who knew the order anyway.
Perhaps creating an enum to index the std::vector would also be helpful for ordering?
Related
I have a struct with two fields :
struct road {
int from, len ;
};
For some reason, I need to be able to order my roads :
by ascending from in an array
by ascending len in a priority queue
I have thus included :
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
I have come across websites suggesting to overload the operator<, but because of the two possible orderings that just feels wrong and it would only solve one of the two.
By messing around with textbooks, I got this to work :
bool cmpFrom (const road & a, const road & b) {
return (a.from < b.from) ;
}
struct cmpLen {
bool operator () (const road & a, const road & b){
return (a.len < b.len) ;
}
};
To be used with :
std::sort(trips, trips + nbRoads, &cmpFrom) ;
std::priority_queue<road, std::vector<road>, cmpLen> pickRoad ;
Where trips is of course a road [].
It compiles perfectly (haven't tried running it, but it should be fine), but it seems weird to define two very similar comparators in two quite different manners, so isn't there a way to define both comparison methods the same way ?
Changing the definition of cmpFrom to
struct cmpFrom {
bool operator () (const road & a, const road & b){
return (a.from < b.from) ;
}
};
Gives
chantier.cpp: In function ‘int main()’:
chantier.cpp:38:48: error: expected primary-expression before ‘)’ token
std::sort(trips, trips + nbRoads, &cmpFrom) ;
Which I assume means "You gave me a type when I was expecting a reference".
While writing
bool cmpLen (const road & a, const road & b) {
return (a.len <= b.len) ;
}
Gives
chantier.cpp: In function ‘int main()’:
chantier.cpp:52:56: error: type/value mismatch at argument 3 in template parameter list for ‘template<class _Tp, class _Sequence, class _Compare> class std::priority_queue’
std::priority_queue<road, std::vector<road>, cmpLen> pickRoad ;
^
chantier.cpp:52:56: note: expected a type, got ‘cmpLen’
chantier.cpp:56:30: error: request for member ‘top’ in ‘pickRoad’, which is of non-class type ‘int’
...
Is there a way to make one of these comparison methods work for both containers ? Or is there perhaps a third way of doing this that could work with both ?
What if I had needed to use the same ordering with both containers ? Would that have required defining twice the same comparison method, but with one inside a struct ?
You almost have it. In std::sort you need an object that you can call operator() on. Using
bool cmpFrom (const road & a, const road & b) {
return (a.from < b.from) ;
}
std::sort(trips, trips + nbRoads, &cmpFrom);
works because a function pointer can be used like a function. When you change cmpFrom to
struct cmpFrom {
bool operator () (const road & a, const road & b){
return (a.from < b.from) ;
}
};
you can't use std::sort(trips, trips + nbRoads, &cmpFrom); anymore because you can't apply & to a type name. Instead what you need to do is get an object of cmpFrom and you do that like
std::sort(trips, trips + nbRoads, cmpFrom{});
now both the priority_queue and sort could use cmpFrom.
It's easier to define both as structures, because you can always create an object from a type and it will behave as expected, but getting a type from a function and having it act as a caller for the function is much more difficult.
You were in fact almost there with struct cmpFrom. However, you've correctly noted that std::sort expects a comparator object (such as a function), not a type. Of course, doing &cmpFrom where cmpFrom is a type is not valid C++. Instead, you need to create an object of that type; thanks to the operator() defined, the object will be callable and do what you want. So just call std::sort like this:
std::sort(trips, trips + nbRoads, cmpFrom{});
The std::sort function and std::priority_queue class template want two different things: sort wants a callable object, while priority_queue template wants a type, which allows creating objects.
Because of that, sort is more omnivorous than priority_queue - you can use it with either functions or functors. The only thing you need is to provide it with a real object (while currently in your code you are trying to take an address of a type, which makes no sense).
To fix it in your example just change the code to
std::sort(trips, trips + nbRoads, cmpFrom{});
I understand a set is ordered, thus adding an object without overloading the < operator doesn't allow to say which object is smaller to keep the container sorted. However, I don't understand why this isn't possible with an unordered_set.
If I try something like this:
#include <iostream>
#include <string
#include <unordered_set>
struct someType{
string name;
int code;
};
int main(){
std::unordered_set <someType> myset;
myset.insert({"aaa",123});
myset.insert({"bbb",321});
myset.insert({"ccc",213});
return 0;
}
I get a couple of errors like:
c:\qt\qt5.1.0\tools\mingw48_32\lib\gcc\i686-w64-mingw32\4.8.0\include\c++\bits\hashtable_policy.h:1070: error: invalid use of incomplete type 'struct std::hash'
c:\qt\qt5.1.0\tools\mingw48_32\lib\gcc\i686-w64-mingw32\4.8.0\include\c++\bits\functional_hash.h:58: error: declaration of 'struct std::hash'
error: no matching function for call to 'std::unordered_set::unordered_set()'
c:\qt\qt5.1.0\tools\mingw48_32\lib\gcc\i686-w64-mingw32\4.8.0\include\c++\bits\hashtable_policy.h:1103: error: no match for call to '(const std::hash) (const someType&)'
c:\qt\qt5.1.0\tools\mingw48_32\lib\gcc\i686-w64-mingw32\4.8.0\include\c++\bits\stl_function.h:208: error: no match for 'operator==' (operand types are 'const someType' and 'const someType')
Why is that and how can I fix it?
To use type in unordered_set or unordered_map you need hashing function for your type. For common types, like int or std::string - hashing function is provided by standard library. For your type, you can overload standard std::hash, like this:
namespace std {
template <> struct hash<someType> {
size_t operator()(const someType & x) const {
std::hash<std::string> h;
return h(x.name);
// or simply return x.code
// or do something more interesting,
// like xor'ing hashes from both members of struct
}
};
}
Another way is to provide your own type with overloaded operator() and put it as hash template argument in unordered_set, like this:
struct someTypeHasher {
size_t operator()(const someType& x) const {
return x.code;
}
};
std::unordered_set<someType, someTypeHasher> myset;
Good reading for theory about hash based containers is here
Also, do not forget, that you need to overload operator== for someType, without it - it will also not work.
As explained in the answer given by Starl1ght, you need to provide a hash function for someType. However, I would combine all members of your class by that hash function. Otherwise, you might get a lot of collisions, for example, if the same name occurs very often, but with different code values. For creating a hash function, you can make use of Boost, but you can also handcraft it.
Starl1ght also mentioned that you need to overload operator== for someType,
but you can also define a separate comparison function instead and provide it to the unordered_set. Moreover, you can use lambda expressions instead of defining the hash and comparison functions. If you put everything together, then your code could be written as follows:
auto hash = [](const someType& st){
return std::hash<std::string>()(st.name) * 31 + std::hash<int>()(st.code);
};
auto equal = [](const someType& st1, const someType& st2){
return st1.name == st2.name && st1.code == st2.code;
};
std::unordered_set<someType, decltype(hash), decltype(equal)> myset(8, hash, equal);
Code on Ideone
Discussion:
Let's say I have a struct/class with an arbitrary number of attributes that I want to use as key to a std::unordered_map e.g.,:
struct Foo {
int i;
double d;
char c;
bool b;
};
I know that I have to define a hasher-functor for it e.g.,:
struct FooHasher {
std::size_t operator()(Foo const &foo) const;
};
And then define my std::unordered_map as:
std::unordered_map<Foo, MyValueType, FooHasher> myMap;
What bothers me though, is how to define the call operator for FooHasher. One way to do it, that I also tend to prefer, is with std::hash. However, there are numerous variations e.g.,:
std::size_t operator()(Foo const &foo) const {
return std::hash<int>()(foo.i) ^
std::hash<double>()(foo.d) ^
std::hash<char>()(foo.c) ^
std::hash<bool>()(foo.b);
}
I've also seen the following scheme:
std::size_t operator()(Foo const &foo) const {
return std::hash<int>()(foo.i) ^
(std::hash<double>()(foo.d) << 1) ^
(std::hash<char>()(foo.c) >> 1) ^
(std::hash<bool>()(foo.b) << 1);
}
I've seen also some people adding the golden ratio:
std::size_t operator()(Foo const &foo) const {
return (std::hash<int>()(foo.i) + 0x9e3779b9) ^
(std::hash<double>()(foo.d) + 0x9e3779b9) ^
(std::hash<char>()(foo.c) + 0x9e3779b9) ^
(std::hash<bool>()(foo.b) + 0x9e3779b9);
}
Questions:
What are they trying to achieve by adding the golden ration or shifting bits in the result of std::hash.
Is there an "official scheme" to std::hash an object with arbitrary number of attributes of fundamental type?
A simple xor is symmetric and behaves badly when fed the "same" value multiple times (hash(a) ^ hash(a) is zero). See here for more details.
This is the question of combining hashes. boost has a hash_combine that is pretty decent. Write a hash combiner, and use it.
There is no "official scheme" to solve this problem.
Myself, I typically write a super-hasher that can take anything and hash it. It hash combines tuples and pairs and collections automatically, where it first hashes the count of elements in the collection, then the elements.
It finds hash(t) via ADL first, and if that fails checks if it has a manually written hash in a helper namespace (used for std containers and types), and if that fails does a std::hash<T>{}(t).
Then my hash for Foo support looks like:
struct Foo {
int i;
double d;
char c;
bool b;
friend auto mytie(Foo const& f) {
return std::tie(f.i, f.d, f.c, f.b);
}
friend std::size_t hash(Foo const& f) {
return hasher::hash(mytie(f));
}
};
where I use mytie to move Foo into a tuple, then use the std::tuple overload of hasher::hash to get the result.
I like the idea of hashes of structurally similar types having the same hash. This lets me act as if my hash is transparent in some cases.
Note that hashing unordered meows in this manner is a bad idea, as an asymmetric hash of an unordered meow may generate spurious misses.
(Meow is the generic name for map and set. Do not ask me why: Ask the STL.)
The standard hash framework is lacking in respect of combining hashes. Combining hashes using xor is sub-optimal.
A better solution is proposed in N3980 "Types Don't Know #".
The main idea is using the same hash function and its state to hash more than one value/element/member.
With that framework your hash function would look:
template <class HashAlgorithm>
void hash_append(HashAlgorithm& h, Foo const& x) noexcept
{
using std::hash_append;
hash_append(h, x.i);
hash_append(h, x.d);
hash_append(h, x.c);
hash_append(h, x.b);
}
And the container:
std::unordered_map<Foo, MyValueType, std::uhash<>> myMap;
So, I had an assignment to write a program to test speeds of different sorting algorithms, and one of them is good old qsort. I need to pass a comparator to it, but not the one it expects, but something boolean a-la std::less, and I know that to use it so that qsort accepts it, I need to actually pass it something like less(b, a) - less(a,b) --- this way, it has the range of [-1; 1] and produces what I need.
The problem is this: I have no idea how to actually do it! I tried to use lambda --- and (because I need to capture the comparator and qsort can't handle this) it failed. I tried to create another function converting my comparator to qsort's:
int make_comparator(const void* a, const void* b) {
return (int)comp(*(int*)b, *(int*)a) - (int)comp(*(int*)a, *(int*)b);
}
But I have no idea on how to actually pass the comp to it (because i can't just write qsort(..., make_comparator(comp, a, b)), can I?). I tried to use a template to pass comp, but couldn't figure out how.
So I'm struggling with it for like an hour already, and I'm no closer to a solution. What's the correct way to do this?
You can try something like this. qsort_friendly_comparator just acts a wrapper around the comparator object. The only downside is that you have to manually specify the comparator type and its argument type.
#include <functional>
#include <cstdlib>
#include <cstdio>
// Assumes Comparator take two arguments of the same type and returns a bool.
// Have to manually specify the ArgType because it is tricky to deduce without
// excessive template magic.
template <typename Comparator, typename ArgType>
int qsort_friendly_comparator(const void *first, const void *second)
{
Comparator comp;
return (int)comp(*(ArgType*)second, *(ArgType*)first) -
(int)comp(*(ArgType*)first, *(ArgType*)second);
}
int main() {
int data[] = {2, 1, 3, 0};
qsort(data,
/*num_elem=*/4,
/*size_of_elem=*/sizeof(int),
&qsort_friendly_comparator<std::less<int>, int>);
for (int i = 0; i < 4; i++) {
printf("%d ", data[i]);
}
printf("\n");
}
You cannot pass cmp to make_comparator via qsort. Your best option is to have a function pointer that is set to the right function.
bool (*comp)(int a, int b) = nullptr;
int make_comparator(const void* a, const void* b) {
return (int)comp(*(int*)b, *(int*)a) - (int)comp(*(int*)a, *(int*)b);
}
and later set comp to a valid function pointer
comp = <some function pointer>;
before calling qsort.
qsort(ptr, count, size, make_comparator);
If you choose to follow this approach, make sure to add checks in make_comparator to prevent calling cmp when it is not set to a valid function.
int make_comparator(const void* a, const void* b) {
if ( comp == nullptr )
{
// Deal with error
}
return (int)comp(*(int*)b, *(int*)a) - (int)comp(*(int*)a, *(int*)b);
}
I want to sort a vector using std::sort, but my sort method is a static method of a class, and I want to call std::sort outside it, but it seems to be trouble doing it this way.
On the class:
static int CompareIt(void *sol1, void *sol2) { ... }
std::sort call:
sort(distanceList.at(q).begin(),
distanceList.at(q).end(),
&DistanceNodeComparator::CompareIt);
Shouldn't it be possible to do this way?
std::sort takes a comparator that accepts value of the type held in the collection and returns bool. It should generally implement some notion of <. E.g., assuming your distanceList elements have collections of integers (I assume they don't, but for the sake of the example):
static bool CompareIt(int sol1, int sol2) { ... }
And of course you only need to supply a comparator if there isn't already a < operator that does the right thing for your scenario.
It should be a boolean method (sort uses operator <() by default to compare values)
The comparison function you've provided has the signature of the one needed by qsort, which is the sorting function that C provided before C++ came along. sort requires a completely different function.
For example if your declaration of distanceList is std::vector<DistanceNode> your function would look like:
static bool CompareIt(const DistanceNode &sol1, const DistanceNode &sol2)
{
return sol1.key < sol2.key;
}
Notice that sorting a std::list with the standard sort algorithm isn't efficient, which is why list supplies its own sort member function.
As others have mentioned, it needs a boolean return type. Here's an example which works:
#include "stdafx.h"
#include <vector>
#include <algorithm>
using namespace std;
class MyClass
{
public:
static bool CompareIt(const void *a1, const void *a2)
{
return a1 < a2;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
// Create a vector that contains elements of type MyData
vector<void*> myvector;
// Add data to the vector
myvector.push_back((void*)0x00000005);
myvector.push_back((void*)0x00000001);
// Sort the vector
std::sort(myvector.begin(), myvector.end(), MyClass::CompareIt);
// Display some results
for( int i = 0; i < myvector.size(); i++ )
{
printf("%d = 0x%08X\n", i, myvector[i] );
}
return 0;
}
[Edit] Updated the code above to make it a little simpler. I'm not suggesting it's nice code, but without know more about the OPs real implementation, it's difficult to give a better example!
First, the return type should be bool. Actually the requirement is only that the return type be assignable to bool, which int is. But the fact that you're returning int suggests that you might have written a three-way comparator instead of the strict weak ordering required by std::sort.
Your CompareIt function takes two void* pointers as parameters. Is distanceList.at(q) a vector<void*> (or vector of something convertible to void*)? If not, then the comparator inputs aren't right either. Using void* with algorithms also suggests that you're doing something wrong, because much of the point of generic programming is that you don't need opaque pointers that later get cast back to their original type.