After a bit of bug searching, I've found that my code leaves with exit code 11 at a certain point, and this is because an EXC_BAD_ACCESS error with code 1. After some googling, I see this must be due to some memory mismanagement, but I'm new to C++ and nothing seems obvious to me.
The code is exiting at the line
fieldFuncts[field](string, equal, validOps, length, difficulty, rng, opGen);
in the file (last function)
//
// Created by Anthony Monterrosa on 4/17/18.
//
#include "MathExpr.h"
#include <list>
#include <iostream>
#include <random>
std::vector<std::function<void(std::vector<MathExpr::CharType> &, MathExpr::NumType &, std::vector<MathExpr::NumType> &)>> MathExpr::layerFuncts;
std::vector<std::function<void(std::vector<MathExpr::CharType> &, MathExpr::NumType &, std::vector<MathExpr::Op> &, unsigned char, unsigned char, std::mt19937 &, std::uniform_int_distribution<MathExpr::OpType> &)>> MathExpr::fieldFuncts;
void MathExpr::init() {
initLayerFuncts();
initFieldFuncts();
}
void MathExpr::initLayerFuncts() {
layerFuncts.resize(Op::EOOE);
layerFuncts[static_cast<unsigned long>(Op::addition)] = [](std::vector<MathExpr::CharType> &string, NumType &equal, std::vector<NumType> & otherArgs) -> void {
string.insert(string.end(), {' ', opToChar(Op::addition), ' '});
equal += otherArgs[0];
std::vector<MathExpr::CharType> digits = intToDigit(otherArgs[0]);
for(int i = 0; i < digits.size(); i++ ) {
string.push_back(digits[i]);
}
};
layerFuncts[static_cast<unsigned long>(Op::subtraction)] = [](std::vector<MathExpr::CharType> &string, MathExpr::NumType &equal, std::vector<NumType> & otherArgs) -> void {
string.insert(string.end(), {' ', opToChar(Op::subtraction), ' '});
equal -= otherArgs[0];
std::vector<MathExpr::CharType> digits = intToDigit(otherArgs[0]);
for(int i = 0; i < digits.size(); i++ ) {
string.push_back(digits[i]);
}
};
}
void MathExpr::initFieldFuncts() {
fieldFuncts.resize(Field::EOFE);
fieldFuncts[static_cast<unsigned long>(Field::integers)] = [](std::vector<MathExpr::CharType> &string, NumType &equal, std::vector<MathExpr::Op> &validOps, unsigned char length, unsigned char difficulty, std::mt19937 &rng, std::uniform_int_distribution<MathExpr::OpType> &opGen) -> void {
std::uniform_int_distribution<MathExpr::NumType> numGen(1, static_cast<MathExpr::NumType>(pow(10, difficulty)));
equal = numGen(rng);
std::vector<MathExpr::CharType> digits = intToDigit(equal);
for(int i = 0; i < digits.size(); i++ ) {
string.push_back(digits[i]);
}
for (int i = 0; i < length - 1; i++) {
MathExpr::Op op = validOps[opGen(rng)];
int count = otherArgsCount(op);
std::vector<MathExpr::NumType> otherArgs(count);
for(int j = 0; j < count; j++) {
otherArgs[j] = (numGen(rng));
}
layer(string, equal, op, otherArgs);
}
};
}
char MathExpr::opToChar(OpType ordinal) {
return opToChar(static_cast<Op>(ordinal));
}
char MathExpr::opToChar(Op op) {
switch(op) {
case Op::addition : return '+';
case Op::subtraction : return '-';
default : return '_';
}
}
MathExpr::NumType MathExpr::otherArgsCount(MathExpr::Op op) {
switch(op) {
case Op::addition : return 1;
case Op::subtraction : return 1;
default : return 0;
}
}
std::vector<MathExpr::CharType> MathExpr::intToDigit(MathExpr::NumType num) {
std::vector<MathExpr::CharType> digits;
while(num >= 10) {
digits.insert(digits.begin(),'0' + static_cast<MathExpr::CharType>(num % 10));
num /= 10;
} digits.insert(digits.begin(), '0' + static_cast<MathExpr::CharType>(num));
return digits;
}
bool MathExpr::initBool = false;
MathExpr::MathExpr(std::vector<CharType> exp, MathExpr::NumType equal) {
if(!initBool) init();
this->string = std::vector<CharType>(exp);
this->equal = equal;
}
void MathExpr::print(MathExpr &exp) {
for(int i = 0; i < exp.string.size(); i++) {
std::cout << exp.string[i];
}
}
void MathExpr::layer(std::vector<MathExpr::CharType> &string, MathExpr::NumType &equal, MathExpr::Op op, std::vector<MathExpr::NumType> &otherArgs) {
layerFuncts[op](string, equal, otherArgs);
}
MathExpr MathExpr::generate(std::vector<MathExpr::Op> &validOps, MathExpr::Field field, unsigned char length, unsigned char difficulty) {
std::vector<MathExpr::CharType> string;
std::random_device rd;
std::mt19937 rng(rd());
std::uniform_int_distribution<MathExpr::OpType> opGen(0, static_cast<MathExpr::OpType>(validOps.size() - 1));
MathExpr::NumType equal;
fieldFuncts[field](string, equal, validOps, length, difficulty, rng, opGen);
return MathExpr::MathExpr(string, equal);
}
here is the corresponding .h file
//
// Created by Anthony Monterrosa on 4/17/18.
//
// EO_E -> "end of _ enum".
#ifndef MATHTESTGENERATOR_MATHEXPR_H
#define MATHTESTGENERATOR_MATHEXPR_H
#include <functional>
#include <random>
#include <vector>
class MathExpr {
public:
using FieldType = unsigned char;
using OpType = unsigned char;
using NumType = short int;
using CharType = char;
enum Field : FieldType {
integers,
EOFE // rational, real, complex.
};
enum Op : OpType {
addition,
subtraction,
EOOE // multiplication, division, absolute value, radical
};
explicit MathExpr(std::vector<CharType>, NumType);
std::vector<CharType> string;
NumType equal;
static void print(MathExpr &);
static MathExpr generate(std::vector<Op> &, Field = Field::integers, unsigned char length = 2, unsigned char difficulty = 1);
//protected:
static void init();
static bool initBool;
static void layer(std::vector<MathExpr::CharType> &, NumType &, Op, std::vector<NumType> &);
static NumType otherArgsCount(Op);
static std::vector<CharType> intToDigit(NumType);
static char opToChar(OpType);
static char opToChar(Op);
static std::vector<std::function<void(std::vector<MathExpr::CharType> &, NumType &, std::vector<NumType> &)>> layerFuncts;
static void initLayerFuncts();
static std::vector<std::function<void(std::vector<MathExpr::CharType> &, NumType &, std::vector<MathExpr::Op> &, unsigned char, unsigned char, std::mt19937 &, std::uniform_int_distribution<MathExpr::OpType> &)>> fieldFuncts;
static void initFieldFuncts();
};
#endif //MATHTESTGENERATOR_MATHEXPR_H
My gut says the error has to do with the "string" vector, but I'm unsure of how to tell. I would appreciate insight into the problem.
Related
I have some simple class with several fields like this:
#ifndef XVector_h
#define XVector_h
struct XVFields
{
unsigned char JointPromosCountSinceBeginning;
unsigned char PromoWeeksCountSinceCurrPromoBeginning;
unsigned char NoPromoWeeksCountSinceLastJointPromo;
bool IsPromo;
};
class XVector
{
public:
XVFields XVFs;
unsigned char *DiscountUsagesCounts;
XVector();
~XVector();
};
#endif XVector_h
It's realization:
#include <sstream>
#include "XVector.h"
#include "DynProgTask.h"
XVector::XVector()
{
memset(&this->XVFs, 0, sizeof(XVFields));
this->DiscountUsagesCounts = (unsigned char*)malloc(DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/);
memset(this->DiscountUsagesCounts, 0, DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/);
this->UniqueValForHuman_private = NULL;
}
XVector::~XVector()
{
free(this->DiscountUsagesCounts);
}
I Have another class named TaskCase that has XVector as one of it's field. Single TaskCase - single XVector.
I have custom hashers and comparators for both of classes:
#ifndef XVectorHasher_h
#define XVectorHasher_h
#include <Windows.h>
#include "XVector.h"
struct XVectorHasher
{
size_t operator()(const XVector *k) const;
};
struct XVectorComparator
{
bool operator()(const XVector *xv1, const XVector *xv2) const;
};
#endif XVectorHasher_h
cpp:
#include "XVectorHasher.h"
#include "DynProgTask.h"
size_t XVectorHasher::operator()(const XVector *k) const
{
size_t result = 0;
const size_t prime = 31;
int unibytes_count = sizeof(XVFields) + (DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/);
unsigned char *unibytes = (unsigned char*)malloc(unibytes_count);
memcpy(unibytes, &k->XVFs, sizeof(XVFields));
memcpy(&unibytes[sizeof(XVFields)], k->DiscountUsagesCounts, DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/);
for (size_t i = 0; i < unibytes_count; i++)
result = unibytes[i] + (result * prime);
free(unibytes);
return result;
}
bool XVectorComparator::operator()(const XVector *xv1, const XVector *xv2) const
{
//this operator compares instances bytes to determine their equality
if (memcmp(&xv1->XVFs, &xv2->XVFs, sizeof(XVFields)) != 0)
return false;
if (memcmp(xv1->DiscountUsagesCounts, xv2->DiscountUsagesCounts, DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/) != 0)
return false;
return true;
}
Another h:
#ifndef TaskCaseHasher_h
#define TaskCaseHasher_h
#include <Windows.h>
#include "TaskCase.h"
struct TaskCaseHasher
{
size_t operator()(const TaskCase *k) const;
};
struct TaskCaseComparator
{
bool operator()(const TaskCase *xv1, const TaskCase *xv2) const;
};
#endif TaskCaseHasher_h
Another cpp:
#include "XVector.h"
#include "TaskCaseHasher.h"
#include "DynProgTask.h"
size_t TaskCaseHasher::operator()(const TaskCase *tc) const
{
size_t result = 0;
const size_t prime = 31;
XVector *k = const_cast<TaskCase*>(tc)->GET_CurrX();
int unibytes_count = sizeof(XVFields) + (DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/);
unsigned char *unibytes = (unsigned char*)malloc(unibytes_count);
memcpy(unibytes, &k->XVFs, sizeof(XVFields));
memcpy(&unibytes[sizeof(XVFields)], k->DiscountUsagesCounts, DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/);
for (size_t i = 0; i < unibytes_count; i++)
result = unibytes[i] + (result * prime);
free(unibytes);
return result;
}
bool TaskCaseComparator::operator()(const TaskCase *tc1, const TaskCase *tc2) const
{
//this operator compares instances bytes to determine their equality
XVector *xv1 = const_cast<TaskCase*>(tc1)->GET_CurrX();
XVector *xv2 = const_cast<TaskCase*>(tc2)->GET_CurrX();
if (memcmp(&xv1->XVFs, &xv2->XVFs, sizeof(XVFields)) != 0)
return false;
if (memcmp(xv1->DiscountUsagesCounts, xv2->DiscountUsagesCounts, DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/) != 0)
return false;
return true;
}
I have self made single directional list of TaskCase pointers. I iterate over them to find unique TaskCases (the ones which has different XVectors).
I have two different functions for that:
SmartArray<TaskCase*> *TaskStep::GET_UniqueCasesByCurrX() //This one doing it's job in 191 second
{
SmartArray<TaskCase*> *unitcs = new SmartArray<TaskCase*>(this->Cases->Length);
std::unordered_set<XVector*, XVectorHasher, XVectorComparator> unique_xs;
int curr_unique_case_i = 0;
for (TaskCase *itr = this->Cases->Head; itr; itr = itr->NEXT_CASE_PTR)
if (unique_xs.find(itr->GET_CurrX()) == unique_xs.end())
{
unique_xs.insert(itr->GET_CurrX());
unitcs->Data[curr_unique_case_i] = itr;
curr_unique_case_i++;
}
unitcs->Resize(curr_unique_case_i);
return unitcs;
}
std::unordered_set<TaskCase*, TaskCaseHasher, TaskCaseComparator> *TaskStep::GET_UniqueCasesSetByCurrX() //This one doing it's job in 363 seconds
{
std::unordered_set<TaskCase*, TaskCaseHasher, TaskCaseComparator> *unique_tcs = new std::unordered_set<TaskCase*, TaskCaseHasher, TaskCaseComparator>();
for (TaskCase *itr = this->Cases->Head; itr; itr = itr->NEXT_CASE_PTR)
if (unique_tcs->find(itr) == unique_tcs->end())
unique_tcs->insert(itr);
return unique_tcs;
}
std::unordered_set based on XVectors (191 sec) is way faster than std::unordered_set based on TaskCases (363 sec).
But I don't understand why. Anyway it falls to calling GET_CurrX() which returns XVector. In first case I can it by myself and in second case it called automatically inside set. What is possible to do to speedup TaskCase set?
I am aligning several arrays in order and performing some sort of classification. I created an array to hold other arrays in order to simplify the operations that I want to perform.
Sadly, my program crashed when I ran it and I went on to debug it to finally realize that the sizeof operator is giving me sizes of pointers and not arrays within the loop.So I resorted to the cumbersome solution and my program worked.
How can I avoid this cumbersome method? I want to calculate within a loop!
#include <iostream>
#include <string>
#define ARRSIZE(X) sizeof(X) / sizeof(*X)
int classify(const char *asset, const char ***T, size_t T_size, size_t *index);
int main(void)
{
const char *names[] = { "book","resources","vehicles","buildings" };
const char *books[] = { "A","B","C","D" };
const char *resources[] = { "E","F","G" };
const char *vehicles[] = { "H","I","J","K","L","M" };
const char *buildings[] = { "N","O","P","Q","R","S","T","U","V" };
const char **T[] = { books,resources,vehicles,buildings };
size_t T_size = sizeof(T) / sizeof(*T);
size_t n, *index = new size_t[T_size];
/* This will yeild the size of pointers not arrays...
for (n = 0; n < T_size; n++) {
index[n] = ARRSIZE(T[n]);
}
*/
/* Cumbersome solution */
index[0] = ARRSIZE(books);
index[1] = ARRSIZE(resources);
index[2] = ARRSIZE(vehicles);
index[3] = ARRSIZE(buildings);
const char asset[] = "L";
int i = classify(asset, T, T_size, index);
if (i < 0) {
printf("asset is alien !!!\n");
}
else {
printf("asset ---> %s\n", names[i]);
}
delete index;
return 0;
}
int classify(const char *asset, const char ***T, size_t T_size, size_t *index)
{
size_t x, y;
for (x = 0; x < T_size; x++) {
for (y = 0; y < index[x]; y++) {
if (strcmp(asset, T[x][y]) == 0) {
return x;
}
}
}
return -1;
}
As you are including <string> and <iostream> I assume that the question is about C++ and not C. To avoid all this complication, simply use containers. E.g:
#include <vector>
std::vector<int> vect = std::vector<int>(3,0);
std::cout << vect.size() << std::endl; // prints 3
One solution if you are coding in C is to terminate your array with a special item, like NULL
const char *books[] = { "A","B","C","D", NULL };
size_t size(const char *arr[])
{
const char **p = arr;
while (*p)
{
p++;
}
return p - arr;
}
You can specify the array size explizit:
size_t n, index[] = {ARRSIZE(books), ARRSIZE(resources), ARRSIZE(vehicles), ARRSIZE(vehicles)};
or if you want to avoid double typing you can you X-Macros to roll out everything:
#define TBL \
X(books) \
X(resources) \
X(vehicles) \
X(buildings)
const char **T[] = {
#define X(x) x,
TBL
};
#undef X
size_t n, index[] = {
#define X(x) ARRSIZE(x),
TBL
};
which produces the same. See Running Demo.
I have a sorted list, which takes a Comparator as parameter. In a unit test I tried to use two different comparators.
The strange thing is, using the StringReverseComparator with the two if-statements disabled (commented out), all works well and valgrind does not claim any error.
template<typename T> class Comparator {
public:
virtual int compare(const T * left, const T * right) = 0;
};
class StringReverseComparator : public Comparator<String> {
public:
int compare(const String *left, const String *right) {
int rv = strcasecmp((const char *)*left, (const char *)*right);
if (rv < 0) return 10;
if (rv > 0) return -10;
return rv;
}
};
class StringComparator : public Comparator<String> {
public:
int compare(const String *left, const String *right) {
return strcasecmp(left->operator const char*(), right->operator const char*());
}
};
As soon as I enable both if-statements, I get a segfault from sort method.
Sort method is the standard qsort by sedgewick.
const char * is an operator from String class to access the character array.
First I thought, that changing the comparator might cause troubles, so I created a new instance of sorted list. But that traps too as soon as I have the two if-statements enabled.
So what's wrong with the compare method?
//edit:
Ok, first the sort code (taken from sedgewick):
template<typename T> class SortedList {
public:
// rest omitted
protected:
void sort() { qsort(0, _size - 1); }
void qsort(int left, int right) {
if (right > left) {
const T *v = _elements[right], *tmp;
int i = left-1;
int j = right;
for (;;) {
while (_comparator->compare(_elements[++i], v) < 0) ;
while (_comparator->compare(_elements[--j], v) > 0) ;
if (i >= j) break;
tmp = _elements[i];
_elements[i] = _elements[j];
_elements[j] = tmp;
}
tmp = _elements[i];
_elements[i] = _elements[right];
_elements[right] = tmp;
qsort(left, i-1);
qsort(i+1, right);
}
}
private:
int _size;
const T **_elements;
Comparator<T> *_comparator;
};
... and here the operator char *.
by the way: String class passed all unit tests and was ok with valgrind too.
class String {
public:
// rest omitted
operator const char * () const { return _s; }
const char * operator * () const { return _s; }
private:
char *_s;
};
about usage of strcasecmp: my first attempt was:
return strcasecmp( ... ) * (-1);
using the same parameters as in StringComparator. When that failed, I tried anything I could imagine about, including the if-statements and different call syntax of operator char *.
strcasecmp does not return just -1, 0, 1 - as mentioned in many tutorials. The return value differs in size and sign and after -1, 0, 1 failed to, I tried using 10. The value has no significance, could be 815 or 42, what ever.
// edith 2
Thanks you all for your attention!
I solved it myself. The point is, the qsort algo was not safe enuf :(
The following sort code works fine (with any kind of comparator):
void qsort(int left, int right) {
if (right > left) {
const T *v = _elements[right], *tmp;
int i = left-1;
int j = right;
for (;;) {
while (++i < _size && _comparator->compare(_elements[i], v) < 0) ;
while (--j > 0 && _comparator->compare(_elements[j], v) > 0) ;
if (i >= j) break;
tmp = _elements[i];
_elements[i] = _elements[j];
_elements[j] = tmp;
}
tmp = _elements[i];
_elements[i] = _elements[right];
_elements[right] = tmp;
qsort(left, i-1);
qsort(i+1, right);
}
}
Thanks.
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.
I'm trying to play with some code..keep getting an compile error RND not declared in scope I found a part of the code that defined it if it ran on linux and if defined it on windows thus ignoring Mac users(no biggie, I would ignore them too!). I removed that part of the code and defined it using the linux settings(since I figured my Mac is more closer to linux than windows), but then I get the same error but for seed. The odd thing is those seed errors are at the same spot at the RND error was. So my question is what the heck is RND/Seed? My searches found them specific to VB but not sure if its useful since I'm using C++.
Here's an offensive code snipped(viewers discretion is advised):
mi = (int)(round(RND*(dimc-1)));
Any tips/suggestions would be great. I'm just starting to learn about c++ so I maybe missing something very simple.
Here's the entire code(stole it from here http://cg.iit.bme.hu/~zsolnai/gfx/genetic/ ):
// a fast genetic algorithm for the 0-1 knapsack problem
// by karoly zsolnai - keeroy#cs.bme.hu
// test case: 1000 items, 50 knapsack size
//
// compilation by: g++ genetic.cpp -O3 -ffast-math -fopenmp
#include <math.h>
#include <time.h>
#include <algorithm>
#include <vector>
#include <fstream>
#include <limits.h>
#define RND ((double)rand_r(&seed)/RAND_MAX) // reentrant uniform rnd
using namespace std;
struct chromo {
chromo(int dimc) { items = new bool[dimc]; }
~chromo() { items = NULL; }
void mutate(const int dimc, const int count) {
int mi;
for(int i=0;i<count;i++) {
mi = (int)(round(RND*(dimc-1)));
items[mi] = !items[mi];
}
}
bool* items;
int f;
};
int fitness(bool*& x, const int dimc, const vector<int>& v, const vector<int>& w, const int limit) {
int fit = 0, wsum = 0;
for(int i=0;i<dimc;i++) {
wsum += x[i]*w[i];
fit += x[i]*v[i];
}
if(wsum>limit) fit -= 7*(wsum-limit); // penalty for invalid solutions
return fit;
}
void crossover1p(const chromo& c1, const chromo& c2, const chromo& c3, const int dimc, const int cp) {
for(int i=0;i<dimc;i++) {
if(i<cp) { c3.items[i] = c1.items[i]; }
else { c3.items[i] = c2.items[i]; }
}
}
void crossover1p_b(const chromo &c1, const chromo &c2, const chromo &c3, int dimc, int cp) {
for(int i=0;i<dimc;i++) {
if(i>=cp) { c3.items[i] = c1.items[i]; }
else { c3.items[i] = c2.items[i]; }
}
}
void crossoverrand(const chromo &c1, const chromo &c2, const chromo &c3, const int dimc) {
for(int i=0;i<dimc;i++) {
if(round(RND)) { c3.items[i] = c1.items[i]; }
else { c3.items[i] = c2.items[i]; }
}
}
void crossoverarit(const chromo &c1, const chromo &c2, const chromo &c3, int dimc) {
for(int i=0;i<dimc;i++) {
c3.items[i] = (c1.items[i]^c2.items[i]);
}
}
bool cfit(const chromo &c1,const chromo &c2) { return c1.f > c2.f; }
bool cmpfun(const std::pair<int,double> &r1, const std::pair<int,double> &r2) { return r1.second > r2.second; }
int coin(const double crp) { // a cointoss
if(RND<crp) return 1; // crossover
else return 0; // mutation
}
// initializes the chromosomes with the results of a greedy algorithm
void initpopg(bool**& c, const std::vector<int> &w, const std::vector<int> &v, const int dimw, const int limit, const int pop) {
std::vector<std::pair<int,double> > rvals(dimw);
std::vector<int> index(dimw,0);
for(int i=0;i<dimw;i++) {
rvals.push_back(std::pair<int,double>(std::make_pair(i,(double)v[i]/(double)w[i])));
}
std::sort(rvals.begin(),rvals.end(),cmpfun);
int currentw = 0, k;
for(int i=0;i<dimw;i++) {
k = rvals[i].first;
if(currentw + w[k] <= limit) { // greedy fill
currentw += w[k];
index[k] = 1;
}
}
for(int i=0;i<pop;i++) {
for(int j=0;j<dimw;j++) {
c[i][j] = index[j];
}
}
}
int main() {
printf("\n");
srand(time(NULL));
vector<int> w, v; // items weights and values
int info=0;
FILE *f = fopen("1000_weights.txt","r");
FILE *f2 = fopen("1000_values.txt","r");
while(!feof(f) || !feof(f2) ) {
fscanf(f," %d ",&info);
w.push_back(info);
info=0;
fscanf(f2," %d ",&info);
v.push_back(info);
} // omitted fclose(f1) and fclose(f2) on purpose
const int limit = 50; // knapsack weight limit
const int pop = 250; // chromosome population size
const int gens = INT_MAX; // maximum number of generations
const int disc = (int)(ceil(pop*0.8)); // chromosomes discarded via elitism
const int dimw = w.size();
int best = 0, ind = 0, ind2 = 0; // a few helpers for the main()
int parc = 0; // parent index for crossover
double avg = 0, crp = 0.35; // crossover probability
vector<chromo> ch(pop,chromo(dimw));
bool **c = new bool*[pop];
for(int i=0;i<pop;i++) c[i] = new bool[dimw];
clock_t start = clock();
printf("Initializing population with a greedy algorithm...");
initpopg(c,w,v,dimw,limit,pop);
printf("done!");
for(int i=0;i<pop;i++) {
ch[i].items = c[i];
ch[i].f = fitness(ch[i].items, dimw ,v, w, limit);
}
printf("\n\n");
for(int p=0;p<gens;p++) {
std::sort(ch.begin(), ch.end(), cfit);
#pragma omp parallel for shared(ch)
for(int i=0;i<pop;i++) {
if(i>pop-disc) { // elitism - only processes the discarded chromosomes
if(coin(crp)==1) { // crossover section
ind = parc+round(10*RND); // choosing parents for crossover
ind2 = parc+1+round(10*RND);
// choose a crossover strategy here
crossover1p(ch[ind%pop],ch[ind2%pop],ch[i],dimw,round(RND*(dimw-1)));
// crossoverrand(ch[ind],ch[ind2],ch[i],dimw);
// crossoverarit(ch[0],ch[1],ch[i],dimw);
ch[i].f = fitness(ch[i].items, dimw ,v, w, limit);
parc += 1;
}
else { // mutation section
ch[i].mutate(dimw,1);
ch[i].f = fitness(ch[i].items, dimw ,v, w, limit);
}
}
avg += ch[i].f;
if(ch[i].f>best) best=ch[i].f;
}
parc = 0;
if(p%5==0) {
printf("\n#%d\t",p);
printf("best fitness: %d \t",best);
printf("avg fitness: %f",avg/pop);
if(best == 675) goto end; // psst...don't tell anyone
}
best = avg = 0;
}
end:
printf("\n\n");
clock_t end = clock();
double t = (double)(end-start)/CLOCKS_PER_SEC;
printf("\nCompletion time: %fs.\n",t);
return 0;
}
The problem is you've inexpertly cut apart the code you've got:
#if defined(__linux) || defined(__linux__)
unsigned int seed = time(NULL);
#define RND ((double)rand_r(&seed)/RAND_MAX) // reentrant uniform rnd
#endif
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
#define RND ((double)rand()/RAND_MAX) // uniform rnd
#endif
This defines the seed variable based on the current time for Linux systems; perhaps the Windows systems do not need a seed?
In any event, if you include both lines from the if defined (__linux) ... branch, instead of only one line, it should work without trouble on your OS X system.