I used the STL map in order to keep information about my vertices and their value
and I have decided to keep them as pointers for faster run time.
The problem is that now I'm trying to add new vertices only if they don't exist, therefore I used
the MAP.FIND() function and logically its not the right thing to do.
(since the find function now only compares by the pointers values and not the vertex values)
Now after googling for a bit, I tried using the longer c-tor
struct classcomp {
bool operator() (const Vertex* v1,const Vertex* v2) const
{
return v1->getX()==v2->getX() &&
v1->getY()==v2->getY() &&
v1->getZ()==v2->getZ();
}
};
map<Vertex *,Position,classcomp> ;
but still no success , what am I doing wrong?
is it even possible to use find in a different way than comparing pointer address values?
Thanks,
-Despair
updates: still having a problem,
since you cant really use bool operator< with 2 arguments inside a struct.
so far I didnt find anything useful and all compilation tryouts failed.
couldnt find an example of doing so.
are you positive its possible with pointers?
thank you.
Your comparison function needs to impleent a strict weak ordering (operator<) not equality.
Related
I have a vector of pointers to structs, and want to check for existing items and sort by the value of a struct member. However, it appears that the check for existing items (I'm using QVector::indexOf()) is not working, and that std::sort is applying its sorting to some pointer value rather than the member value. I don't understand why the struct operator overloads aren't getting called (or called properly).
In header file:
struct PlotColumn {
quint32 octagonLength;
QVector<Qt::GlobalColor> columnVector;
bool operator< (const PlotColumn& pc) const
{
return (this->octagonLength < pc.octagonLength);
}
bool operator==(PlotColumn const& pc)
{
return this->octagonLength == pc.octagonLength;
}
bool operator> (const PlotColumn& pc) const
{
return (this->octagonLength > pc.octagonLength);
}
};
QVector<PlotColumn*> *plotMap;
In source file:
PlotColumn *pcNew = new PlotColumn();
pcNew->octagonLength = octagonLength;
// check if octagonLength has arrived before:
int plotXAxis = plotMap->indexOf(pcNew);
if (plotXAxis == -1) { // unknown, so insert:
... (some housekeeping)
plotMap->append(pcNew);
std::sort(plotMap->begin(), plotMap->end());
....
(some more housekeeping)
}
Using an external (not in the struct) is possible for sort, but not for indexOf (afaik).
Any thoughts?
PS: yes I know there are tons of already answered questions about sorting vectors of pointers to structs and I've tried to apply the pattern in those solutions but it still doesn't work.
You need to provide a comparater to std::sort() that works with pointers.
bool PlotColumnPointerComparator(const PlotColumn *a, const PlotColumn *b)
{
return (*a) < (*b); // this will call your PlotColumn:: operator<()
}
And change your std::sort() statement to
std::sort(plotMap->begin(), plotMap->end(), PlotColumnPointerComparator);
In C++11, you could do the above with a lambda function, but that's just a syntactic convenience.
Compilers are not omnipotent mindreaders. If you tell it to sort a set of pointers, it will do pointer comparison. If you want the comparison to dereference the pointers and compare the pointed-to objects, you need to tell it to do that .... as in the above.
std::sort is applying its sorting to some pointer value
Let's see:
QVector<PlotColumn*> *plotMap
std::sort(plotMap->begin(), plotMap->end());
Yep, that's exactly what you told std::sort to do (assuming QVector resembles vector): you have a container of pointers, told it to sort the elements of the container. Thus, you told it to sort pointers, not PlotColumn objects.
If you want to sort the pointers based on how the objects they point to compare, you have to apply one of those
answered questions about sorting vectors of pointers to structs
You've already identified your problem:
I've tried to apply the pattern in those solutions but it still doesn't work.
Don't give up on that line of inquiry: you've correctly identified something you need to work on, and should work to understand how those patterns work and how to apply them to your problem, and maybe ask a focused question regarding your attempts at that.
It will not work because You are using the same signature. Overload works for different signatures. Check your function signatures.
I am sorry if the title isn't very descriptive, I was having a hard time figuring out how to name this question. This is pretty much the first time I need to use a set, though I've been using maps forever.
I don't think it is possible, but I need to ask. I would like to perform a specific action on a struct when I add it to my std::set, but only if equality is true.
For example, I can use a list and then sort() and unique() the list. In my predicate, I can do as I wish, since I will get the result if 2 values are equal.
Here is a quick example of what my list predicate looks like:
bool markovWeightOrdering (unique_ptr<Word>& w1, unique_ptr<Word>& w2) {
if (w1->word_ == w2->word_) {
w1->weight_++;
return true;
}
return false;
}
Does anyone have an idea how to achieve a similar result, while using a std::set for the obvious gain in performance (and simplicity), since my container needs to be unique anyways? Thank you for any help or guidance, it is much appreciated.
element in set are immutable, so you cannot modify them.
if you use set with pointer (or similar), the pointed object may be modified (but care to not modify the order). std::set::insert returns a pair with iterator and a boolean to tell if element has been inserted, so you may do something like:
auto p = s.insert(make_unique<Word>("test"));
if (p.second == false) {
(*p.first)->weight += 1;
}
Live example
Manipulating a compare operator is likely a bad idea.
You might use a std::set with a predicate, instead:
struct LessWord
{
bool operator () (const std::unique_ptr<Word>& w1, const std::unique_ptr<Word>& w2) {
return w1->key < w2->key;
}
};
typedef std::set<std::unique_ptr<Word>, LessWord> word_set;
Than you test at insert if the word is existing and increment the weight:
word_set words;
std::unique_ptr<Word> word_ptr;
auto insert = words.insert(word_ptr);
if( ! insert.second)
++(insert.first->get()->weight_);
Note: Doing this is breaking const correctness, logically. A set element is immutable, but the unique_ptr enables modifications (even a fatal modification of key values).
This is a rather simple question, and I think it breaks down to whether or not this is legal. I have a class with the ">" and "<" operators overloaded. I'm trying to make a single statement that will return true or false without having to declare an iterator for the class, just for "simplicity".
Here is an example of what I'm talking about:
class Guy
{
double bigness; //I'm not illiterate by the way
public:
bool operator>(const Guy& that) {return (this->bigness > that.bigness);}
};
Ok, this right there should work. Now I make two vectors of type guy, and I want to compare two members in a single expression so I get the truth value for that expression, like this:
vector<Guy> gang, mafia;
bool mafiaWins = (*(mafia.begin()) > *(gang.begin()));
I didn't populate the vector, so I wouldn't get anything reasonable from this code. But how could I manage to have the expression (mafia.begin() > gang.begin()) to work? I know they are both returning iterators; but dereferencing them doesn't work. Using .front() gives me the same error. I get an assertion for having a non-dereferencable iterator (Shouldn't it be dereferenciable?). I'm wondering if the problem is on not storing it in an iterator first...
Thanks for your attention
Solution:
So, the code I have above (Edited from when I first posted), should work for the expression. That is not the code I actually had in my .cpp, that's why its so messy. You don't need to use random access reference if you are only trying to reach the first member of the vector, and ,front() will the the reference for the first term. I originally left the vector empty in this post, because I thought it was irrelevant to populate them, and by doing that I accidentally got what my problem was in my code. The function I had getting vectors and checking their members was not checking whether or not the members I was comparing existed. So, for anyone that gets a similar assertion problem, you are possibly trying to dereference something that doesn't exist. For checking if a vector is empty there is a member function that can be used.
Example:
bool thereIsWar = (!(mafia.empty() && !(gang.empty()));
if(thereIsWar)
bool mafiaWins = (*(mafia.begin()) > *(gang.being()));
Hope it helps anyone with a similar question
that is a reference.
Change:
bool operator>(const Guy& that) {return (this->bigness > that->bigness);}
To:
bool operator>(const Guy& that) {return (this->bigness > that.bigness);}
Ensure your vector has at least one element before testing:
vector<Guy> gang, mafia;
gang.push_back(Guy()); // Not sure you how you set 'bigness'.
mafia.push_back(Guy());
Dereference iterators for comparision:
bool mafiaWins = (*mafia.begin() > *gang.begin()) ;
This should work so long as the vector has contents (I get a segfault if they are empty).
bool mafiaWins = (*mafia.begin()) > (*gang.begin());
as should
bool mafiaWins = mafia.front() > gang.front();
If it doesn't fix please post the exact error.
NB: the less than/greater than operator are defined for random access iterator for comparing position.
I have this struct:
struct MxMInstanceData
{
D3DXVECTOR2 mTransform;
float mSpacing;
};
Then I create a vector of MxMInstanceData:
std::vector<MxMInstanceData> instInFrustumData;
If I call instInFrustumData.clear() I get this error:
Assertion failed (vector iterators
incompatible)
Vector creation code:
instInFrustumData.reserve(mNumInstances);
Vector update code:
void Terrain::updateInstances()
{
mNumInstancesInFrustum = 0;
if(instInFrustumData.size() != 0)
instInFrustumData.clear();
mpMxMInstInFrustumB->Map(D3D10_MAP_WRITE_DISCARD, NULL, (void**) &instInFrustumData);
for(int x = 0; x < mNumInstances; x++)
{
if(mpCamera->point2DInFrustum(instData[x].mTransform +
D3DXVECTOR2(instData[x].mSpacing/2 + mpCamera->getPosition().x, instData[x].mSpacing/2 + mpCamera->getPosition().z), instData[x].mSpacing/2)
!= OUTSIDE)
{
instInFrustumData.push_back(instData[x]);
mNumInstancesInFrustum++;
}
}
mpMxMInstInFrustumB->Unmap();
}
What can make this happen?
And in the destructor of my class I also call clear()
You may want to check out a reference on using std::vector like http://www.cplusplus.com/reference/stl/vector/ or buy a good STL book. You are using some methods in what I would consider unorthodox ways.
Use empty() to check if a vector has elements (if not empty clear just reads better)
Use locally scoped variables when possible (things that don't need to stay in scope shouldn't)
Use STL iterators or container sizes in loops (is having two incrementing integers in one loop needed?)
Use the "best" STL container for your implementation (do you want vectors or maps here?)
Avoid C-style casts and misuse of objects ((void**) &instInFrustumData is a very bad idea)
You have so many members variables whose definition is unknown as well unknown methods Map() and UnMap() and still haven't shown any code using iterators related to your original error. I would guess your use of instData[x] is dangerous and problematic as well as the way that loop is constructed in general. You also really don't want to be treating STL containers as anything but STL containers. Things like (void**) &instInFrustumData should be avoided as they can only cause problems.
I highly suggest you learn C++ first before tackling DirectX or graphics and game engines written in both.
Kind of guessing here, but maybe your problem is this line:
mpMxMInstInFrustumB->Map(D3D10_MAP_WRITE_DISCARD, NULL, (void**) &instInFrustumData);
You're passing a pointer to the vector itself to this Map function, which I'm guessing might be overwriting some of its internals? I don't have its documentation, but it doesn't look like a function that's expecting a pointer to a vector :)
can anyone recommend a nice and tidy way to achieve this:
float CalculateGoodness(const Thing& thing);
void SortThings(std::vector<Thing>& things)
{
// sort 'things' on value returned from CalculateGoodness, without calling CalculateGoodness more than 'things.size()' times
}
Clearly I could use std::sort with a comparison function that calls CalculateGoodness, but then that will get called several times per Thing as it is compared to other elements, which is no good if CalculateGoodness is expensive. I could create another std::vector just to store the ratings and std::sort that, and rearrange things in the same way, but I can't see a tidy way of doing that. Any ideas?
Edit: Apologies, I should have said without modifying Thing, else it's a fairly easy problem to solve :)
I can think of a simple transformation (well two) to get what you want. You could use std::transform with suitable predicates.
std::vector<Thing> to std::vector< std::pair<Result,Thing> >
sort the second vector (works because a pair is sorted by it first member)
reverse transformation
Tadaam :)
EDIT: Minimizing the number of copies
std::vector<Thing> to std::vector< std::pair<Result,Thing*> >
sort the second vector
transform back into a secondary vector (local)
swap the original and local vectors
This way you would only copy each Thing once. Notably remember that sort perform copies so it could be worth using.
And because I am feeling grant:
typedef std::pair<float, Thing*> cached_type;
typedef std::vector<cached_type> cached_vector;
struct Compute: std::unary_function< Thing, cached_type >
{
cached_type operator()(Thing& t) const
{
return cached_type(CalculateGoodness(t), &t);
}
};
struct Back: std::unary_function< cached_type, Thing >
{
Thing operator()(cached_type t) const { return *t.second; }
};
void SortThings(std::vector<Thing>& things)
{
// Reserve to only allocate once
cached_vector cache; cache.reserve(things.size());
// Compute Goodness once and for all
std::transform(things.begin(), things.end(),
std::back_inserter(cache), Compute());
// Sort
std::sort(cache.begin(), cache.end());
// We have references inside `things` so we can't modify it
// while dereferencing...
std::vector<Thing> local; local.reserve(things.size());
// Back transformation
std::transform(cache.begin(), cache.end(),
std::back_inserter(local), Back());
// Put result in `things`
swap(things, local);
}
Provided with the usual caveat emptor: off the top of my head, may kill kittens...
You can have a call to CalculateGoodness that you call for each element before sorting, and then CalculateGoodness simply updates an internal member variable. Then you can sort based on that member variable.
Another possibility if you can't modify your type, is storing some kind of std::map for your objects and their previously calculated values. Your sort function would use that map which acts as a cache.
I've upvoted Brian's answer because it clearly best answers what you're looking for. But another solution you should consider is just write it the easy way. Processors are getting more powerful every day. Make it correct and move on. You can profile it later to see if CalculateGoodness really is the bottleneck.
I'd create pairs of ratings and things, calling CalculateGoodness once per thing, and sort that on the rating. if applicable you could also move this to a map from rating to thing
the other option would be to cache CalculateGoodness in the Thing itself either as a simple field or by making CalculateGoodness a method of Thing (making sure the cache is mutable so const Things still works)
Perhaps a tidy way of doing the separate vector thing is to actually create a vector< pair<float, Thing*> >, where the second element points to the Thing object with the corresponding float value. If you sort this vector by the float values, you can iterate over it and read the Thing objects in the correct order, possibly playing them into another vector or list so they end up stored in order.