Unable to access contents of initialized struct from a vector - c++

I have a struct:
typedef struct
{
Qt::Key qKey;
QString strFormType;
} KeyPair;
Now I initialize KeyPair instantiations so I could use it for my Automated Test App.
KeyPair gTestMenu[] =
{
{ Qt::Key_1 , "MyForm" },
{ Qt::Key_1 , "SubForm" },
{ Qt::Key_Escape, "DesktopForm" }
};
KeyPair gBrowseMenu[] =
{
{ Qt::Key_1 , "MyForm" },
{ Qt::Key_2 , "Dialog" },
{ Qt::Key_Escape, "DesktopForm" }
};
and like 100 more instantiations....
Currently, I call a function which uses these KeyPairs.
pressKeyPairs( gTestMenu );
pressKeyPairs( gBrowseMenu );
and more calls for the rest...
I would like to put all these KeyPair instantiations in a vector so I wouldn't have to call pressKeyPairs() a hundred times... I'm a newbie in using vectors... so I tried:
std::vector<KeyPair, std::allocator<KeyPair> > vMasterList;
vMasterList.push_back( *gTestMenu );
vMasterList.push_back( *gBrowseMenu );
std::vector<KeyPair, std::allocator<KeyPair> >::iterator iKeys;
for(iKeys = vMasterList.begin(); iKeys != vMasterList.end(); ++iKeys)
{
pressKeyPairs(*iKeys);
}
However, this code block isn't working... :( Can somebody tell me how to properly put these KeyPairs in a vector?

You've to use insert to populate the vector with your different arrays. Here is how you should do it.
//initialized with one array
std::vector<KeyPair> vMasterList(gTestMenu, gTestMenu + 3);
//adding more items
vMasterList.insert( vMasterList.end(), gBrowseMenu , gBrowseMenu + 3);
And then reimplement your pressKeyPair function, so that you can use std::for_each from <algorithm> header file as,
//pressKeyPair will be called for each item in the list!
std::for_each(vMasterList.begin(), vMasterList.end(), pressKeyPair);
Here is how you can write the pressKeyPair function:
void pressKeyPair(KeyPair &keyPair) //takes just one item!
{
//press key pair!
}
In my opinion, this is better design, as it doesn't need "manual" loop anymore at the calling site!
You can even call pressKeyPair for first 5 items in the list as,
//pressKeyPair will be called for first 5 items in the list!
std::for_each(vMasterList.begin(), vMasterList.begin() + 5, pressKeyPair);
One more example:
//pressKeyPair will be called for 5 items after the first 5 items, in the list!
std::for_each(vMasterList.begin()+5, vMasterList.begin() + 10, pressKeyPair);
EDIT:
If you want to use manual loop, then you've to use this:
std::vector<KeyPair>::iterator it;
for( it = vMasterList.begin(); it != vMasterList.end(); ++it)
{
pressKeyPair(*it);
}
But I would say it's not as elegant as the approach described earlier. Remember, this assumes that the function pressKeyPair has this signature:
void pressKeyPair(KeyPair &keyPair); //it doesn't accept array!

I think that the problem is that the code
vMasterList.push_back( *gTestMenu );
Only adds a single element of gTestMenu to the vector, namely the first. The reason is that this code is equivalent to the following:
vMasterList.push_back( gTestMenu[0] );
From which I think it's a bit easier to see what's going wrong.
To fix this, you probably want to add all of the elements in gTestMenu to the master list. You can do this using the three-parameter vector::insert function:
vMasterList.insert(v.begin(), // Insert at the beginning
gTestMenu, // From the start of gTestMenu...
gTestMenu + kNumTests); // ... to the end of the list
Here, you'll need to specify how many tests are in gTestMenu as kNumTests. You can do the same for gBrowseMenu.
By the way, you don't need to specify the allocator type in the vector declaration if you just want to use the default std::allocator. You can just write
std::vector<KeyPair> vMasterList;
And you'll be totally fine.

Related

How to get exactly one element from an unordered_set?

