Class variable resets after function completes - c++

I'm new to C++ from a java/python/etc background, and am trying to teach myself OO programming before I have to take a class on it next semester.
I'm trying to make an animation system using SFML, but am having some trouble with one of my class variables; it keeps resetting to 0 after I increment it. I'll start with the code and follow with a log output I'm using to help figure out what's going on.
SOLUTION: Being new to C++, I was an idiot and returned a new instance of my class in my getter functions; using [class]& func()... instead of [class] func() solved this, but now I have some refactoring to do.
Code (header):
...
typedef std::vector<Frame> frameVect; // (Frame defined above)
typedef std::vector<double> dubVect;
...
class limbAnim
{
private:
int limbNum;
int numFrames;
int curFrame;
frameVect frames;
public:
limbAnim(int limb, int nFrames, frameVect F);
<getters/setters>
void incCurFrame();
dubVect incrementAnimation(dubVect curPos, double curRot);
}
Code (cpp):
... (include vector, ofstream, etc)
std::ofstream AnimLog("log.log")
typedef std::vector<Frame> frameVect; // (Frame defined above)
typedef std::vector<double> dubVect;
...
limbAnim::limbAnim(int limb, int nFrames, frameVect F)
{
limbNum = limb;
curFrame = 0;
numFrames = nFrames;
frames = F;
}
void limbAnim::incCurFrame()
{
curFrame=curFrame+1;
if (curFrame >= numFrames)
{
curFrame = 0;
AnimLog << "Greater than." << std::endl;
}
}
dubVect limbAnim::incrementAnimation(dubVect curPos, double curRot)
{
AnimLog << limbNum << ", " << numFrames << std::endl;
if (numFrames > 0)
{
AnimLog << curFrame << std::endl;
dubVect curStepP = frames[curFrame].getStepPos();
double curStepR = frames[curFrame].getStepRot();
curPos[0] = curPos[0] + curStepP[0];
curPos[1] = curPos[1] + curStepP[1];
curRot = curRot + curStepR;
incCurFrame();
AnimLog << "Incremented: " << curFrame << std::endl;
}
dubVect retV = curPos;
retV.push_back(curRot);
return retV;
}
So, my log output looks good since I'm testing with 2 frames on limbs 6 & 8, except those limbs' curFrame seems to reset to 0 after incrementing:
...
5, 0
6, 2
0
Incremented: 1
7, 0
8, 2
0
Incremented: 1
9, 0
...
5, 0
6, 2
0
Incremented: 1
7, 0
8, 2
0
Incremented: 1
9, 0
...(ad nauseam)
Edit: Code calling the increment function.
(main.cpp)
// (Outside main loop.)
Animation walk_anim(12, "assets/anim/walk.dat");
// (Inside main loop.)
for (int i=0; i<12; i++)
{
dubVect animDat = walk_anim.getLimbFrame(i).incrementAnimation(limbPos[i], curDegs[i]);
dubVect newPos = getDVect(animDat[0], animDat[1]);
double newRot = animDat[2];
curDegs[i] = newRot;
if (curDegs[i] >= 360)
curDegs[i] -=360;
limbPos[i] = newPos;
}
getLimbFrame
Animation::Animation(int lNum, string fName)
{
numLimbs = lNum;
fileName = fName;
// Fill up limbVect with correct # of empty frames.
for (int i=0; i<numLimbs; i++)
{
frameVect emptyFVect;
limbAnim LA(i, 0, emptyFVect);
limbFrames.push_back(LA);
}
// Boring .dat parsing, populates the 'frames' var of each limbAnim.
loadAnim();
}
limbAnim Animation::getLimbFrame(int index)
{
if (index < numLimbs)
{
return limbFrames[index];
}
}

Hopefully you're aware that your functions take arguments by value, so they work on a copy of something.
You carefully avoided to show the really interesting code parts where you call incrementAnimation, most likely it follows the same bad pattern as the other functions.
I suggest reading up on how to pass objects by reference and const reference -- and how function arguments work in C++.

