How to create a QSet of iterators - c++

What I am trying to do is:
#include <QVector>
#include <QLinkedList>
#include <QSet>
class MyType
{
//...
};
int main(int argc, char** argv)
{
QVector<MyType> vector;
QSet<QVector<MyType>::iterator> a;
a.insert(vector.begin()); // This is fine
QLinkedList<MyType> linkedList;
QSet<QLinkedList<MyType>::iterator> b;
b.insert(linkedList.begin()); // This does not compile
return 0;
}
The compiler message is:
error: no matching function for call to 'qHash(const QLinkedList<MyType>::iterator&)'
I know, that the reason why the first three lines compile is that for the QVector, the iterator is defined as typedef T* iterator; but for the QLinkedList it is a custom type.
I found out, that the QSet template class is implemented in terms of a hash table.
Apparently it is possible to evaluate the hash function for a pointer, but not for a custom type.
Please, could you tell me, how to overload the qHash function for my program to compile? I have read some basic information on the workings of a hash table, but I lack the confidence in the subject.
I tried to understand the inner workings of the QLinkedList<T>::iterator. It seems that it is very similar to the QVector<T>::iterator. It just holds a pointer to a node in the linked list instead of a pointer to the item itself.
class iterator
{
public:
...
Node *i;
...
};
So I tried to define the function in this manner:
uint qHash(QLinkedList<MyType>::iterator it)
{
return qHash(it.i);
}
The program compiled, but I have no confidence in my solution. How should I correctly overload the qHash function?

You did perfectly well already. The basic rules of hash functions are:
If x = y then hash(x) = hash(y).
If x != y then hash(x) != hash(y) (as often as possible). This isn't a strict rule, but the better it's followed, the better the performance of the hash table. Ideally, the outputs will appear random.
Your way works because if two iterators, ia and ib are equal (referring to the same node), their internal pointers, ia.i and ib.i will be equal. That takes care of rule 1. Then you use a built-in hash function on those pointers; Qt takes care of rule 2 for you.
Cheers!

Related

Is it possible / advisable to return a range?