I have:
std::unordered_set<ObjectRepresentation*> incompletePieces;
I would like to get exactly one object from the unordered_set. To do that I am using a for loop, and "break", at the end of the loop so that the loop runs at most once.
while (incompletePieces.size()){
for (auto containedPiece : incompletePieces){ //Warning at this line that loop will run at most once
// .... doing some stuff with the contained piece
incompletePieces.erase(containedPiece);
break;
}
}
This is the desired behaviour that I want. The problem is that the compiler shows a warning:
Loop will run at most once (loop increment never executed)
How do I rewrite my code so that the warning goes away ? Is there a better way to get an item from the unordered_set ?
You could use begin() to get the first element.
if (incompletePieces.size() > 0)
auto containedPiece = *(incompletePieces.begin());
The code you presented does in fact process all elements and clears the set of them as it gets done, but it does so in a highly unidiomatic way.
There are two idiomatic ways of doing this, depending on whether processing an element could modify the set itself.
1) If the "doing some stuff" code is guaranteed to not touch incompletePieces (i.e. completing one piece does not create additional incomplete pieces), then the idiomatic and efficient solution is to just loop over the set and clear it afterwards:
for (auto piece : incompletePieces) {
// process piece
}
incompletePieces.clear();
2) If this is not the case, or you really need to clear elements as you go, then the idiomatic solution is still iterator based looping:
auto it = incompletePieces.begin();
while (it != incompletePieces.end()) {
// process *it
#if C++11
it = incompletePieces.erase(it);
#else
auto prev = it++;
incompletePieces.erase(prev);
#endif
}
Whereas *unordered_set::begin() will give you first element (no unordered_set::front()),
I would rewrite:
while (incompletePieces.size()){
for (auto containedPiece : incompletePieces){
// .... doing some stuff with the contained piece
incompletePieces.erase(containedPiece);
break;
}
}
into:
for (auto* containedPiece : incompletePieces){
// .... doing some stuff with the contained piece
}
incompletePieces.clear();
You can rewrite the code as below:
for(auto* containedPiece : incompletePieces){
//Process the set contents
}
//Clear entire set in one go
incompletePieces.clear();
If you want to clear it one by one, you would have to use iterators as shown below:
auto it = incompletePieces.begin(); //Take the pointer to first element of set
for( ; it !=incompletePieces.end() ; it++){
incompletePieces.erase(*it); //Erase one element at a time
}

Modifying set while iterating gives me segfault

I am currently working on a function working with a vector of sets of int.
I want my function merge( ) to merge all sets that share an int in common, so for example I want this to happen :
[0] - 0, 1, 2
[1] - 1, 3 Then it will [0] - 0, 1, 2, 3
[2] - 0, 3 output this vector -> [1] - 4, 5
[3] - 4, 5 [2] - 6, 7, 8, 9
[4] - 6, 7, 8
[5] - 8, 9
I have already written this function, of which code is presented down here.
I have commented almost every line so that it is not too difficult to understand my code !
// Merges all sets that shares at least one int
//
// PARAMETERS...
// vectorE : vector of sets of int
void mergeStates( std::vector< std::set< int > >& vectorE )
{
// For every set of ints
for( auto &currentSet : vectorE )
{
// For every ints of the set
for( auto currentInt : currentSet )
{
// The two for( ) loops down there allow me to iterate over
// every int of every set of the vectorE
for( auto setToCheck : vectorE )
{
// If the set is different from the one we're already targeting
if( currentSet != setToCheck )
{
for( auto intToCheck : setToCheck )
{
// if we have found an int that is the same as the one we're targeting
if( intToCheck == currentInt )
{
// Merge
etatsetEtudie.insert( setToCheck.begin(), setToCheck.end() );
// Deleting the set we copied from, because we won't need it anymore
for(auto setToErase = vectorE.begin() ; setToErase != vectorE.end() ; ){
if( *setToErase == setToCheck )
setToErase = vectorE.erase( setToErase );
else
++setToErase;
}
}
}
}
}
}
}
}
Every time I run my program, I get a segfault when it comes to deleting the set we copied from : where is my error?
Edit : I got it to work !
Alright, thanks guys I simply made my parameter const and added a return value so that I can add dynamically every constructed set I need to a new vector, and return this vector :-)
The problem isn't modifying any set, it's modifying the vector.
Erasing something from a vector shifts the elements after it. First, this means iterators into the vector after the erased position (the for-range loop uses iterators internally) are no longer valid. Second, if the shifting copied and overwrote sets (instead of moving them), all your iterators into the sets will no longer be valid.
The result is lots of undefined behavior in your code.
Also, your innermost loop is not a good way to go about erasing the set, even if the method was valid. It's very, very inefficient.
You need to rethink, at the very least, the way you're erasing elements. But I think that coming up with a generally better algorithm would be the better approach.
Try to make a new vector instead of modifying the original one:
std::vector<std::set<int>> mergeStates(const std::vector<std::set<int>> & vectorE ) {
std::vector<std::set<int>> new_vector;
...
return new_vector;
}
You are using the std::vector::erase function which invalidates iterators. Consequently your code inside the range based for loop tries to access the iterator past the container end.
The end iterator used by the range-based for is determined prior to the loop. Since you erase() during the iteration, the end actually changes. Obtaining the iterator from the result of the erase() is insufficient as the end also changed. I think you can get away not using range-based for loops for the ranges you erase from.