I think you need to declare that member variable, with the static keyword, then you can say it is a class variable, where it will be the shared for every instance of your class. Like this:
static int curFrame;
Then you need to initialize it from outside de class. Have in mind that declaration, is a lot different to initialization.
You can read about it here and here

Related

default argument overwriting c++

i have a problem that when you make an object and enter arguments into it, for example foo f(0,5,0); the objects default arguments take over and replace the arguemnts i entered when making the object. I hope this makes sense and feel free to leave a comment if I need to change something.
code:
yo.h
class yo {
public:
yo(int Y=0, int Speed=1);
void move(); //where i use the varibles
int getx() { return x;}
private:
int x, y, speed;
};
yo.cpp
yo::yo(int Y, int Speed)
:x(1280), y(Y), speed(Speed)
{}
void yo::move() {
x -= speed;
std::cout << speed << std::endl;
}
main.cpp
std::vector<yo> yos(20);
void spawnYos() {
yo e(340, 5);
yos.push_back(e);
}
int main() {
spawnYos();
while(true) {
for (int i = 0; i != yos.size(); i++) {
yos.at(i).move();
if (yos.at(i).getx() < 0 or yos.at(i).getx() > 1290) {
yos.erase(yos.begin() + i);
}
}
}
return 0;
}
this yos.at(i).move(); prints the default argument (1) while I spesificly said yo e(340, 5); . it should be printing 5.
You start by default constructing 20 yos:
std::vector<yo> yos(20);
All 20 have x = 1280, y = 0 and speed = 1.
You then add one extra with the value you want:
yo e(340, 5);
yos.push_back(e);
You now have 20 default constructed yos + 1 with x = 1280, y = 340 and speed = 5.
From your description, I think you want 20 yo(340, 5) elements. You can create those by adding a yo to copy when constructing your vector of 20 elements:
std::vector<yo> yos(20, {340, 5});
spawnYos() can now be removed and the declaration of yos can be moved into main instead of having a global variable.
Also correct the stepping of i in your for loop. If you erase an element, you will miss the i != yos.size() condition or miss to move() the next element after the erased if you do i++ unconditionally.
Example:
int main() {
std::vector<yo> yos(20, {340, 5});
while(true) {
for (size_t i = 0; i != yos.size();) {
yos.at(i).move();
if (yos.at(i).getx() < 0 or yos.at(i).getx() > 1290) {
yos.erase(yos.begin() + i);
} else {
++i; // only step i if you don't remove an element
}
}
}
return 0;
}
Note: This program has no way of terminating normally. Such programs have undefined behavior so you need to give the program some clean way of exiting. I suggest changing
while(true) { ... }
to
while(!yos.empty()) { ... }

c++ String returning with an extra char

I have tested my program and am certain right before being returned the string in my function equals "card001". But the returned value equals "card0011". I have no idea how this even happens. Help me before I lose my mind. ;)
std::string function_cardTexture(int card) {
//removes the last 1
card = card - 10000;
int ctr = 0;
card = floor(card / 10);
std::cout << card << std::endl;
//turn int card into a string
std::string a = static_cast<std::ostringstream*>(&(std::ostringstream() << card))->str();
//combines card and string a into one string
std::string nametext = "card00" + a;
std::cout << nametext << std::endl;
return (nametext);
}
void function_Battle(tempPlayer &Player, tempCard &card001) {
if (Player.Start == true) {
//Draw hand
for (int i = 0; i < Player.numDrawn; i++) {
int x = rand() % Player.deckSize + 0; ;
Player.Hand[i] = Player.Deck[x];
Player.Discarded[x] = 1;
}
Player.Start = false;
}
std::map<std::string, tempCard> Vars;
//draw hand
for (int i = 0; i < Player.handMax;i++) {
if (Player.Hand[i] != 0) {
sf::RectangleShape Card(sf::Vector2f(80.0f, 128.0f));
std::string nametext = function_cardTexture(Player.Hand[i]);
std::cout << nametext;
sf::Texture texture = Vars[nametext].Art;
Card.setTexture(&texture);
window.draw(Card);
}
}
}
Your problem is how you're printing things out without a newline in the function_Battle() function, so you're likely "smashing together" your new value with an old one. If you replace your main function with just a loop with clearer printing of values, you can see you don't have a problem:
http://coliru.stacked-crooked.com/a/8d1e4f51643b84b9
That link will go to an online compiler where I just replaced the calling function with a loop that makes numbers. It even supplies a negative one.