I'm using the ranges library to help filer data in my classes, like this:
class MyClass
{
public:
MyClass(std::vector<int> v) : vec(v) {}
std::vector<int> getEvens() const
{
auto evens = vec | ranges::views::filter([](int i) { return ! (i % 2); });
return std::vector<int>(evens.begin(), evens.end());
}
private:
std::vector<int> vec;
};
In this case, a new vector is constructed in the getEvents() function. To save on this overhead, I'm wondering if it is possible / advisable to return the range directly from the function?
class MyClass
{
public:
using RangeReturnType = ???;
MyClass(std::vector<int> v) : vec(v) {}
RangeReturnType getEvens() const
{
auto evens = vec | ranges::views::filter([](int i) { return ! (i % 2); });
// ...
return evens;
}
private:
std::vector<int> vec;
};
If it is possible, are there any lifetime considerations that I need to take into account?
I am also interested to know if it is possible / advisable to pass a range in as an argument, or to store it as a member variable. Or is the ranges library more intended for use within the scope of a single function?
This was asked in op's comment section, but I think I will respond it in the answer section:
The Ranges library seems promising, but I'm a little apprehensive about this returning auto.
Remember that even with the addition of auto, C++ is a strongly typed language. In your case, since you are returning evens, then the return type will be the same type of evens. (technically it will be the value type of evens, but evens was a value type anyways)
In fact, you probably really don't want to type out the return type manually: std::ranges::filter_view<std::ranges::ref_view<const std::vector<int>>, MyClass::getEvens() const::<decltype([](int i) {return ! (i % 2);})>> (141 characters)
As mentioned by #Caleth in the comment, in fact, this wouldn't work either as evens was a lambda defined inside the function, and the type of two different lambdas will be different even if they were basically the same, so there's literally no way of getting the full return type here.
While there might be debates on whether to use auto or not in different cases, but I believe most people would just use auto here. Plus your evens was declared with auto too, typing the type out would just make it less readable here.
So what are my options if I want to access a subset (for instance even numbers)? Are there any other approaches I should be considering, with or without the Ranges library?
Depends on how you would access the returned data and the type of the data, you might consider returning std::vector<T*>.
views are really supposed to be viewed from start to end. While you could use views::drop and views::take to limit to a single element, it doesn't provide a subscript operator (yet).
There will also be computational differences. vector need to be computed beforehand, where views are computed while iterating. So when you do:
for(auto i : myObject.getEven())
{
std::cout << i;
}
Under the hood, it is basically doing:
for(auto i : myObject.vec)
{
if(!(i % 2)) std::cout << i;
}
Depends on the amount of data, and the complexity of computations, views might be a lot faster, or about the same as the vector method. Plus you can easily apply multiple filters on the same range without iterating through the data multiple times.
In the end, you can always store the view in a vector:
std::vector<int> vec2(evens.begin(), evens.end());
So my suggestions is, if you have the ranges library, then you should use it.
If not, then vector<T>, vector<T*>, vector<index> depending on the size and copiability of T.
There's no restrictions on the usage of components of the STL in the standard. Of course, there are best practices (eg, string_view instead of string const &).
In this case, I can foresee no problems with handling the view return type directly. That said, the best practices are yet to be decided on since the standard is so new and no compiler has a complete implementation yet.
You're fine to go with the following, in my opinion:
class MyClass
{
public:
MyClass(std::vector<int> v) : vec(std::move(v)) {}
auto getEvens() const
{
return vec | ranges::views::filter([](int i) { return ! (i % 2); });
}
private:
std::vector<int> vec;
};
As you can see here, a range is just something on which you can call begin and end. Nothing more than that.
For instance, you can use the result of begin(range), which is an iterator, to traverse the range, using the ++ operator to advance it.
In general, looking back at the concept I linked above, you can use a range whenever the conext code only requires to be able to call begin and end on it.
Whether this is advisable or enough depends on what you need to do with it. Clearly, if your intention is to pass evens to a function which expects a std::vector (for instance it's a function you cannot change, and it calls .push_back on the entity we are talking about), you clearly have to make a std::vector out of filter's output, which I'd do via
auto evens = vec | ranges::views::filter(whatever) | ranges::to_vector;
but if all the function which you pass evens to does is to loop on it, then
return vec | ranges::views::filter(whatever);
is just fine.
As regards life time considerations, a view is to a range of values what a pointer is to the pointed-to entity: if the latter is destroied, the former will be dangling, and making improper use of it will be undefined behavior. This is an erroneous program:
#include <iostream>
#include <range/v3/view/filter.hpp>
#include <string>
using namespace ranges;
using namespace ranges::views;
auto f() {
// a local vector here
std::vector<std::string> vec{"zero","one","two","three","four","five"};
// return a view on the local vecotor
return vec | filter([](auto){ return true; });
} // vec is gone ---> the view returned is dangling
int main()
{
// the following throws std::bad_alloc for me
for (auto i : f()) {
std::cout << i << std::endl;
}
}
You can use ranges::any_view as a type erasure mechanism for any range or combination of ranges.
ranges::any_view<int> getEvens() const
{
return vec | ranges::views::filter([](int i) { return ! (i % 2); });
}
I cannot see any equivalent of this in the STL ranges library; please edit the answer if you can.
EDIT: The problem with ranges::any_view is that it is very slow and inefficient. See https://github.com/ericniebler/range-v3/issues/714.
It is desirable to declare a function returning a range in a header and define it in a cpp file
for compilation firewalls (compilation speed)
stop the language server from going crazy
for better factoring of the code
However, there are complications that make it not advisable:
How to get type of a view?
If defining it in a header is fine, use auto
If performance is not a issue, I would recommend ranges::any_view
Otherwise I'd say it is not advisable.

Insertion into map<char*, struct _channel*l> channel

I have this map:
typedef struct _channel {
int stuffs;
}channel;
map<char*, struct _channel*> channel
But when I use:
char* str = (char*)malloc(MAX_NAME);
channel* pointer = (channel*)malloc(sizeof(channel));
channel[str] = pointer;
I don't get pass through it:
// This should've been true, even with find() method
if(channel[str] != 0) {
// code here
}
The default equality comparison for any pointer, including char*, is just to literally compare the pointer.
If you have two C-strings, at different locations in memory, with the same contents, comparing pointers to them will not give you the answer you wanted. That's why we use strcmp instead when comparing these things.
It's possible to make std::map do that, so that lookup works as you intended, but instead you should just use std::string as a key; it does this stuff for you.
As an aside, you're using a lot of C-isms here. typedef struct, malloc… Also, you can't create an object with malloc before C++20, so your program technically has undefined behaviour, though this was more a standard oversight than anything any implementation ever deliberately took advantage of, so in practice you're likely to be fine (ref). Robust, safe, well-defined code is recommended regardless.
#include <map>
#include <string>
#include <memory>
struct channel
{
int stuff;
};
std::map<std::string, std::unique_ptr<channel>> channels;
int main()
{
channel["key"] = std::make_unique<channel>();
// ...
if (channel["key"] != nullptr)
{
// ...
}
}
You probably don't need the pointer indirection at all; you can likely just store channels in the map as values.
If not, because code elsewhere needs to "own" the channels too, std::shared_ptr is your friend.

c++ - sorting a vector of custom structs based on frequency

I need to find the most frequent element in an array of custom structs. There is no custom ID to them just matching properties.
I was thinking of sorting my vector by frequency but I have no clue how to do that.
I'm assuming by frequency you mean the number of times an identical structure appears in the array.
You probably want to make a hash function (or overload std::hash<> for your type) for your custom struct. Then iterate over your array, incrementing the value on an unordered_map<mytype, int> for every struct in the array. This will give you the frequency in the value field. Something like the below would work:
std::array<mytype> elements;
std::unordered_map<mytype, int> freq;
mytype most_frequent;
int max_frequency = 0;
for (const mytype &el : elements) {
freq[el]++;
if (freq[el] > max_frequency) {
most_frequent = el;
}
}
For this to work, the map will need to be able to create a hash for the above function. By default, it tries to use std::hash<>. You are expressly allowed by the standard to specialize this template in the standard namespace for your own types. You could do this as follows:
struct mytype {
std::string name;
double value;
};
namespace std {
template <> struct hash<mytype> {
size_t operator()(const mytype &t) const noexcept {
// Use standard library hash implementations of member variable types
return hash<string>()(t.name) ^ hash<double>()(t.value)
}
}
}
The primary goal is to ensure that any two variables that do not contain exactly the same values will generate a different hash. The above XORs the results of the standard library's hash function for each type together, which according to Mark Nelson is probably as good as the individual hashing algorithms XOR'd together. An alternative algorithm suggested by cppreference's hash reference is the Fowler-Noll-Vo hash function.
Look at std::sort and the example provided in the ref, where you actually pass your own comparator to do the trick you want (in your case, use the frequencies). Of course, a lambda function can be used too, if you wish.

c++ syntax to find min in a list

I see some complicated discussion on this topic but I'm failing to make what should be a very simple piece of code work. I've found two ways to make it work but I'm puzzled by the some issues of syntax. I just want to find the minimum value in a list. This works:
using namespace std;
#include <iostream>
#include <list>
#include <algorithm>
int main(void)
{
auto init_list = {10, 20, 30};
list<int> alist(init_list);
list<int>::iterator minnum = min_element(begin(alist), end(alist));
cout << "The min in the list is: " << *minnum << endl;
return(1);
}
But there is also the definition of min() which takes an initializer_list as an argument. I think I am misunderstanding the syntax of it. Since I've already defined an init_list I can call
int minint = min(init_list);
and that works. But I don't understand why. The documentation on min says this call should be of form:
template< class T, class Compare >
T min( std::initializer_list<T> ilist, Compare comp );
So shouldn't I have to specify a < comparison operator to be able to use this (apparently not...why?)? However, I don't know how to pass this operator correctly, and have been unable to come up with a clear explanation. Each of the following generate a wide variety of syntax errors:
int minint = min(init_list, operator<);
int minint = min(init_list, int::operator<);
How am I supposed to pass the operator? Clearly it doesn't matter in this case but if my list was not a list of ints but a list of some custom type with overloaded operators then would I need to pass an operator< ? Or does the syntax simply mean that class T must have a comparison operator defined, but you never have to specify it in the min function call?
There is version of min (third declaration) which takes only initialization list.
And if you want to be explicit about comparing with < relation, use std::less:
int minint = min(init_list, std::less<int>());
Thanks Tomasz Klak. I'm pretty new to operator overloading so I didn't know the std::less syntax. That's very useful to know. Clearly I wasn't reading carefully enough, since I missed that other definition of min(). My other question was about how this works with custom objects. I think I've answered that for myself now. Here's what I've found.
You can make an initialization list of custom objects. For example, I have a very simple class that I call Double_list_struct, which is just a container for an int (the index) and a double (the value), with some typical member functions (constructors, getters, setters, toString() and overloaded operators, including operator<). So I can make an initialization list of it by:
Double_list_struct doub1(1,5.5);
Double_list_struct doub2(1,3.5);
Double_list_struct doub3(1,26.5);
auto init_list_doub = {doub1, doub2, doub3};
Having done this the min() function works on it perfectly well.
Double_list_struct mindoub = min(init_list_doub);
I assume (but haven't checked) that if the Double_list_struct class hadn't included an operator< function I would have had to provide one.
Cheers!

C++ sort method

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.