RemoveAt from StructArray Ue4

I struggle a bit with deleting struct from my TArray of structs.My struct contains AudioComponent and float.I was using Array.RemoveAt(index), but what i got from this was only removing half of my struct, which is AudioComponent.
Why is that? My function Removing elements looks like this:
void RemoveArrayElement( UAudioComponent AudioComponent )
{
for( int i=0; i<Array.Num(); i++ )
{
if( AudioComponent == Array[i].AudioComponent )
{
Array.RemoveAt( i );
}
}
}
What i want to achieve is completely deleting index, AudioComponent with it's float.
There are few issues with your code. As others mentioned in comments, you should use pointers. And if I'm not mistaken, you aren't allowed to use construction like this:
UPROPERTY()
TArray<UAudioComponent> invalidArray;
You should use UPROPERTY macro, otherwise your properties could and probably will be garbage collected. UPROPERTY wiki.
Next thing is that you are changing array over which you are iterating. I wrote few approaches, let's look at them:
void RemoveArrayElement(UAudioComponent* AudioComponent)
{
TArray<UAudioComponent*> audioArray; // array will be initialized somewhere else, this is for demo purpose.
// you always should check your pointers for validity
if (!AudioComponent || !AudioComponent->IsValidLowLevel() || AudioComponent->IsPendingKill())
return;
// Correct approach 1 (multiple):
TQueue<UAudioComponent*> toDelete;
for (int i = 0; i < audioArray.Num(); i++)
{
auto item = audioArray[i];
if (AudioComponent == item || true) // we simulate another condition for multiselect
{
toDelete.Enqueue(item);
}
}
// better approach for iteration:
for (auto item : audioArray)
if (item == AudioComponent || true) // we simulate another condition for multiselect
toDelete.Enqueue(item);
// finalize deletion in approach 1
UAudioComponent* deleteItem;
while (toDelete.Dequeue(deleteItem))
audioArray.Remove(deleteItem);
// almost correct approach 2 (single) :
UAudioComponent* foundItem;
for (auto item : audioArray)
if (item == AudioComponent)
{
foundItem = item;
break; // we can skip rest - but we must be sure, that items were added to collection using AddUnique(...)
}
if (foundItem)
audioArray.Remove(foundItem);
// correct and the best - approach 3 (single)
audioArray.Remove(AudioComponent);
}
First keep in mind that comparing two objects does not necessarily lead to the expected result of equality. Using the == operator means executing a function (bool operator==(L, R);) that specifies what should happen. So if you did not overload the == operator then you don't know what using it would result to unless you look at the source code where it's defined. Since you want to remove the exact audio component and not an instance of it that looks the same, you want to use pointers in your array. That also helps performance since your are not copying the whole component when calling RemoveArrayElement(...); but a single pointer. Also when there are two identical audio components stored in the array and they are at index a and a+1, then removing the audio component at index a the next iteration would skip your second audio component since all upper indexes are decremented by one.

Unity / C#: using lists to store numbers, delete elements