Retrieving data from vector from a fresh instance returns 0

I am creating an instance after updating records to the vector vecData inside counter.cpp when I am trying to retrieve this data I am getting the value as 0
main.cpp
#include <iostream>
#include "counter.hpp"
#define MAX_PORTS 3
int main()
{
Counter *counter = new Counter(3);
counter->age = 1;
counter->id = 12;
counter->pincode = 123;
std::vector<Counter> counterData(MAX_PORTS);
counter->storeCounterData(counter,1);
counter->storeCounterData(counter,2);
counter->storeCounterData(counter,3);
counter->getCounterData(counterData);
// getCounterData here is not able to retrieve value updated in above vector `vecData`
Counter *count = new Counter(3);
count->getCounterData(counterData);
return 0;
}
counter.cpp
#include "counter.hpp"
#include <iostream>
#include <vector>
Counter::Counter()
{
}
Counter::Counter(int vecSize):vecData(vecSize)
{
}
void Counter::storeCounterData(Counter *counter,int user)
{
vecData[user].age = counter->age;
vecData[user].id = counter->id;
vecData[user].pincode = counter->pincode;
std::cout<<"vector size inside storeCounterData = "<<vecData.size()<<std::endl;
}
void Counter::getCounterData(std::vector<Counter> &counter)
{
counter = vecData;
std::cout<<"vector size inside getCounterData = "<<vecData.size()<<std::endl;
std::vector<Counter>::iterator it;
for( it = vecData.begin(); it != vecData.end(); ++it)
{
std::cout<<vecData[1].age<<std::endl;
std::cout<<vecData[1].id<<std::endl;
std::cout<<vecData[1].pincode<<std::endl;
}
}
counter.hpp
#include <vector>
class Counter
{
private:
std::vector<Counter> vecData; // vectors
public:
Counter();
Counter(int vecSize);
int age, id, pincode;
void storeCounterData(Counter *counter,int user);
void getCounterData(std::vector<Counter> &counter);
};
Output
./a.out vector size inside storeCounterData = 3
vector size inside storeCounterData = 3
vector size inside storeCounterData = 3
vector size inside getCounterData = 3
1 12 123
1 12 123
1 12 123
vector size inside getCounterData = 3
0 0 0
0 0 0
0 0 0
This is C++, don't use macros for constants:
#define MAX_PORTS 3
Use a const variable instead:
const int max_ports = 3;
Why are you creating this variable on the heap?
Counter *counter = new Counter(3);
counter->age = 1;
counter->id = 12;
counter->pincode = 123;
C++ is not Java or C#, don't do that. You are failing to delete those variables, so you leak memory. Don't do that. This is the correct way:
Counter counter(3);
counter.age = 1;
counter.id = 12;
counter.pincode = 123;
Then either change setCounterData to take a reference, or pass in the address of the object:
counter->storeCounterData(&counter,1);
You are indexing into the vector with the values 1, 2 and 3, but the vector only has three elements, so the correct indices are 0, 1, and 2. Doing vecData[user] when user==3 and vecData.size()==3 is a serious bug. Don't do that.
Why are you always accessing vecData[1] instead of using the iterator?
std::vector<Counter>::iterator it;
for( it = vecData.begin(); it != vecData.end(); ++it)
{
std::cout<<vecData[1].age<<std::endl;
std::cout<<vecData[1].id<<std::endl;
std::cout<<vecData[1].pincode<<std::endl;
}
You probably meant this:
std::vector<Counter>::iterator it;
for( it = vecData.begin(); it != vecData.end(); ++it)
{
std::cout<<it->age<<std::endl;
std::cout<<it->id<<std::endl;
std::cout<<it->pincode<<std::endl;
}
Or much simpler:
for (auto& c : vecData)
{
std::cout << c.age << std::endl;
std::cout << c.id << std::endl;
std::cout << c.pincode << std::endl;
}
(And notice how much easier it is to read that with spaces around the << operators).
when I am trying to retrieve this data I am getting the value as 0
The reason you always get 0 printed is because you are getting the data from an un-filled vector that just contains three new Counter objects:
Counter *count = new Counter(3);
count->getCounterData(counterData);
You haven't stored any values in count->vecData[1] so the values you print out are just the default values created by the Counter constructor, which fails to initialize its members:
Counter::Counter()
{
}
This means the member variables are just garbage. You happen to get zeros because your program has only just started so the heap happens to be full of zeros, but it's actually undefined behaviour to read those variables.
Your Counter class needs to initialize those members properly:
class Counter
{
private:
std::vector<Counter> vecData; // vectors
public:
Counter();
Counter(int vecSize);
int age = 0;
int id = 0;
int pincode = 0;
void storeCounterData(Counter *counter,int user);
void getCounterData(std::vector<Counter> &counter);
};

