I am trying to access a function of a pointer and it does not work and it gives me an LoadStoreAlignmentCause Exception. Furthermore I want to check if the pointer does exist, but it always returns true for that.
LedFunction.h
#include "Led/LedStates.h"
class LedStates;
class LedFunction {
public:
LedStates *state;
virtual bool init();
bool loadValues();
virtual void render() = 0;
};
LedFunction.cpp
#include "Led/LedFunction.h"
bool LedFunction::init() {
return false;
}
RainbowFunction.h
class RainbowFunction: public LedFunction {
public:
RainbowFunction() {
Serial.println("Rainbow Constructor.");
}
void render() {
Serial.println("From Rainbow...");
}
}
};
LedStates.h
#include "Handlers/LedHandler.h"
#include "Led/LedFunction.h"
class LedHandler;
class LedFunction;
class LedStates {
public:
uint8_t (*values)[3];
int count = 0;
bool dirty = false;
LedHandler* ledHandler;
LedFunction* function = 0;
LedStates(LedHandler* handler);
void setFunction(LedFunction *newFunction);
void setRgb(int i, uint8_t r, uint8_t g, uint8_t b);
void render(); //TODO check virtual key
void setValues(LedStates &to);
void commit();
void fade(LedStates &to, long f0, long f1);
};
LedStates.cpp
#include "Led/LedStates.h"
#include "Led/Animations/RainbowFunction.h"
LedStates::LedStates(LedHandler* handler) {
this->ledHandler = handler;
count = ledHandler->getLength();
values = new uint8_t[count][3];
this->setFunction(new RainbowFunction());
}
void LedStates::setFunction(LedFunction* newFunction) {
Serial.println("SETTING FUNCTION");
if(function)
delete function; //TODO check virtual destructor
function = newFunction;
if(!function)
return;
function->state = this;
Serial.println("-----Setting Done-----");
}
void LedStates::render() {
Serial.println(2);
Serial.println("B:" + (String) (function != 0));
Serial.println("B:" + (String) (function != false));
if(function == nullptr) { //This is the check that is not working properly
Serial.println(22222);
//delay(1000);
//function->render();
} else {
Serial.println(33333);
function->render();
}
Serial.println(3);
}
LedHandler.h
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#include <FastLED.h>
//#include "Led/LedFunction.h"
#include "Led/LedStates.h"
#include "Led/Fading.h"
class LedStates;
class LedHandler {
public:
LedHandler(int length, uint16_t pin);
void clear();
void show();
void setColor(int s, int r, int g, int b);
void loop();
Adafruit_NeoPixel getStrip();
int getLength();
private:
LedStates* currentState;
LedStates* targetState;
Fader<LedStates> *ledFader;
int length;
Adafruit_NeoPixel strip;
CRGB* leds;
};
LedHandler.cpp
#include "Handlers/LedHandler.h"
LedHandler::LedHandler(int length, uint16_t pin) {
Serial.begin(115200);
this->length = length;
this->strip = Adafruit_NeoPixel(length, pin);
this->strip.begin();
CRGB* arr = new CRGB[length];
this->leds = arr;
FastLED.addLeds<WS2812B, 6, RGB>(leds, 60).setCorrection(TypicalLEDStrip);
//Serial.println("-----Creating States-----");
LedStates currentLedStates = LedStates(this);
LedStates targetLedStates = LedStates(this);
Fader<LedStates> ledFader = Fader<LedStates>(currentLedStates, targetLedStates);
//Serial.println("-----Created States-----");
this->currentState = ¤tLedStates;
this->targetState = &targetLedStates;
this->ledFader = &ledFader;
}
void LedHandler::loop() {
Serial.println("--::--::--::--::--::--::--");
currentState->render();
Serial.println(99);
Serial.println(6);
currentState->commit();
Serial.println("-------------------------");
delay(10000);
}
The Serialmonitor output:
SETTING FUNCTION
-----Setting Done-----
Rainbow Constructor.
SETTING FUNCTION
-----Setting Done-----
--::--::--::--::--::--::--
2
B:1
B:1
33333
Exception (9):
epc1=0x40202a92 epc2=0x00000000 epc3=0x00000000 excvaddr=0x4020d32d depc=0x00000000
These lines define local variables inside the function LedHandler::LedHandler(int length, uint16_t pin):
LedStates currentLedStates = LedStates(this);
LedStates targetLedStates = LedStates(this);
These lines remember the address of the local variables:
this->currentState = ¤tLedStates;
this->targetState = &targetLedStates;
This line deletes the local variables so the memory can be used for something else:
}
and this line calls the something else (nobody knows what it will be):
currentState->render();
I am trying to make a library for redirection of data printed to Print class. I am unfortunately stuck on error that reads
error: cannot declare variable 'diagData' to be of abstract type 'PrintToString'
note: because the following virtual functions are pure within 'PrintToString'
note: virtual size_t PrintToString::write(uint8_t)
I tried several variations of how to implement this but with no luck. (Sourced from the internet)
Links
Print class: github.com/ Print.h and Print.cpp
My code
PrintToString.h
#ifndef PRINT_TO_STRING_H
#define PRINT_TO_STRING_H
#include <Arduino.h>
class PrintToString : public Print
{
private:
String* data;
public:
PrintToString();
~PrintToString();
String* results();
void clear();
size_t write(uint8_t) = 0;
size_t write(const uint8_t* buffer, size_t size);
};
#endif
PrintToString.cpp
#include "PrintToString.h"
PrintToString::PrintToString()
{
data = new String();
}
PrintToString::~PrintToString()
{
delete data;
data = NULL;
}
String* PrintToString::results()
{
return data;
}
void PrintToString::clear()
{
delete data;
data = new String();
}
size_t PrintToString::write(const uint8_t* buffer, size_t size)
{
size_t n = 0;
while (size--)
{
if (data->concat(*buffer++))
n++;
else
break;
}
return n;
}
TestSketch.ino (I have left out content of all the constants)
#include <ESP8266WiFi.h>
#include <PrintToString.h>
const char* WIFI_SSID
const char* WIFI_PASS
const char* API_HOST
const uint16_t API_PORT
const uint16_t LOCAL_UDP_PORT
WiFiUDP UDPClint;
PrintToString diagData;
uint64_t packetNumber = 0;
void setup()
{
WiFi.begin(WIFI_SSID, WIFI_PASS);
UDPClint.begin(LOCAL_UDP_PORT);
while (WiFi.status() != WL_CONNECTED)
delay(500);
WiFi.printDiag(diagData);
sendStringPacket(diagData.result());
diagData.clear();
}
void loop()
{
delay(1000);
}
void sendStringPacket(String payload)
{
UDPClint.beginPacket(API_HOST, API_PORT);
uint64_t thisPNumber = packetNumber++;
String thisPNumberStr;
while (thisPNumber > 0)
{
uint8_t digit = thisPNumber % 10;
thisPNumberStr.concat(digit);
thisPNumber /= 10;
}
UDPClint.write(';');
for (uint64_t i = 0; i < payload.length(); i++)
UDPClint.write(payload.charAt(i));
UDPClint.endPacket();
}
This is because this class has a pure virtual function here:
size_t write(uint8_t) = 0;
A class with a pure virtual function cannot be instantiated. So method write(uint8_t) must be somehow implemented in your code.
EDIT: Consider making use of the code you used in sendStringPacket() for write(uint8_t). You may be able to redirect output without using sendStringPacket(diagData.result()); statement.
Getting a "throttle not declared in this scope" error when I attempt to compile the main.cpp. I am very new to c++, so bear with me. I have the #include "throttle.h" in the headers for both cpp files, so I am not sure why when i try to create a throttle object is is not declared...
main.cpp file:
#include <stdio.h>
#include "throttle.h"
using namespace std;
int main(int argc, char **argv)
{
throttle throt1(5, 0);
throttle throt2(4, 0);
return 0;
}
throttle.h file:
#ifndef MAIN_SAVITCH_THROTTLE
#define MAIN_SAVITCH_THROTTLE
namespace main_savitch_2A
{
class throttle
{
public:
// CONSTRUCTORS
//throttle( );
//throttle(int size);
throttle(int size = 1, int start = 0); //by adding this default
//constructor the other two
//are not needed
// MODIFICATION MEMBER FUNCTIONS
void shut_off( ) { position = 0; }
void shift(int amount);
// CONSTANT MEMBER FUNCTIONS
double flow( ) const
{
return position / double(top_position);
}
bool is_on( ) const
{
return (position > 0);
}
int get_top_position()const;
int get_position()const;
friend bool operator <(const throttle& throt1, const throttle& throt2);
//postcondtion: returns true if flow of throt1 < flow of throt2.
//return false if flow of throt1 > flow of throt2
private:
int top_position;
int position;
};
}
#endif
throttle.cpp file :
#include <cassert> // Provides assert
#include "throttle.h" // Provides the throttle class definition
using namespace std; // Allows all Standard Library items to be used
namespace main_savitch_2A
{
//throttle::throttle( )
//{ // A simple on-off throttle
//top_position = 1;
//position = 0;
//}
//throttle::throttle(int size)
// Library facilities used: cassert
//{
//assert(size > 0);
//top_position = size;
//position = 0;
//}
throttle::throttle(int size, int start)
{
assert(size > 0);
assert(start = 0);
top_position = size;
position = start;
}
void throttle::shift(int amount)
{
position += amount;
if (position < 0)
position = 0;
else if (position > top_position)
position = top_position;
}
bool operator <(const throttle& throt1, const throttle& throt2)
{
return(throt1.flow() < throt2.flow());
}
int throttle::get_top_position()const
{
return top_position;
}
int throttle::get_position()const
{
return position;
}
}
In your main, it should be main_savitch_2A::throttle throt1(5, 0);.
The same for throt2.
See namespaces for further details.
First of all I'm sorry for my bad english, hope you guys will understand me :) Im writing WinAPI game and my classes behave very strange: all operations with vector
crash my program so Windows says that my .exe stopped working. But when I debug these lines
I get exceptions.
This is how my class header looks like:
#ifndef FIGURE_H_INCLUDED
#define FIGURE_H_INCLUDED
#include <vector>
#include <Windows.h>
#include "Other.h"
using namespace std;
enum Figure_Type { I, J, L, O, S, T, Z };
class Figure
{
public:
/* CONSTRUCTORS */
Figure();
Figure(Figure_Type);
/* MOVEMENT */
bool Move(vector<Cell>&, Direction&);
void Drop(vector<Cell>&);
bool Rotate(vector<Cell>&);
/* OTHER */
void Draw(HDC&);
private:
/* METHODS */
void Generate();
void GenerateMasks();
void GenerateFigure();
Figure GetFigureCopy() const;
/* DATA */
Shift shift;
char mask[4][4];
vector<Cell> vCells;
Figure_Type type;
int rotation;
};
#endif
My constructors are using Generate() method, which code is:
void Figure::GenerateFigure()
{
vCells.clear();
int defPosX = 4,
defPosY = 20;
Cell cell;
for(int y = 0; y < 4; y++)
{
for(int x = 0; x < 4; x++)
{
if(mask[y][x] == '0')
{
cell.x = defPosX + x + shift.dx;
cell.y = defPosY - y + shift.dy;
vCells.push_back(cell);
}
}
}
}
And I'm getting exceptions on vCells.clear() method and (if I comment first line) vCells.push_back(cell) line. Actually every operation with vector / vector iterators crash my program even incrementing iterator, those are just the first so my code isn't running any longer after them.
Exception text:
"Unhandled exception at 0x5A4ACCD2 (msvcp110d.dll) in Tetris_completely_new.exe: 0xC000041D: An unhandled exception was encountered during a user callback."
And these exceptions are thrown on 217's line of "xutility". I commented it:
....
// MEMBER FUNCTIONS FOR _Container_base12
inline void _Container_base12::_Orphan_all()
{ // orphan all iterators
#if _ITERATOR_DEBUG_LEVEL == 2
if (_Myproxy != 0)
{ // proxy allocated, drain it
_Lockit _Lock(_LOCK_DEBUG);
for (_Iterator_base12 **_Pnext = &_Myproxy->_Myfirstiter;
*_Pnext != 0; *_Pnext = (*_Pnext)->_Mynextiter)
**(*_Pnext)->_Myproxy = 0;** // <------------ THIS LINE
_Myproxy->_Myfirstiter = 0;
}
#endif /* _ITERATOR_DEBUG_LEVEL == 2 */
}
....
Here is how my Cell struct looks like:
struct Cell
{
Cell() : x(1), y(1) { }
Cell(int _x, int _y): x(_x), y(_y) { }
void Draw(HDC&) const;
bool operator ==(const Cell& a) const { return (x == a.x && y == a.y); }
bool operator !=(const Cell& a) const { return !(*this == a); }
int x;
int y;
};
And Figure constructor:
Figure::Figure()
{
srand(time(NULL));
vCells.clear();
type = Figure_Type(rand() % 7);
rotation = 0;
shift.dx = 0;
shift.dy = 0;
Generate();
}
You're likely invoking undefined behaviour.
Without any more information, I'd say you're calling instance methods through stale object references/pointers (a reference taken at the time of callback registration is no longer valid?).
Also, as currently written in the question, you're generating a figure based on unitialized bytes in mask, so you'd likely want to initialize these too.
Here's a take on oa slightly modernized/cleaned up version. Note
the use of initializer lists
uniform initialization
reordered member initialization
not using using namespace in headers
moved srand into main instead of the constructor
See it Live on Coliru
#ifndef FIGURE_H_INCLUDED
#define FIGURE_H_INCLUDED
#include <vector>
#ifdef _WIN32
# include <Windows.h>
# include "Other.h"
#else
# include <cstdint>
# include <cstdlib>
# include <ctime>
using HDC = uint32_t;
#endif
struct Cell
{
Cell(int _x=1, int _y=1): x(_x), y(_y) { }
void Draw(HDC&) const;
bool operator ==(const Cell& a) const { return (x == a.x && y == a.y); }
bool operator !=(const Cell& a) const { return !(*this == a); }
int x;
int y;
};
struct Shift
{
Shift(int dx=0, int dy=0) : dx(dx), dy(dy) {}
int dx, dy;
};
enum class Direction
{
up, down, left, right
};
enum Figure_Type { I, J, L, O, S, T, Z };
class Figure
{
public:
/* CONSTRUCTORS */
Figure();
Figure(Figure_Type);
/* MOVEMENT */
bool Move(std::vector<Cell>&, Direction&);
void Drop(std::vector<Cell>&);
bool Rotate(std::vector<Cell>&);
/* OTHER */
void Draw(HDC&);
private:
/* METHODS */
void Generate();
void GenerateMasks();
void GenerateFigure();
Figure GetFigureCopy() const;
/* DATA */
char mask[4][4];
std::vector<Cell> vCells;
Figure_Type type;
int rotation;
Shift shift;
};
#endif
/*
* And I'm getting exceptions on vCells.clear() method and (if I comment first
* line) vCells.push_back(cell) line. Actually every operation with vector /
* vector iterators crash my program even incrementing iterator, those are just
* the first so my code isn't running any longer after them.
*
* Exception text:
* **"Unhandled exception at 0x5A4ACCD2 (msvcp110d.dll) in
* Tetris_completely_new.exe: 0xC000041D: An unhandled exception was
* encountered during a user callback."**
*
* And these exceptions are thrown on 217's line of "xutility". I commented it:
*
* ....
* // MEMBER FUNCTIONS FOR _Container_base12
* inline void _Container_base12::_Orphan_all()
* { // orphan all iterators
* #if _ITERATOR_DEBUG_LEVEL == 2
* if (_Myproxy != 0)
* { // proxy allocated, drain it
* _Lockit _Lock(_LOCK_DEBUG);
*
* for (_Iterator_base12 **_Pnext = &_Myproxy->_Myfirstiter;
* *_Pnext != 0; *_Pnext = (*_Pnext)->_Mynextiter)
* **(*_Pnext)->_Myproxy = 0;** // <------------ THIS LINE
* _Myproxy->_Myfirstiter = 0;
* }
* #endif // _ITERATOR_DEBUG_LEVEL == 2
* }
* ....
*
* Here is how my **Cell struct** looks like:
*/
//And **Figure constructor**:
Figure::Figure()
: mask {{0}},
vCells(),
type((Figure_Type) (rand() % 7)),
rotation(0),
shift({0,0})
{
Generate();
}
//My constructors are using Generate() method, which code is:
void Figure::Generate()
{
GenerateFigure();
}
void Figure::GenerateFigure()
{
vCells.clear();
for(int y = 0; y < 4; y++) {
for(int x = 0; x < 4; x++) {
if(mask[y][x] == '0')
vCells.push_back({4 + x + shift.dx, 20 - y + shift.dy});
}
}
}
int main()
{
srand(time(0));
Figure fig1;
Figure fig2;
}
Im having an issue with one of my classes. The class has only 1 array<> member in it. I am building a static object of this class, and initializing the values in a function. The problem is the values are never inserted.
When I step into the debugger and look at some basic insert statements into this array, the array remains empty. However if I step into the insert function itself, I can see a 'second' array of the exact same name, storing the values as expected.
It looks to me as though there is the static outer scoped array, which has nothing in it, and a second internal version (the exact same array) that has the contents stored properly.
Is there something I am missing here? I really dont know why this is happening.
Here is the minimum source code, as per request
circularbuffer.hpp
#ifndef __ma__circularbuffer_guard
#define __ma__circularbuffer_guard
#include <array>
template < typename T, int SIZE>
class CircularBuffer
{
private:
int _index;
int _size;
std::array<T, SIZE> _buffer;
public:
CircularBuffer() { _index = 0; _size = SIZE; }
int length ();
typename T& at (int);
void insert (T);
int index ();
private:
int realign (int&);
};
template < typename T, int SIZE>
int CircularBuffer<T, SIZE>::realign (int& index)
{
if (index >= _size)
{
index -= _size;
realign(index);
} else if (index < 0)
{
index += _size;
realign(index);
}
return index;
}
template < typename T, int SIZE>
int CircularBuffer<T, SIZE>::length ()
{
return _size;
}
template < typename T, int SIZE>
typename T& CircularBuffer<T, SIZE>::at (int index)
{
realign(index);
return _buffer.at(index);
}
template <typename T, int SIZE>
void CircularBuffer<T, SIZE>::insert (T data)
{
realign(_index);
_buffer.at(_index) = data;
_index += 1;
}
template <typename T, int SIZE>
int CircularBuffer<T, SIZE>::index ()
{
return _index;
}
#endif
global buffer initializer
#ifndef __guard__namespace__notes__
#define __guard__namespace__notes__
#include "circularbuffer.hpp"
#include <memory>
typedef CircularBuffer<char, 7> CB_Natural_T;
typedef CircularBuffer<int, 12> CB_Chromatic_T;
static CB_Natural_T WHITENOTES = CB_Natural_T(); // buffer of letter notes
static CB_Chromatic_T POSITIONS = CB_Chromatic_T(); // buffer of absolute positions on keyboard
struct Initialize
{
Initialize()
{
WHITENOTES.insert('C');
WHITENOTES.insert('D');
WHITENOTES.insert('E');
WHITENOTES.insert('F');
WHITENOTES.insert('G');
WHITENOTES.insert('A');
WHITENOTES.insert('B');
// Initialize all positions
for (int i = 0; i < 12; ++i)
POSITIONS.insert(i);
}
};
static Initialize dummy_init_var = Initialize();
#endif
to initialize the static buffers so I can unit test my other classes.
Note class header and cpp
#ifndef __guard__note__
#define __guard__note__
#include "macros.h"
#include <string>
#include <memory>
class Note
{
public:
enum Qualities { UNKNOWN = -3, DFLAT, FLAT, NATURAL, SHARP, DSHARP }; // qualities of note
typedef DEF_PTR(Note); // pointer type
private:
char _letter [1]; // the letter of the note
std::string _name; // the full name of the note
int _value; // absolute value
int _position; // relative position
Qualities _quality; // sharp/natural/flat quality
public:
Note();
Note(char); // letter
Note(char, Qualities); // letter, and quality
// setters
void sharp(); // Sets the quality of the note to 1
void Dsharp(); // Sets the quality of the note to 2
void flat(); // Sets the quality of the note to -1
void Dflat(); // Sets the quality of the note to -2
void natural(); // Sets the quality of the note to 0
// getters
char letter() const; /* returns character letter */
std::string name() const; /* returns true name of note */
int position() const; /* returns relative position on keyboard */
int quality() const; /* returns the quality of the note */
void respell() const; /* respells a note to the nearest other note */
static pointer_type make(char); // returns a shared pointer of a new Note
static pointer_type make(char, Qualities); // returns a shared pointer of a new Note
// operators
bool operator ==(Note& r) const; // Returns true if Notes are truly equal
bool operator !=(Note& r) const; // Returns true if Notes are truly not equal
bool isEnharmonic(Note& r) const; // Returns true if Notes are enharmonically equal
bool isNatural() const; // Returns true if Note is natural
bool isSharp() const; // Returns true if Note is sharp
bool isDSharp() const; // Returns true if Note is double sharp
bool isFlat() const; // Returns true if Note is flat
bool isDFlat() const; // Returns true if Note is double flat
private:
void makeName(); /* sets name of Note */
};
#endif
#include "note.h"
Note::Note()
{
_letter[1] = 'u';
_name = "";
_value = -1;
_quality = UNKNOWN;
_position = -1;
}
Note::Note(char l)
{
_letter[1] = l;
// determine absolute value based on letter
switch (l)
{
case 'C':
_value = 0; break;
case 'D':
_value = 2; break;
case 'E':
_value = 4; break;
case 'F':
_value = 5; break;
case 'G':
_value = 7; break;
case 'A':
_value = 9; break;
case 'B':
_value = 11; break;
default:
_value = -1; break;
}
_quality = NATURAL;
_position = _value + _quality;
makeName();
}
Note::Note(char l, Note::Qualities q)
{
_letter[1] = l;
// determine absolute value based on letter given
switch (l)
{
case 'C':
_value = 0; break;
case 'D':
_value = 2; break;
case 'E':
_value = 4; break;
case 'F':
_value = 5; break;
case 'G':
_value = 7; break;
case 'A':
_value = 9; break;
case 'B':
_value = 11; break;
default:
_value = -1; break;
}
_quality = q; // assert for good data
_position = _value + _quality;
makeName();
}
void Note::sharp() { _quality = SHARP; _position = _value + 1; makeName();}
void Note::Dsharp() { _quality = DSHARP; _position = _value + 2; makeName();}
void Note::flat() { _quality = FLAT; _position = _value - 1; makeName();}
void Note::Dflat() { _quality = DFLAT; _position = _value - 2; makeName();}
void Note::natural() { _quality = NATURAL; _position = _value; makeName(); }
char Note::letter() const { return _letter[1]; }
std::string Note::name() const { return _name; }
int Note::position() const { return _position; }
int Note::quality () const { return _quality; }
Note::pointer_type Note::make(char l) { return pointer_type(new Note(l)); }
Note::pointer_type Note::make(char l, Note::Qualities q) { return pointer_type(new Note(l, q)); }
void Note::makeName()
{
_name = "";
_name += _letter[1]; // add letter to name
// find out quality, add quality to name
switch (_quality)
{
case DFLAT:
_name += "bb"; break;
case FLAT:
_name += "b"; break;
case SHARP:
_name += "#"; break;
case DSHARP:
_name += "x"; break;
case NATURAL:
break;
default:
_name += "u"; break;
}
}
bool Note::operator ==(Note& r) const
{
// true if letter, value, position, and quality are all equal
return (_letter[1] == r._letter[1]) && (_value == r._value) && (_position == r._position) && (_quality == r._quality);
}
bool Note::operator !=(Note& r) const
{
return !(*this == r);
}
bool Note::isEnharmonic (Note& r) const
{
return (_position == r._position);
}
bool Note::isNatural() const
{
return _quality == NATURAL;
}
bool Note::isSharp() const
{
return _quality == SHARP;
}
bool Note::isDSharp() const
{
return _quality == DSHARP;
}
bool Note::isFlat() const
{
return _quality == FLAT;
}
bool Note::isDFlat() const
{
return _quality == DFLAT;
}
I would post interval as well, but that one is very big. But basically There is this code inside one of Intervals functions called findInterval
Interval::findInterval
void Interval::findInterval(Note& bottom, Note& top)
{
int index = 0; // temp placeholder for start position
// find where the bottom note is in relation to buffer
for (int i = 0; i < WHITENOTES.length(); ++i)
{
if (bottom.letter() == WHITENOTES.at(i))
{
index = i; // set start position to this position
break;
}
}
// find the interpreted interval
// starting from index, with offset of length + index
for (int i = index; i < (index + WHITENOTES.length()); ++i)
{
if (top.letter() == WHITENOTES.at(i))
{
_interval = i - index; // set interval
break;
}
}
// modify index to serve as the position of the bottom note
index = bottom.position();
// find the physical distance
for (int i = index; i < (index + POSITIONS.length()); ++i)
{
if (top.position() == POSITIONS.at(i)) // values match
{
_distance = i - index; // set physical distance
break;
}
else if (top.position() > 11 && ((top.position() - 11) == POSITIONS.at(i))) // if top position is higher than octave
{
_distance = (i - index) + 11;
break;
}
}
}
It fails to set the data members here, because WHITENOTES is empty, even though i called to initialize it with a static struct.
One other thing to note, if I compile my ut_interval, the tests all come back perfect with no failures, and when i check the values of the buffers in the debugger, they show up as being \0. however it still goes through the if statements and matches the char with the letter (is this some sort of encryption on chars in the debugger?)
However, exact same #includes in ut_chord, and it fails to evaluate the intervals
Here is a sample of the interval ut, and chord ut
ut_interval
#include "../common/namespace_notes.h"
#include "../common/note.h"
#include "../common/interval.h"
#define BOOST_TEST_MODULE IntervalTest
#include <boost/test/auto_unit_test.hpp>
#define TEST_IVL(i, dist, itv, q, n) \
BOOST_CHECK(i.distance() == dist); \
BOOST_CHECK(i.interval() == i.itv); \
BOOST_CHECK(i.quality() == i.q); \
BOOST_CHECK(i.name() == n)
BOOST_AUTO_TEST_CASE(INTERVAL_UNISONS)
{
// make some notes
Note C = Note('C');
Note Cs = Note('C', Cs.SHARP);
Note Cds = Note('C', Cds.DSHARP);
Note Cf = Note('C', Cf.FLAT);
Note Cdf = Note('C', Cdf.DFLAT);
// make some intervals
Interval PUnison = Interval(C, C);
Interval AugUnison = Interval(C, Cs);
Interval Aug2Unison = Interval(C, Cds);
Interval DimUnison = Interval(C, Cf);
Interval Dim2Unison = Interval(C, Cdf);
// make sure members are accurate
TEST_IVL(PUnison, 0, UNISON, PER, "Perfect Unison");
BOOST_CHECK(PUnison.isPerfect());
TEST_IVL(AugUnison, 1, UNISON, AUG, "Augmented Unison");
BOOST_CHECK(AugUnison.isAugmented());
TEST_IVL(Aug2Unison, 2, UNISON, AUG, "Augmented Unison");
BOOST_CHECK(AugUnison.isAugmented());
TEST_IVL(DimUnison, 1, UNISON, AUG, "Augmented Unison");
BOOST_CHECK(DimUnison.isAugmented());
TEST_IVL(Dim2Unison, 2, UNISON, AUG, "Augmented Unison");
BOOST_CHECK(Dim2Unison.isAugmented());
}
ut_chord
#include "../common/namespace_notes.h"
#include "../common/note.h"
#include "../common/interval.h"
#include "../common/chord.h"
#define BOOST_TEST_MODULE ChordTest
#include <boost/test/auto_unit_test.hpp>
#include <memory>
BOOST_AUTO_TEST_CASE(ChordConstructor)
{
typedef std::shared_ptr<Note> nt;
nt C = nt(new Note('C'));
nt E = nt(new Note('E'));
nt G = nt(new Note('G'));
nt B = nt(new Note('B'));
Interval PUnison = Interval(*C, *C); // cannot determine this interval
Chord C7 = Chord(C , E, G, B);
Chord C72 = Chord(B, G, E, C);
Chord C73 = Chord(E, G, C, B);
}
Firstly, you should not include a .cpp file. To fix the linker problem you are having, follow the inclusion model: place your function definitions in the template's header file.
Secondly, I have tried the following example program and it works now - the problem might have been due to the linker error.
Read this SO question for more information regarding including a cpp file (and templates).
main.cpp:
#include <array>
#include "circularbuffer.h"
typedef CircularBuffer<char, 7> CB_Natural_T;
typedef CircularBuffer<int, 12> CB_Chromatic_T;
static CB_Natural_T WHITENOTES = CB_Natural_T(); // buffer of letter notes
static CB_Chromatic_T POSITIONS = CB_Chromatic_T();
int main()
{
WHITENOTES.insert('C');
WHITENOTES.insert('D');
WHITENOTES.insert('E');
WHITENOTES.insert('F');
WHITENOTES.insert('G');
WHITENOTES.insert('A');
WHITENOTES.insert('B');
// Initialize all positions
for (int i = 0; i < 12; ++i)
POSITIONS.insert(i);
return 0;
}
circularbuffer.h:
#ifndef _CIRCULAR_BUFFER_H
#define _CIRCULAR_BUFFER_H
#include <array>
template < class T, int SIZE>
class CircularBuffer
{
private:
int _index;
int _size;
std::array<T, SIZE> _buffer;
public:
CircularBuffer() : _index(0), _size(SIZE), _buffer() {}
int length ()
{
return _size;
}
T& at (int index)
{
realign(index);
return _buffer.at(index);
}
void insert (T data)
{
realign(_index);
_buffer.at(_index) = data;
_index += 1;
}
int index ()
{
return _index;
}
private:
int realign (int& index)
{
if (index >= _size)
{
index -= _size;
realign(index);
} else if (index < 0)
{
index += _size;
realign(index);
}
return index;
}
};
#endif
Also, use inclusion guards to make sure your files are not included twice.
static CB_Natural_T WHITENOTES = CB_Natural_T();
static CB_Chromatic_T POSITIONS = CB_Chromatic_T();
It is these two that don't behave as you expect them to, right? Since these are globals, you should put
extern CB_Natural_T WHITENOTES;
extern CB_Chromatic_T POSITIONS;
into a header file to declare them and
CB_Natural_T WHITENOTES;
CB_Chromatic_T POSITIONS;
into a cpp file to actually define them. The static caused these objects to have internal linkage, so every file (precisely: compilation unit) that includes the header will have two such objects created instead of sharing them between different files.
I also think these two objects are constants, right? In that case, you could declare them as such. You would then need a helper that generates these objects or a constructor that allows initializing:
CB_Natural_T whitenotes()
{
CB_Natural_T init;
...
return init;
}
CB_Natural_T const WHITENOTES = whitenotes();
Notes:
The "= T()" is redundant, as already mentioned.
The template SIZE parameter is stored in an int, which is unnecessary since the value is always present.
You are using a realign() function that both modifies the argument and returns the result. I'd use one of these only. Also, since it is a function that only modifies a parameter without touching any members (see point above!), you could make it a static function. At least it should be a const member function.