I have been banging my head on a problem related to lists. I am a beginner, so sorry if this is a bit unclear..
My goal is to be able to write numbers from keyboard inputs, that will be displayed in a UI element in Unity.
To do so i decided to use a list, because i was wanting to add control to the display, (for example, to add a "." every 3 digits so that they would be more easily readable, like "3.489.498").
So basically, i store new inputs in this list, then i display this list with a display.text every time there is a new digit as an input.
This works actually very well, but then i wanted to be able to delete the last typed element. So i added a backspace hotkey with a List.Remove().
And this is where the nightmare starts. The thing seems to work when i press "1" and deletes right after, but for some reasons it does not work with 2.
Error message is : "Argument out of range, parameter name: index."
I just can't wrap my head around this problem :(
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
public class BoxCreateNumber : MonoBehaviour {
public Text textDisplayNumber;
public List<int> numberList = new List<int>();
void Start () {
}
void Update () {
CollectingNumberInput ();
}
void CollectingNumberInput(){
if (Input.GetKeyDown(KeyCode.Keypad1)){
numberList.Add (1);
//numberList.Insert (numberList.Count,1);
DisplayNumber ();
} else if (Input.GetKeyDown(KeyCode.Keypad2)) {
numberList.Add (2);
//numberList.Insert (numberList.Count,2);
DisplayNumber ();
} else if (Input.GetKeyDown(KeyCode.Backspace)) {
numberList.Remove(numberList.Count);
DisplayNumber ();
}
}
void DisplayNumber(){
textDisplayNumber.text = "";
for (int i = 0; i <= numberList.Count; i++) {
textDisplayNumber.text += numberList [i];
}
}
}
You just need to read the documentation.
public bool Remove(
T item
)
Parameters
item - The object to remove from the List. The value can be null for reference types.
Instead of passing the function the object to remove, you pass the number of elements in the list. This means that if the list contains element "1" as its only element, then it will work, but only by accident.
Calling RemoveAt(numberList.Count - 1) will do what you want. RemoveAt takes the index of the element to remove, and the indices are 0-based, so the last one is Count-1.
Try this to remove the last element
numberList.RemoveAt(numberList.Count-1);

Can I put a lua_table in another table to build a multi-dimensional array via C++?

for my game im trying to accomplish the following array structure by C++ because the data come from an external source and should be available in a lua_script.
The array structure should look like this: (The data are in a map, the map contains the name of the variable and a list of Pairs (Each pair is a key value pair considered to be one element in one subarray)...
The data prepared in the map are complete and the structure is definetly okay.
So basically I have
typedef std::map<std::string, std::list<std::pair> >;
/\index(e.g: sword) /\ /\
|| ||
|| Pair: Contains two strings (key/value pair)
||
List of Pairs for each array
items = {
["sword"] = {item_id = 1294, price = 500},
["axe"] = {item_id = 1678, price = 200},
["red gem"] = {item_id = 1679, price = 2000},
}
What I got so far now is:
for(ArrayMap::iterator it = this->npc->arrayMap.begin(); it != this->npc->arrayMap.end(); it++) {
std::string arrayName = (*it).first;
if((*it).second.size() > 0) {
lua_newtable(luaState);
for(ArrayEntryList::iterator itt = (*it).second.begin(); itt != (*it).second.end(); itt++) {
LuaScript::setField(luaState, (*itt).first.c_str(), (*itt).second.c_str());
}
lua_setglobal(luaState, arrayName.c_str());
}
}
But this will only generate the following structure:
(table)
[item_id] = (string) 2000
[name] = (string) sword
[price] = (string) 500
The problem is that the table can ofcourse only contain each index once.
Thatswhy I need something like "a table in a table", is that possible?
Is there a way to achieve this? Im glad for any hints.
So from what I understand, if you have two "sword", then you can not store second one? If that's the case, you are doing it wrong. The key of map should be unique and if you decide that you are going to use std::map to store your items then your external source should provide unique keys. I used std::string as key in my previous game. Example:
"WeakSword" -> { some more data }
"VeryWeakSword" -> { some more data }
or, with your data (assuming item_ids are unique) you can get something like following from external source:
1294 -> { some more data }
1678 -> { some more data }
I'm not sure how efficient is this but I wasn't programming a hardware-hungry 3D bleeding-edge game so it just did a fine job.
The data structure that you are using also depends on the how you are going to use it. For example, if you are always iterating through this structure why don't you store as follows:
class Item {public: ... private: std::string name; int id; int value;}
std::vector<Item> items // be careful tho, std::vector copies item before it pushes
Extract(or Parse?) the actual value you want from each entity in external source and store them in std::vector. Reaching the middle of std::vector is expensive, however, if your intention is not instant accessing but rather iterating over data, why use map? But, if your intention is actually reaching a specific key/value pair, you should alter your external data and use unique keys.
Finally, there is also another associative container that stores non-unique key/value pairs called std::multimap but I really doubt you really need it here.