Sorry if some of my source looks really bad. This is my first attempt writing Arduino code as well as c++. I usually stay in my comfort zone of c#.
I'm getting the following errors when trying to run my source on a Tinkercad circuit. The way Tinkercad spits out errors is horrible for learning. Anyone who might be able to point out my mistakes that it is having a fit with would be very helpful.
Errors
- 2:23: error: use of enum 'LedActionTypes' without previous
declaration
- 4:23: error: use of enum 'SignalTypes' without previous declaration
- 4:64: error: use of enum 'SignalDirections' without previous declaration
- 5:23: error: use of enum 'SignalTypes' without previous declaration
- 5:64: error: use of enum 'SignalDirections' without previous declaration
- 5:103: error: use of enum 'DigitalSignalValues' without previous declaration
- 8:16: error: variable or field 'AddAction' declared void
- 8:16: error: 'LedAction' was not declared in this scope
My source code
/* Enums */
enum SignalTypes
{
General = 0,
Analog = 1,
Digital = 2,
};
enum SignalDirections
{
Inbound = 0x0,
Outbound = 0x1,
};
enum DigitalSignalValues
{
Low = 0x0,
High = 0x1,
};
enum LedActionTypes
{
None = 0,
ChangeBrightness = 1,
};
/* LED Action object */
class LedAction {
// Functions
void Constructor(enum LedActionTypes action, int intensity, int delay)
{
// Check for valid intensity value
if (intensity < 0)
intensity = 0;
if (intensity > 255)
intensity = 255;
Intensity = intensity;
Finished = false;
Action = action;
CompleteOn = millis() + delay;
}
public:
// Properties
boolean Finished;
enum LedActionTypes Action = None;
int Intensity = 0;
unsigned long CompleteOn = 0;
// Constructor
LedAction()
{
Constructor(Action, Intensity, 0);
}
LedAction(enum LedActionTypes action)
{
Constructor(action, Intensity, 0);
}
LedAction(enum LedActionTypes action, int intensity, int delay)
{
Constructor(action, intensity, delay);
}
// Methods
void Loop() {
if (Finished) { return; }
unsigned long currentTimeStamp = millis();
if (CompleteOn >= currentTimeStamp)
{
//Process the action
Finished = true;
}
}
};
/* LED object */
class Led {
// Functions
void Constructor(enum SignalTypes signalType, byte pbPin, enum SignalDirections signalDirection, int intensity)
{
// Check for valid intensity value
if (intensity < 0)
intensity = 0;
if (intensity > 255)
intensity = 255;
Intensity = intensity;
Constructor(SignalType, PBPin, SignalDirection, DigitalSignalValue);
}
void Constructor(enum SignalTypes signalType, byte pbPin, enum SignalDirections signalDirection, enum DigitalSignalValues signalValue)
{
SignalType = signalType;
PBPin = pbPin;
SignalDirection = signalDirection;
DigitalSignalValue = signalValue;
}
public:
// Properties
byte PBPin;
int Intensity;
enum DigitalSignalValues DigitalSignalValue;
enum SignalTypes SignalType = Analog;
enum SignalDirections SignalDirection = Outbound;
LedAction Actions[20]{ LedAction(None) };
// Constructor
Led()
{
Constructor(SignalType, 0, SignalDirection, 0);
}
Led(byte pbPin, enum SignalDirections signalDirection, int intensity)
{
Constructor(SignalType, pbPin, signalDirection, intensity);
}
Led(byte pbPin, enum SignalDirections signalDirection, enum DigitalSignalValues signalValue)
{
Constructor(SignalType, pbPin, signalDirection, signalValue);
}
Led(enum SignalTypes signalType, byte pbPin, enum SignalDirections signalDirection, int intensity)
{
Constructor(signalType, pbPin, signalDirection, intensity);
}
Led(enum SignalTypes signalType, byte pbPin, enum SignalDirections signalDirection, enum DigitalSignalValues signalValue)
{
Constructor(signalType, pbPin, signalDirection, signalValue);
}
// Methods
void Setup()
{
switch (SignalType)
{
case Analog:
analogWrite(PBPin, Intensity);
break;
case Digital:
digitalWrite(PBPin, Intensity);
pinMode(PBPin, SignalDirection);
break;
}
}
void Loop()
{
int n;
// Loop through all actions and find unfinished ones then fire them off
for ( n=0 ; n<20 ; ++n )
{
if (!Actions[n].Finished)
{
Actions[n].Loop();
if (Actions[n].Finished)
{
//Action was just flagged ready to process
switch (Actions[n].Action)
{
case ChangeBrightness:
digitalWrite(PBPin, Actions[n].Intensity);
break;
}
}
}
}
}
void AddAction(LedAction action)
{
int n;
// Loop through all actions and find an unfinished one then reuse it
for (n = 0; n < 20; ++n)
{
if (!Actions[n].Finished)
{
action.Finished = false;
Actions[n] = action;
break;
}
}
}
void ClearAllActions()
{
int n;
// Loop through all actions and mark all as finished
for (n = 0; n < 20; ++n)
{
if (!Actions[n].Finished)
{
Actions[n].Finished = true;
}
}
}
};
An example of the code on Tinkercad Circuits can be found at...
https://www.tinkercad.com/things/gmGeFVKOA3e-adrunio-test/editel
Once on the page, click the "Simulate" play button (bottom left) and then hit the "Start Simulation" button at the top. You should see the same errors I'm seeing.
OK, so I think I found my answer...
After having a few conversations with Tinkercad and the great support team there I was told that they use an old version of Arduino to compile their code. Well, all the code I'm writing and the IDE I'm writing in are based on the latest releases of everything. I think this is where my issue starts to happen.
Currently, when trying to compile the following code in the current version that Tinkercad uses I get the respective error that follows...
enum TestEnum
{
OptOne = 1,
OptTwo = 2
};
class TestClass {
private:
void InitilizeClass(enum TestEnum te)
{ }
public:
TestClass(enum TestEnum te)
{ }
};
void setup()
{ }
void loop()
{ }
As you can see, I'm trying to create a function in my class called InitializeClass() that takes my enum as a parameter. The compiler does not like that one bit, and the error highlighting along with the error returned are horrible for some reason telling me nothing!
However, if I change the class to only use the enum in the constructor itself, that seems to compile just fine with no errors.
enum TestEnum
{
OptOne = 1,
OptTwo = 2
};
class TestClass {
private:
void InitilizeClass()
{ }
public:
TestClass(enum TestEnum te)
{ }
};
void setup()
{ }
void loop()
{ }
The Problem
I wanted to create a class that has as least repetitive code as possible, this is usually the way I try and code in general. If I have to type the same code twice... Start looking at making it a function. This way a) there is less code and b) if you ever need to change the logic you're changing it in one place rather than multiple places. So I start out usually making my classes look like this...
enum TestEnum
{
OptOne = 1,
OptTwo = 2
};
class TestClass {
private:
void InitilizeClass(enum TestEnum te, int n, int t)
{
if (n < 0)
n = 0;
if (n > 255)
n = 255;
TE = te;
N = n;
T = t;
}
public:
enum TestEnum TE = OptOne;
int N = 0;
int T = 0;
TestClass(enum TestEnum te)
{ InitilizeClass(te, 0, 0); }
TestClass(enum TestEnum te, int n)
{ InitilizeClass(te, n, 0); }
TestClass(enum TestEnum te, int n, int t)
{ InitilizeClass(te, n, t); }
};
void setup()
{ }
void loop()
{ }
This works perfectly fine in VS IDE (c++) as well as the latest Arduino IDE. However, since Tinkercad is using an old Arduino compiler it has a fit with me using the enums in a private class function. (Update: Just found out this happens in ANY class function! Not just a private one.)
The Solution
So this whole time, I have been thinking with a c# mentality when coding my Arduino project when I should have been thinking more c++ like. Well, in my defense I am after all a c# guy through and through. I love it SO much more the c++. Well in almost every way. There are apparently exceptions. I recently found out, that while c# does not allow it c++ DOES allow you to overload class constructors! That changes everything for the way I would write my classes in c++. Now there is no reason for the private class function to initialize the class. So I can just have something like the following...
enum TestEnum
{
OptOne = 1,
OptTwo = 2
};
class TestClass {
public:
enum TestEnum TE = OptOne;
int N = 0;
int T = 0;
TestClass(enum TestEnum te)
{ TestClass(te, 0, 0); }
TestClass(enum TestEnum te, int n)
{ TestClass(te, n, 0); }
TestClass(enum TestEnum te, int n, int t)
{ TestClass(te, n, t); }
};
void setup()
{ }
void loop()
{ }
That my friends compiles completely fine in the old Arduino compilers. It also saves me some space for other code (these ATTiny chips are not huge so a smaller program is always a win!).
Related
I am building a program that measure the voltage of a component. When the potential is under 1 V, the vibration motor will turn on.
But my problem is, I want the Arduino to turn on the motor, if the voltage has been under 1 V for 10 seconds or more. For example, if the voltage level is under 1 V for 8 seconds and then it changes to over 5 V again, the motor should not turn on. Also, the motor should turn off after 3 seconds of vibration. I am able to satisfy the first condition.. but I'm not able to turn off the motor.
Here is my code so far:
int Pin = 2;
const float baselineVoltage = 200.0;
int belowBaselineVoltage = false;
unsigned long turnOnAt = 10000;
const unsigned long turnOnDelay = 10 * 1000;
void setup() {
Serial.begin(9600);
pinMode(Pin, OUTPUT);
}
void loop() {
int sensorValue = analogRead(A0);
float voltage = sensorValue * (5.0 / 1023.0);
float voltage2 = voltage*(1023.0 / 5.0);
Serial.println(voltage2);
delay(1000);
if (voltage2 < baselineVoltage) {
if (belowBaselineVoltage == true) {
if (millis() >= turnOnAt) {
digitalWrite(2, HIGH);
} else {
} else {
belowBaselineVoltage = true;
turnOnAt = millis() + turnOnDelay;
}
} else {
belowBaselineVoltage = false;
}
}
State machines are very popular in embedded systems. It makes the code behave in a predictable way, and that's invaluable when you do not have a debugger to step through the code.
Let's define your states.
motor_off: Motor is OFF, read voltage, if voltage < 1V, go to time_low_voltage state.
time_low_voltage: Motor is OFF, read voltage, if voltage >= 1V go to state motor_off. After 8 seconds in this state, go to motor_on state.
motor_on: Motor is ON. after 3 seconds, go to motor_off state.
We have 1 data item to keep track of, a timestamp of when the state has changed.
// **********************************
// shaker motor related definitions
enum class shaker_state : char
{
motor_off,
time_low_voltage,
motor_on,
};
struct shaker_data_t
{
shaker_state state;
unsigned int timestamp;
// you could add here a small buffer to hold a few previous values
// of ADC readings to do a bit of filtering... The median of 5 filter
// should work very well here. It is not absolutely needed for now,
// but you should consider having some kind of filter. The motor may
// introduce noise in the readings. We'll just keep the last reading for
// now.
unsigned int adc_value;
};
// constants are declared constexpr to save memory space.
constexpr unsigned int SHAKER_TIMEOUT_LOW_VOLTAGE = 8000; // in milliseconds
constexpr unsigned int SHAKER_TIMEOUT_MOTOR_ON = 3000; // .idem.
// Again to save memory, and allow for DAC related constants
// to be computed at compile-time.
constexpr inline float adc_value_to_volts(unsigned int value)
{
return value * (5.f / 1024.f);
}
constexpr inline unsigned int volts_to_adc_value(float tension)
{
return unsigned int(tension * (1024.f / 5.f));
}
constexpr unsigned int SHAKER_ADC_THRESHOLD = volts_to_adc_value(1.f);
constexpr int SHAKER_MOTOR = 2;
constexpr int SHAKER_ADC = A0;
// **********************************
// global data
shaker_data_t shaker;
// **********************************
static void shaker_control()
{
// always read adc.
shaker.adc_value = analogRead(SHAKER_ADC);
switch (shaker.state)
{
default: // in case of catastrophic error, turn motor OFF.
case shaker_state::motor_off:
digitalWrite(SHAKER_MOTOR, LOW);
if (shaker.adc_value < SHAKER_ADC_THRESHOLD)
{
shaker.state = shaker_state::time_low_voltage;
shaker.timestamp = (unsigned int)millis();
}
break;
case shaker_state::time_low_voltage:
digitalWrite(SHAKER_MOTOR, LOW);
if (shaker.adc_value >= SHAKER_ADC_THRESHOLD)
{
shaker.state = shaker_state::motor_off;
}
else if ((unsigned int)millis() - shaker.timestamp >= SHAKER_TIMEOUT_LOW_VOLTAGE)
{
digitalWrite(SHAKER_MOTOR, HIGH);
shaker.state = shaker_state::motor_on;
shaker.timestamp = (unsigned int)millis();
}
break;
case shaker_state::motor_on:
digitalWrite(SHAKER_MOTOR, HIGH);
if ((unsigned int)millis() - shaker.timestamp >= SHAKER_TIMEOUT_MOTOR_ON)
{
digitalWrite(SHAKER_MOTOR, LOW);
shaker.state = shaker_state::motor_off;
shaker.timestamp = (unsigned int)millis();
}
break;
}
}
// **********************************
void setup()
{
Serial.begin(9600);
digitalWrite(SHAKER_MOTOR, LOW);
pinMode(SHAKER_MOTOR, OUTPUT);
shaker.state = shaker_state::motor_off;
shaker.timestamp = (unsigned int)millis();
}
// **********************************
void loop()
{
// get temp and control moror.
shaker_control();
// shaker_control is very fast and never blocks, so you have plenty
// of extra time to blink LEDs, read switches, etc...
}
[EDIT]
Here is a simple median of 5 filter that you can add to filter out unwanted noise on the ADC reading.
class median_of_5_filter
{
public:
void push(unsigned int value)
{
buffer_[next_] = value;
if (++next_ >= 5)
next_ = 0;
compute();
}
unsigned int get() const
{
return filtered_;
}
private:
void compute()
{
// bubble sort, but only the first 3 elements
unsigned int buf_copy[5] = { buffer_[0], buffer_[1], buffer_[2],
buffer_[3], buffer_[4] };
for (unsigned char i = 0; i < 3; ++i)
{
auto min_index = i;
auto min_value = buf_copy[i];
for (unsigned char j = i + 1; j < 5; ++j)
{
if (buf_copy[j] < min_value)
{
min_value = buf_copy[j];
min_index = j;
}
}
if (i != min_index)
swap(buf_copy[i], buf_copy[min_index]);
}
filtered_ = buf_copy[2];
}
static void swap(unsigned int& a, unsigned int& b)
{
auto t = a;
a = b;
b = t;
}
private:
unsigned int filtered_ = 0;
unsigned char next_ = 0;
unsigned int buffer_[5] = {};
};
To use:
struct shaker_data_t
{
// ...
median_of_5_filter adc_value;
};
// when reading ADC...
shaker.adc_value.push(analogRead(SHAKER_ADC));
// to get filtered adc reading
if (shaker.adc_value.get() < SHAKER_ADC_THRESHOLD)
I'm trying to create a Monopoly game in C++ and I've been messing with object-oriented-programming, the problem happens with the classes "Game" and "Player", I would like to know how to use "Game"'s functions inside "Player" and "Player"'s functions inside "Game", but I've been getting a compiler error saying that the class is not defined.
Switching class positions won't work (obviously) but I tried anyways.
Code (reduced and minimized to the Game and Player classes):
namespace Monopoly {
typedef enum { normal, train, company, incometax, luxurytax, start, chancecard, chestcard, jail } type;
class Game {
private:
bool running = false;
int turn = 1;
int currentPlayerID;
int startingMoney = 1000;
std::vector<Player> players;
public:
// Functions
void createPlayer() {
++currentPlayerID;
Player newPlayer(currentPlayerID, startingMoney);
players.push_back(newPlayer);
++currentPlayerID;
}
void createPlayers(int playerAmount) {
for (int i = 0; i <= playerAmount; ++i) {
createPlayer();
}
}
Player getPlayer(int index) {
Player p = players[index];
return p;
}
};
class Player {
private:
int playerID;
int money;
std::vector<int> propertiesOwned;
void addProperty(int id) {
this->propertiesOwned.push_back(id);
}
public:
// Constructor
Player(int pID, int sMoney) {
this->playerID = pID;
this->money = sMoney;
}
// Functions
Player payMoney(int payAmount, unsigned int destinationID, Game engine) {
this->money -= payAmount;
if (destinationID > 0) {
// Checks if you're paying to a player or bank
bool playerFound = false;
for (int i = 0; i <= engine.getPlayerAmount(); ++i) {
if (engine.getPlayer(i).getID() == destinationID) {
playerFound = true;
break;
}
}
if (playerFound) {
// Player was found
engine.getPlayer(destinationID).giveMoney(payAmount);
return;
}
else {
std::cout << "\nERROR: Invalid player ID at function payMoney\n";
return;
}
}
else {
// You're paying to the bank
}
return;
}
void buyProperty(int id, int price, Game engine) {
payMoney(price, 0, engine);
addProperty(id);
}
void giveMoney(int payMoney) {
this->money += payMoney;
}
// Returns
inline int getMoney() { return this->money; }
inline int getID() { return this->playerID; }
inline auto getProperties(int index) {
auto p = propertiesOwned[index];
return p;
}
inline int getPropertyAmount() {
int amount = std::size(propertiesOwned);
return amount;
}
};
}
I expected the classes to run the other classes function normally, but it seens like that in C++, classes are defined in certain order, and you can only access classes (in a class) declared before the class you're using, feedback and alternatives that fix this would help
You are correct that in C++ declaration order matters, and that is the cause of your errors, however there are a few other issues with the code.
Firstly, you should swap the order that Game and Player are defined. This will make it easier, as Player relies on Game fewer times than Game relies on Player.
Next, add a forward declaration for Game before the definition of Player:
class Game;
This tells the compiler that a class named Game exists and allows you to use it in scenarios where it doesn't need to know the contents (i.e. definition) of the class.
Next, make payMoney and buyProperty accept their engine parameter by reference instead of by value by changing the parameter specifier to Game &engine. This is important for two reasons. First, passing by value can only be done if you have already defined the type, which we have not (we've only declared it). Second, passing by value creates a copy of the object, which in this case means a completely new vector of completely new Player objects, and the changes will not synchronize back to the old object. See here for a better explanation of references.
Next, you need to extract the definition of payMoney to after the definition of Game. The reason is that while the parameter list of payMoney no longer relies on the definition of Game, the code in the function body does (because it calls functions on the engine object). See the end for what this looks like.
This fixes all the problems with declaration/definition order. You also should make payMoney return void as its return value is never provided and never used, pick a consistent type for IDs (either int or unsigned int, not a mix), and add the getPlayerAmount to Game.
Here's what the final code could look like:
namespace Monopoly {
typedef enum { normal, train, company, incometax, luxurytax, start, chancecard, chestcard, jail } type;
class Game;
class Player {
private:
int playerID;
int money;
std::vector<int> propertiesOwned;
void addProperty(int id) {
this->propertiesOwned.push_back(id);
}
public:
// Constructor
Player(int pID, int sMoney) {
this->playerID = pID;
this->money = sMoney;
}
// Functions
void payMoney(int payAmount, int destinationID, Game &engine);
void buyProperty(int id, int price, Game &engine) {
payMoney(price, 0, engine);
addProperty(id);
}
void giveMoney(int payMoney) {
this->money += payMoney;
}
// Returns
inline int getMoney() { return this->money; }
inline int getID() { return this->playerID; }
inline auto getProperties(int index) {
auto p = propertiesOwned[index];
return p;
}
inline int getPropertyAmount() {
int amount = std::size(propertiesOwned);
return amount;
}
};
class Game {
private:
bool running = false;
int turn = 1;
int currentPlayerID;
int startingMoney = 1000;
std::vector<Player> players;
public:
// Functions
void createPlayer() {
++currentPlayerID;
Player newPlayer(currentPlayerID, startingMoney);
players.push_back(newPlayer);
++currentPlayerID;
}
void createPlayers(int playerAmount) {
for (int i = 0; i <= playerAmount; ++i) {
createPlayer();
}
}
Player getPlayer(int index) {
Player p = players[index];
return p;
}
int getPlayerAmount() {
int amount = players.size();
return amount;
}
};
void Player::payMoney(int payAmount, int destinationID, Game &engine) {
this->money -= payAmount;
if (destinationID > 0) {
// Checks if you're paying to a player or bank
bool playerFound = false;
for (int i = 0; i <= engine.getPlayerAmount(); ++i) {
if (engine.getPlayer(i).getID() == destinationID) {
playerFound = true;
break;
}
}
if (playerFound) {
// Player was found
engine.getPlayer(destinationID).giveMoney(payAmount);
return;
}
else {
std::cout << "\nERROR: Invalid player ID at function payMoney\n";
return;
}
}
else {
// You're paying to the bank
}
return;
}
}
Side note: it's technically better C++ to use size_t instead of int for variables storing the size of vectors, as that is what the size functions return (and it's an unsigned integer type whereas int is signed), but that's not especially important.
I'm getting this error when I try to compile an Arduino sketch. I don't see where it thinks I'm trying to call Counter::Counter without arguments. What's going on here?
sketch/periodic_effect.cpp: In constructor 'PeriodicEffect::PeriodicEffect(uint32_t, uint32_t, Counter::Mode)':
periodic_effect.cpp:4: error: no matching function for call to 'Counter::Counter()'
PeriodicEffect::PeriodicEffect( uint32_t durationMs, uint32_t periods, Counter::Mode mode) : Runnable(), periods(periods) {
periodic_effect.cpp
#include <Arduino.h>
#include "periodic_effect.h"
PeriodicEffect::PeriodicEffect( uint32_t durationMs, uint32_t periods, Counter::Mode mode) : Runnable(), periods(periods) {
intervalMs = durationMs / periods;
index = Counter(intervalMs, mode);
}
void PeriodicEffect::run() {
uint32_t currTimeMs = millis();
if( (currTimeMs - prevTimeMs) > intervalMs ) {
applyChange();
prevTimeMs = currTimeMs;
index.increment();
}
}
periodic_effect.h
#ifndef PERIODIC_EFFECT_H
#define PERIODIC_EFFECT_H
#include "counter.h"
#include "runnable.h"
class PeriodicEffect : public Runnable {
public:
PeriodicEffect( uint32_t durationMs, uint32_t periods, Counter::Mode mode = Counter::Mode::RESETTING );
Counter index;
private:
uint32_t intervalMs, periods, prevTimeMs;
virtual void applyChange() = 0;
void run();
virtual void setup() = 0;
};
#endif
counter.h
#ifndef COUNTER_H
#define COUNTER_H
#include <stdint.h>
class Counter {
public:
enum Mode { RESETTING, RETURNING };
Counter(uint32_t limit, Counter::Mode mode = RESETTING, uint32_t index = 0);
uint32_t getIndex();
void increment();
float percent();
private:
bool goingForward;
uint32_t index;
const uint32_t limit;
Counter::Mode mode;
void incrementReturning();
void incrementResetting();
};
#endif
counter.cpp
#include "counter.h"
Counter::Counter(uint32_t limit, Counter::Mode mode, uint32_t index) : index(index), limit(limit), mode(mode) {
goingForward = true;
}
uint32_t Counter::getIndex() {
return index;
}
void Counter::increment() {
switch( mode ) {
case RESETTING : incrementResetting();
case RETURNING : incrementReturning();
}
}
void Counter::incrementResetting() {
index = (index + 1) % limit;
}
void Counter::incrementReturning() {
if( goingForward ) {
index++;
if( index >= limit ) {
index = limit - 1;
goingForward = false;
}
} else {
index--;
if( index <= 0 ) {
index = 1;
goingForward = true;
}
}
}
float Counter::percent() {
return (float) index / (float) limit;
}
Your class PeriodicEffect has a Counter member variable, and no constructor. This means the member variable will need to be default constructed, i.e. constructed by a constructor with no parameters. You haven't supplied one.
You can either give Counter a default constructor, or let the existing constructor have defaults for all of the parameters. Or you can make a PeriodicEffect constructor with an initializer list to provide the proper parameters to index.
PeriodicEffect::PeriodicEffect(
uint32_t durationMs, uint32_t periods, Counter::Mode mode) :
Runnable(), periods(periods) { /* ... */
PeriodicEffect has a member Counter index;. Since index is not in the member initializer list of this constructor, it gets default constructed.
The statement
index = Counter(intervalMs, mode);
inside the constructor is an assignment, not an initialization. Too late.
The following code asserts in the indicated place with "iterator+offset is out of range."
void Network::PushInput(int c, int h, int w) {
Input* input = new Input(batch, c, h, w, data);
layers.push_back(input); // this happens to be the first push_back()
// layers.push_back(input); // doing another doesn't change the assert!
Layer *foo = layers.back(); // asserts here
Layer *baz = layers[layers.size()-1]; // does not assert
}
Input is a public subclass of Layer. layers is declared as
std::vector<Layer *>layers;
If I attempt to duplicate the above with more vanilla template types, e.g., int*, back() works as expected with no asserts. Somehow, the template type matters here. (Note: _ITERATOR_DEBUG_LEVEL is 2, which triggers that assert check in the vector class.)
I'd rather not bluntly change all of the back()'s in the code to size()-1, but would rather understand what is going on here.
Any ideas? (I'll continue to perturb the code until I can find the apparent cause of this, but hopefully this will be obvious to someone else.)
(I'm using Visual Studio 2013 Community Edition, if that matters.)
.....
Here's a stand-alone file that compiles that shows the problem:
#include <vector>
using namespace std;
namespace layer {
class Layer {
public:
Layer(float alpha = 0, float momentum = 0.9f, float weight_decay = 0);
virtual ~Layer();
// three virtual method that all layers should have
virtual void forward(bool train = true) = 0;
virtual void backward() = 0;
virtual void update() = 0;
void adjust_learning(float scale); // change the learning rate
Layer* prev; // previous layer
Layer* next; // next layer
float* data; // X': output (cuDNN y)
int batch; // n: batch size
float alpha; // learning rate
float momentum; // beta: momentum of gradient
float weight_decay; // gamma: weight decay rate
};
} /* namespace layer */
namespace layer {
Layer::Layer(float alpha_, float momentum_, float weight_decay_)
{
std::memset(this, 0, sizeof(*this));
alpha = alpha_;
momentum = momentum_;
weight_decay = weight_decay_;
}
Layer::~Layer() {}
void Layer::adjust_learning(float scale) {
alpha *= scale;
}
}
namespace layer {
class Input : public Layer {
public:
Input(int n, int c, int h, int w, float* _data);
virtual ~Input();
void forward(bool train = true);
void backward();
void update();
};
}
namespace layer {
Input::Input(int n, int c, int h, int w, float* _data) : Layer() {
prev = NULL;
batch = n;
data = _data;
}
Input::~Input() {
data = NULL;
}
void Input::forward(bool train) {
// nothing
}
void Input::backward() {
// nothing
}
void Input::update() {
// nothing
}
}
using namespace layer;
namespace model {
class Network {
private:
std::vector<Layer*> layers; // list of layers
bool has_input, has_output; // sanity check
float* data; // input on device
int batch; // whole size of data, batch size
public:
Network(int batch_size);
virtual ~Network();
void PushInput(int c, int h, int w);
};
}
namespace model {
void Network::PushInput(int c, int h, int w) {
Input* input = new Input(batch, c, h, w, data);
layers.push_back(input);
Layer *foo = layers.back(); // **WHY DOES THIS ASSERT??**
}
Network::Network(int _batch) {
std::memset(this, 0, sizeof(*this));
batch = _batch;
}
Network::~Network() {
for (Layer* l : layers)
delete l;
}
}
void main()
{
model::Network foo(10);
foo.PushInput(2, 3, 4);
}
You have undefined behavior in your code.
In the Layer constructor you do
std::memset(this, 0, sizeof(*this));
The problem with this is that the above call will clear the virtual function table (which is a part of the object) as well. Any virtual function called after that will not work as expected, if at all. That includes the destruction of the objects as the destructors are virtual.
I've got weird problem. I'm trying to write simple game in C++, but I failed on objects and data types. There's a code:
// C++
// Statki
#include <stdio.h>
#include <time.h>
#include <map>
#include <vector>
#include <string>
#include <list>
#define D true
using namespace std;
void _(char* message){printf("%s\n",message);};
struct relpoint { int x,y; };
struct point { int x,y; };
struct size { int w,h; };
map<const char*, vector<relpoint> > shipshape;
list<char*> shipTypes = {"XS", "S", "M", "L", "XL"};
string alpha="ABCDEFGHIJKLMNOPRSTUVWXYZ";
enum fieldtype { UNKNOWN=-1,EMPTY=0,SHIP=1,HIT=2,MISS=3,};
enum rotation { EAST=0, SOUTH=1, WEST=2, NORTH=3 };
class Ship
{
char* type;
};
class Sea
{
public:
void init(size mapsize) { init( mapsize, EMPTY ); };
void init(size mapsize, fieldtype fill)
{
if(D)printf("Generating sea\n");
vector<fieldtype> v;
seamap.reserve(mapsize.h);
v.reserve(mapsize.w);
for (int y=0; y<mapsize.h; y++)
{
v.clear();
for(int x=0; x<mapsize.w; x++)
{
v.push_back(fill);
}
seamap.push_back(v);
}
view();
};
bool place_ship(Ship ship);
void view()
{
for( vector< vector<fieldtype> >::const_iterator yy = seamap.begin(); yy != seamap.end(); ++yy )
{
for( vector<fieldtype>::const_iterator xx = (*yy).begin(); xx != (*yy).end(); ++xx )
{
if(D)printf("%d ", *xx);
}
if(D)printf("\n");
}
};
private:
vector< vector<fieldtype> > seamap;
};
class Game
{
public:
void initmap(size mapsize)
{
if(D) printf("\nInit %d×%d map\n", mapsize.w, mapsize.h);
(*enemymap).init(mapsize, UNKNOWN);
//(*selfmap).init(mapsize);
};
bool placeship(string type, point position, rotation rotate);
fieldtype shoot(point target);
void viewmap(){(*selfmap).view();};
bool eog();
Sea * enemymap;
Sea * selfmap;
};
class Bot
{
public:
void init(size mapsize)
{
if(D)_("Init Bot");
}
private:
Game * g;
};
class Player
{
public:
Player() { if(D){_("Player fake init");} };
void init(size mapsize)
{
(*g).initmap(mapsize);
};
void viewmap(){(*g).viewmap();};
private:
Game * g;
};
class Router
{
public:
void startgame();
void welcomescreen()
{
printf("\n\n\n\t\t\tShips minigame\n\t\t\t\tby Kris\n\n");
mainmenu();
};
void mainmenu()
{
printf("Menu (type letter):\n\tN: New game\n\tS: Settings\n\tQ: Quit game\n\n > ");
char opt;
opt = toupper(getchar());
size ms;
switch(opt)
{
case 'N':
ms = getmapsize();
(*P1).init(ms);
(*P2).init(ms);
break;
case 'S':
break;
case 'Q':
break;
default:
printf("Invalid option %c", opt);
mainmenu();
}
};
private:
Player * P1;
Bot * P2;
size getmapsize()
{
size ms;
printf("\nSet map size (X Y)\n > ");
scanf("%d %d", &ms.w, &ms.h);
return ms;
};
};
int main () {
vector<relpoint> shp;
shp.reserve(5);
list<char*>::const_iterator tp = shipTypes.begin();
shp.push_back({0,0});
shipshape[*(tp++)] = shp;
shp.push_back({1,0});
shipshape[*(tp++)] = shp;
shp.push_back({2,0});
shipshape[*(tp++)] = shp;
shp.push_back({3,0});
shipshape[*(tp++)] = shp;
shp.push_back({2,1});
shipshape[*tp] = shp;
Router R;
R.welcomescreen();
printf("\n\n");
return 0;
}
It can be compiled, but after line Init 5×5 map program stops with Naruszenie ochrony pamięci (Memory access violation in polish) error. Problem seems to occur at both Sea::init() functions.
I'm compiling it with g++ -std=c++0x -Wno-write-strings ships2.cpp (to prevent warnings) on Ubuntu.
Any idea what's wrong with it?
All the classes contain pointers, but you never seem to initialize the pointers or allocate space for the objects they should point to.
Doing this
(*enemymap).init(mapsize, UNKNOWN);
when enemymap doesn't point anywhere, is an almost sure way to get an access violation.
You are using an uninitialized pointer. You can fix it by instantiating an object here, or in a constructor somewhere else.
Here is an example of instantiating in the initmap call.
void initmap(size mapsize)
{
// Initialize the pointer by instantiating a class
enemymap = new Sea;
if(D) printf("\nInit %d×%d map\n", mapsize.w, mapsize.h);
(*enemymap).init(mapsize, UNKNOWN);
//(*selfmap).init(mapsize);
};
+1 for Bo. But for your own sake:
compile with -g and then then
gdb ./mygame.bin
type 'run'
after setting the map size 5 5 :
Program received signal SIGSEGV, Segmentation fault.
mainmenu (this=<optimized out>) at memacvio.cpp:158
158 (*P1).init(ms);
This should tell you that P1 is probably not a valid poiner.