How to limit a decrement?

There is a initial game difficulty which is
game_difficulty=5 //Initial
Every 3 times if you get it right, your difficulty goes up to infinity but every 3 times you get it wrong, your difficulty goes down but not below 5. So, in this code for ex:
if(user_words==words) win_count+=1;
else() incorrect_count+=1;
if(win_count%3==0) /*increase diff*/;
if(incorrect_count%3==0) /*decrease difficulty*/;
How should I go about doing this?
Simple answer:
if(incorrect_count%3==0) difficulty = max(difficulty-1, 5);
But personally I would wrap it up in a small class then you can contain all the logic and expand it as you go along, something such as:
class Difficulty
{
public:
Difficulty() {};
void AddWin()
{
m_IncorrectCount = 0; // reset because we got one right?
if (++m_WinCount % 3)
{
m_WinCount = 0;
++m_CurrentDifficulty;
}
}
void AddIncorrect()
{
m_WinCount = 0; // reset because we got one wrong?
if (++m_IncorrectCount >= 3 && m_CurrentDifficulty > 5)
{
m_IncorrectCount = 0;
--m_CurrentDifficulty;
}
}
int GetDifficulty()
{
return m_CurrentDifficulty;
}
private:
int m_CurrentDifficulty = 5;
int m_WinCount = 0;
int m_IncorrectCount = 0;
};
You could just add this as a condition:
if (user words==words) {
win_count += 1;
if (win_count %3 == 0) {
++diff;
}
} else {
incorrect_count += 1;
if (incorrect_count % 3 == 0 && diff > 5) {
--diff
}
}
For example:
if(win_count%3==0) difficulty++;
if(incorrect_count%3==0 && difficulty > 5) difficulty--;
This can be turned into a motivating example for custom data types.
Create a class which wraps the difficulty int as a private member variable, and in the public member functions make sure that the so-called contract is met. You will end up with a value which is always guaranteed to meet your specifications. Here is an example:
class Difficulty
{
public:
// initial values for a new Difficulty object:
Difficulty() :
right_answer_count(0),
wrong_answer_count(0),
value(5)
{}
// called when a right answer should be taken into account:
void GotItRight()
{
++right_answer_count;
if (right_answer_count == 3)
{
right_answer_count = 0;
++value;
}
}
// called when a wrong answer should be taken into account:
void GotItWrong()
{
++wrong_answer_count;
if (wrong_answer_count == 3)
{
wrong_answer_count = 0;
--value;
if (value < 5)
{
value = 5;
}
}
}
// returns the value itself
int Value() const
{
return value;
}
private:
int right_answer_count;
int wrong_answer_count;
int value;
};
And here is how you would use the class:
Difficulty game_difficulty;
// six right answers:
for (int count = 0; count < 6; ++count)
{
game_difficulty.GotItRight();
}
// check wrapped value:
std::cout << game_difficulty.Value() << "\n";
// three wrong answers:
for (int count = 0; count < 3; ++count)
{
game_difficulty.GotItWrong();
}
// check wrapped value:
std::cout << game_difficulty.Value() << "\n";
// one hundred wrong answers:
for (int count = 0; count < 100; ++count)
{
game_difficulty.GotItWrong();
}
// check wrapped value:
std::cout << game_difficulty.Value() << "\n";
Output:
7
6
5
Once you have a firm grasp on how such types are created and used, you can start to look into operator overloading so that the type can be used more like a real int, i.e. with +, - and so on.
How should I go about doing this?
You have marked this question as C++. IMHO the c++ way is to create a class encapsulating all your issues.
Perhaps something like:
class GameDifficulty
{
public:
GameDifficulty () :
game_difficulty (5), win_count(0), incorrect_count(0)
{}
~GameDifficulty () {}
void update(const T& words)
{
if(user words==words) win_count+=1;
else incorrect_count+=1;
// modify game_difficulty as you desire
if(win_count%3 == 0)
game_difficulty += 1 ; // increase diff no upper limit
if((incorrect_count%3 == 0) && (game_difficulty > 5))
game_difficulty -= 1; //decrease diff;
}
inline int gameDifficulty() { return (game_difficulty); }
// and any other access per needs of your game
private:
int game_difficulty;
int win_count;
int incorrect_count;
}
// note - not compiled or tested
usage would be:
// instantiate
GameDiffculty gameDifficulty;
// ...
// use update()
gameDifficulty.update(word);
// ...
// use access
gameDifficulty.gameDifficulty();
Advantage: encapsulation
This code is in one place, not polluting elsewhere in your code.
You can change these policies in this one place, with no impact to the rest of your code.

Can I have a single-dimension array of booleans that skip indexes?

I'm pretty new to Arduino programming, but am getting the hang of it. I've got a pretty strong PHP & Javascript background, so that's where I'm coming from.
I've been working on a project with an LCD touchscreen, with grids of buttons that I expect other developers to customize to their needs. There's an array that a developer can freely add items to, and additional pages of buttons are added dynamically. Clicking one simply sends that button's index to a receiver.
Now I want to make some buttons "sticky" so they stick on until turned off. My idea was to set up a simple array of booleans, like so:
boolean stickyTriggers[1] = 1;
boolean stickyTriggers[2] = 0;
boolean stickyTriggers[3] = 0;
boolean stickyTriggers[9] = 1;
What I'd expect is that buttons 1, 2, 3, and 9 are sticky, and buttons 1 and 9 are "on" by default. I figured when I'm rendering the buttons, I can simply say something like:
if (stickyTriggers[i]) {
// highlight button
}
The error I'm getting on compile is:
array must be initialized with a brace-enclosed initializer
I sort of realize I'm re-declaring the variable 3 times here, but I've tried other ways and got other errors.
What is the correct way to hold a simple array of flags? I want to be able to look up a key and get a true or false (or null), without having to hard-code an array length. I'm sure it's all possible, but is there a simple way?
Thanks
Clarification: The key here is that triggers 4-8 are NOT in the stickyTriggers array at all, since they are NOT sticky. I want to be able to skip indexes, or use arbitrary IDs as keys, as the title suggests. Most of the answers miss this.
I think you need somethig like this
#include <iostream>
using namespace std;
int main()
{
bool b[5] = {true, false, true, false, true};
cout << "Hello World" << endl;
for (int i=0; i < 5; i++) {
if (b[i]) {
cout<< "Index " << i << " is true" <<endl;
} else {
cout<< "Index " << i << " is false"<<endl;
}
}
return 0;
}
Try this:
#define ARRAY_SIZE 10
#define BOOL unsigned int
#define TRUE 1
#define FALSE 0
int main()
{
BOOL stickyTriggers[ARRAY_SIZE] = { FALSE };
stickyTriggers[1] = TRUE ;
stickyTriggers[9] = TRUE ;
return 0;
}
Yes, you are re-defining the array several times.
In C++, you may want to use bitset, check this link
http://en.cppreference.com/w/cpp/utility/bitset
You can try vector (which can dynamically change its size) too, like this
#include<iostream>
#include<vector>
using namespace std;
enum trigger_status {ON, OFF, NON_STICKY};
int main(){
vector<trigger_status> sticky_triggers(251, trigger_status::OFF); // you can add element to it dynamically, default all to off
sticky_triggers[0] = trigger_status::ON;
sticky_triggers[9] = trigger_status::ON;
sticky_triggers.push_back(trigger_status::ON); // sticky_triggers[251] = true, now u have 252 elements
}
Maybe I should have left out the C++ tag, to only invite Arduino-specific solutions.
But here's what I found, which solves my problem:
Alexander Brevig's HashMap Library:
http://playground.arduino.cc/Code/HashMap
As discussed here: http://forum.arduino.cc/index.php?PHPSESSID=q7rt05n43aa4enp6hepri50pi1&topic=42114.msg305435#msg305435
#include <HashMap.h>
const byte HASH_SIZE = 5;
HashType<int,boolean> hashRawArray[HASH_SIZE];
HashMap<int,boolean> hashMap = HashMap<int,boolean>(hashRawArray, HASH_SIZE);
void setup() {
Serial.begin(9600);
hashMap[0](1,true);
hashMap[1](2,false);
hashMap[2](3,false);
hashMap[3](4,false);
hashMap[4](9,true);
Serial.println(hashMap.getValueOf(1));
Serial.println(hashMap.getValueOf(2));
Serial.println(hashMap.getValueOf(9));
}
I can add a quick wrapper to add items to the hashMap without having to hard-code the index, but this gives me an easy way to set up an associative array of booleans, using arbitrary integers as the keys.
Thanks for trying everyone, sorry none of the answers worked on Arduino.
You might be able to use this it does not quite fulfil your desire to have no references to non-sticky triggers but it is fast and only uses 64 bytes of memory to map out 256 triggers.
I already had it in single boolean mode so I adapted it to make two maps and renamed it to suit your question..
Basically it is a bitmap.
Compiles and tests run OK for Arduino Duo.
// bitmapped booleans
const int IntBits = 16;
const int NumTriggers = 256;
const int idxSticky = 0;
const int idxDown = 1;
unsigned int TriggerMap[NumTriggers/IntBits][2];
void setup() {
Serial.begin(9600);
clearTriggerMap; // set all to not sticky and not down
// tests
setStickyTrigger(1, true, true);
setStickyTrigger(2, true, false);
setStickyTrigger(3, true, false);
setStickyTrigger(9, true, true);
setStickyTrigger(30, true, true);
setStickyTrigger(128, true, true);
setStickyTrigger(255, true, true);
}
void loop() {
// tests
Test(0);
Test(1);
Test(2);
Test(3);
Test(9);
Test(30);
Test(128);
Test(255);
delay(5000);
}
void Test( int ATrigger) {
// testing
if (IsStickyTrigger(ATrigger)) {
Serial.print( "Trigger ");
Serial.print(ATrigger);
Serial.print(" is sticky");
if (IsStickyTriggerDown(ATrigger)) {
Serial.print(" and it is down");
}
}
Serial.println();
}
void clearTriggerMap() {
for (int i = 0; i < NumTriggers/IntBits; i++) {
for (int j = 0; j < 2; j++){
TriggerMap[i][j] = 0;
}
}
}
void setStickyTrigger(int AIndex, boolean ASticky, boolean IsDown) {
unsigned int o;
unsigned int b = 1;
o = AIndex / IntBits;
b = b << (AIndex % IntBits);
if (ASticky) {
TriggerMap[o][idxSticky] = TriggerMap[o][idxSticky] | b;
} else {
b = ~b;
TriggerMap[o][idxSticky] = TriggerMap[o][idxSticky] & b;
}
if (IsDown) {
TriggerMap[o][idxDown] = TriggerMap[o][idxDown] | b;
} else {
b = ~b;
TriggerMap[o][idxDown] = TriggerMap[o][idxDown] & b;
}
}
boolean IsStickyTrigger(int AIndex) {
unsigned int b = 1;
b = b << (AIndex % IntBits);
return (TriggerMap[AIndex / IntBits][idxSticky] & b) != 0;
}
boolean IsStickyTriggerDown(int AIndex) {
unsigned int b = 1;
b = b << (AIndex % IntBits);
return (TriggerMap[AIndex / IntBits][idxDown] & b) != 0;
}