I'm reading from file with the data below. What I need to achieve is at the end populating the vector m_hoteli with CHotel objects which have CTurist objects. 2nd-5th row are hotels with their variables and after the big numbers(500,400,300,600), that's the tourists in each hotel.
When I run it, my vectors gets filled with the information from the file but fills the details about the tourists as hotel data, thus I can't make a proper connection between the input and CTurist.
Marina 5 500 Joe 21 1 Tisho 20 6 Victoria 31 20
Tulip 4 400 Sarah 41 17 Rositsa 49 14 Valeria 24 2
BlackSea 3 300 John 35 12 Jon 35 11 Janni 28 6
SwissBell 5 600 Orlin 26 1 Margarita 27 2 Juliette 31
class CComplex:CHotel
{
protected:
string m_complex;
vector<CHotel> m_hoteli;
public:
CComplex(){};
CComplex(string filename, string nComplex)
{
string str;
m_complex = nComplex;
fstream file(filename, ios::in);
if (file.is_open())
{
CHotel temp(" ", 0, 0);
while (file >> temp)
{
m_hoteli.push_back(temp);
}
file.close();
}
else
throw "ERROR! ";
}
CHotel(String stringname) constructor:
class CHotel : public CTurist //втори клас, наследник на CTurist
{
protected:
string hName; //име хотел
int stars; //звезди на хотела
int beds; //брой легла
public:
map<CTurist, unsigned> Turisti;
unsigned Sum = 0;
int br = 0;
CHotel(){};
CHotel(string s)
{
map<CTurist, unsigned> TR;
bool first = true;
istringstream TList(s);
int i = 0;
while (getline(TList, s, ' '))
{
switch (i)
{
case 0: this->setName(s); break;
case 1: this->setAge(stoi(s)); break;
}
if (i ==2 )
{
if (!first){ setName(getName()); setAge(getAge()); first = true; }
else{ TR[CTurist("", 0)]; }
i = -1;
}
i++;
}
Turisti = TR;
}
That's some advancement I think I have made on the constructor. Looking through the breakpoints, the hotel receives it's information,aka name, stars and beds, but the tourists do not. Any tips?
CHotel(string s)
{
CTurist b("",0);
bool first = true;
istringstream TList(s);
string Days;
int i = 0;
string ime;
string godini;
if (first == true)
{
while (getline(TList, s, ' '))
{
switch (i)
{
case 0: this->hName = s; break;
case 1:this->stars = stoi(s); break;
case 2:this->beds = stoi(s); break;
}
i++;
if (i == 3)break;
}
first = false;
i = 0;
}
while (getline(TList, s, '|'))
{
switch (i)
{
case 0: ime = s; break;
case 1: godini = s; break;
case 2: Days = s; break;
}
i++;
if (i == 3)
{
CTurist T1("ime", stoi(Days));
//Turisti.insert(pair<CTurist,unsigned>("ime", stoi(Days)));
i = 0;
}
}
}
How to insert T1 into mapTuristi???
This is a c++ question. I want the code of the desired c++ function (see below) to be (as much as possible) in c-style and using c string library functions because I think that this will lead the quickest code in terms of execution time. (Am I wrong ? If so, how much ?) Yes, I value performance more than readability for this question because the desired function will be called a lot (millions) of times.
I am receiving const char *'s of the form "25Dec2016" that represent dates and I am parsing them to back out from them three int's (one for the day, the second for the month and the last for the year (that I assumed to be a number between 0 and 9999)) thanks to a function
Parse(const char * cDate, int & day, int & month, int & year)
I coded such a function and tested it : it works on correct const char*s (those that indeed represent date in my format), but I feel that my use of c functions (atoi for instance) is incorrect, even if I don't see why. There are also other problems :
the code is inelegant (the big if ... else if ... if) : one cannot do a switch statement on a string, but is there an elegant way to do this without resorting the std::map and c++11 ?
surely problematic from a c string point of view (I am not an expert) : for instance, I am really not happy with the way I extract the three substring into "buffers" ... Plus it appears I have problems with not null terminated char arrays that I'd like to correct. I could force a \0 at the end of _day and _year as I did for _month but I find that doing so is awful, so that I suspect a bad "design" of my parsing function
quite bad from an error handling point of view : the function is not a constructor for now, but could finally be, this is the reason why I throw.
I am open to any comments !
Here is the initial code :
Parse(const char * cDate, int & day, int & month, int & year)
{
if (0 == cDate)
{
throw "Error : null string pointer";
}
else
{
if (strlen(cDate) < 8)
{
throw "Error : invalid string format";
}
else
{
char _day[2];
char _month[4];
char _year[5]; // implictely the biggest year we authorize is 99999 ; it should suffice
for (int i = 0; i < 2; ++i)
{
_day[i] = cDate[i];
}
day = atoi(_day); // if fail, Undefined behaviour, see strtol for a more robust cross-platform alternative
char c;
for (int i = 2; i < 5; ++i)
{
c = cDate[i];
_month[i-2] = toupper(c);
}
_month[3] = '\0';
if (0 == strcmp("JAN", _month))
{
month = 1;
}
else if (0 == strcmp("FEB", _month))
{
month = 2;
}
else if (0 == strcmp("MAR", _month))
{
month = 3;
}
else if (0 == strcmp("APR",_month))
{
month = 4;
}
else if (0 == strcmp("MAY", _month))
{
month = 5;
}
else if (0 == strcmp("JUN", _month))
{
month = 6;
}
else if (0 == strcmp("JUL", _month))
{
month = 7;
}
else if (0 == strcmp("AUG", _month))
{
month = 8;
}
else if (0 == strcmp("SEP", _month))
{
month = 9;
}
else if (0 == strcmp("OCT",_month))
{
month = 10;
}
else if (0 == strcmp("NOV", _month))
{
month = 11;
}
else if (0 == strcmp("DEC", _month))
{
month = 12;
}
else
{
throw "Error : invalid month string";
}
for (int i = 5; i < 10; ++i)
{
_year[i-5] = cDate[i];
}
year = atoi(_year);
}
}
}
I finally opted for the function to be a constructor of a Date class, and inspired myself from rici's answer also using strtol as I intended initially (see comment in my initial code) instead of atoi:
#include <cstring> // for strlen
#include <ctype.h> // for toppuer
#include <stdlib.h>
int up(char c)
{
return toupper((unsigned char)(c));
}
Date::Date(const char * cDate)
{
if (0 == cDate)
{
throw "Error : null string pointer";
}
else
{
if (strlen(cDate) < 8)
{
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case"; // for now, valid format is 24Oct1979
}
else
{
char * ppEnd;
int day = strtol(cDate, &ppEnd, 10);
if (0 == day)
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case";
m_Day = day;
char cMonth[4];
int month;
memcpy(cMonth, &ppEnd[0], 3);
switch (up(cMonth[0]))
{
case 'A':
{
switch (up(cMonth[1]))
{
case 'P': if (up(cMonth[2]) == 'R') month = 4;
break;
case 'U': if (up(cMonth[2]) == 'G') month = 8;
break;
}
break;
}
case 'D':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'C')
month = 12;
break;
}
case 'F':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'B')
month = 2;
break;
}
case 'J':
{
switch (up(cMonth[1]))
{
case 'A': if (up(cMonth[2]) == 'N')
month = 1;
break;
case 'U': switch (up(cMonth[2]))
{
case 'N': month = 6;
case 'L': month = 7;
}
break;
}
break;
}
case 'M':
{
if (up(cMonth[1]) == 'A')
{
switch (up(cMonth[2]))
{
case 'R': month = 3;
case 'Y': month = 5;
}
}
break;
}
case 'N':
{
if (up(cMonth[1]) == 'O' && up(cMonth[2]) == 'V') month = 11;
break;
}
case 'O':
{
if (up(cMonth[1]) == 'C' && up(cMonth[2]) == 'T') month = 10;
break;
}
case 'S':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'P') month = 9;
break;
}
}
m_Month = (Month)month;
int year = strtol(ppEnd + 3, &ppEnd, 10);
if (0 == year)
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case";
m_Year = year;
updateSerial();
}
}
}
Remark. Being lazy, I did not throw everywhere I should in the "month" part of the code.
If your system is Posix-compatible, then you could just use strptime with the format %d%b%Y:
bool Parse(const char* date, int& day, int& month, int& year) {
struct tm components;
const char* rest = strptime(date, "%d%b%Y", &components);
if (rest == NULL || *rest != '\0') return false;
day = components.tm_mday;
month = components.tm_mon;
year = components.tm_year + 1900;
return true;
}
That is likely to be as fast as a naive parser, and it is certainly a lot easier to write :)
Otherwise, you should use strtol rather than atoi, since it will let you know both whether the parse was successful and where the next character is. And if you want to parse the month names quickly, you'll want to build a trie, either as a table or directly in code (the table is probably faster, fwiw):
static int up(char c) { return toupper((unsigned char)(c)); }
int parseMonth(const char* p) {
switch (up(p[0])) {
case 'A': {
switch (up(p[1])) {
case 'P': if (up(p[2]) == 'R') return 4;
break;
case 'U': if (up(p[2]) == 'G') return 8;
break;
}
break;
}
case 'D': {
if (up(p[1]) == 'E' && up(p[2]) == 'C') return 12;
break;
}
case 'F': {
if (up(p[1]) == 'E' && up(p[2]) == 'B') return 2;
break;
}
case 'J': {
switch (up(p[1])) {
case 'A': if (up(p[2]) == 'N') return 1;
break;
case 'U': switch (up(p[2])) {
case 'N': return 6;
case 'L': return 7;
}
break;
}
break;
}
case 'M': {
if (up(p[1]) == 'A') {
switch (up(p[2])) {
case 'R': return 3;
case 'Y': return 5;
}
}
break;
}
case 'N': {
if (up(p[1]) == 'O' && up(p[2]) == 'V') return 11;
break;
}
case 'O': {
if (up(p[1]) == 'C' && up(p[2]) == 'T') return 10;
break;
}
case 'S': {
if (up(p[1]) == 'E' && up(p[2]) == 'P') return 9;
break;
}
}
return -1;
}
Solution based on boost Spirit X3
#include <iostream>
#include <memory>
#include <string.h>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
namespace x3 = boost::spirit::x3;
struct Month_ : x3::symbols<std::uint8_t>
{
Month_()
{
add
("Jan", 1)
("Feb", 2)
("Mar", 3)
("Apr", 4)
("May", 5)
("Jun", 6)
("Jul", 7)
("Aug", 8)
("Sep", 9)
("Oct", 10)
("Nov", 11)
("Dec", 12);
}
}month_;
using ResultType = std::tuple<int, int, int>;
namespace grammar
{
using namespace x3;
auto const start_ = rule<struct start_, ResultType>{"start"}
= int_ >> month_ >> int_;
};
int main(int athc, char* argv[])
{
std::string str{"25Dec2016"};
auto beg = std::begin(str);
auto end = std::end(str);
ResultType res;
auto ret = x3::parse(beg,end, grammar::start_, res);
if(ret && (beg==end) )
{
std::cout << "parse done :" << std::get<0>(res) << " " << std::get<1>(res) << " " << std::get<2>(res) << "\n";
}
else
{
std::cout << "parse failed '" << std::string(beg, std::next(beg, 10)) << "'\n";
}
return 0;
}
Note, this works also with char*
Live On Coliru
Here are remarks on your code and some possible simplifications:
You can remove the else branches from the error handling if tests that throw an exception. As a matter of fact, you could just return a completion status instead of throwing exceptions.
Structured exceptions would be more precise than just throwing strings, but I'm not a C++ expert.
If the input string is null-terminated after the year part, there is no need to extract the number fields, but you might want to verify correct formatting.
Using an array to match the month part would greatly simplify that part.
Here is a simpler version:
typedef enum ParseStatus {
Parse_OK = 0,
Parse_NullStringPointer,
Parse_InvalidStringFormat,
Parse_InvalidDayNumber,
Parse_InvalidMonthName,
Parse_InvalidYearNumber
} ParseStatus;
ParseStatus Parse(const char *cDate, int &day, int &month, int &year) {
static const char months[4][12] = {
"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
};
static const int maxday[12] = {
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
char mon[4];
unsigned int dd, mm, yyyy;
char c;
if (!cDate) {
return Parse_NullStringPointer;
}
/* Using sscanf for a simple solution.
* If the string has the correct form, `sscanf` will convert 3 fields.
* extra spaces will be accepted and ignored.
*/
if (sscanf(cDate, "%u%3s%u%c", &dd, mon, &yyyy, &c) != 3) {
return Parse_InvalidStringFormat;
}
/* If you have `strupr(char *);`, you could use it here */
mon[0] = toupper((unsigned char)mon[0];
mon[1] = toupper((unsigned char)mon[1];
mon[2] = toupper((unsigned char)mon[2];
for (mm = 0; mm < 12; mm++) {
if (!strcmp(mon, months[mm]))
break;
}
if (mm >= 12) {
return Parse_InvalidMonthName;
}
/* Further validation on the day number */
if (dd < 1 || dd > 31 || dd > maxday[mm]) {
return Parse_InvalidDayNumber;
}
/* check for leap year, assuming gregorian calendar */
if (dd == 29 && mm == 1 &&
(yyyy % 4 != 0 || (yyyy % 100 == 0 && yyyy % 400 != 0)) {
return Parse_InvalidDayNumber;
}
/* check some limits for the year */
if (yyyy < 1 || yyyy > 9999) {
return Parse_InvalidYearNumber;
}
day = dd;
month = mm + 1;
year = yyyy;
return Parse_OK;
}
If you do not want to use sscanf(), you can use strtol() this way, but it is more cumbersome:
char *p;
int i;
dd = strtol(cDate, &p, 10);
for (i = 0; i < 3 && isalpha((unsigned char)p[i]); i++) {
mon[i] = toupper((unsigned char)p[i]);
}
mon[i] = '\0';
yyyy = strtol(p, &p, 10);
if (*p != '\0') {
return Parse_InvalidStringFormat;
}
When it comes to low level parsing I think std::strtol is your friend because it keeps track of the current pointer position in the string you are parsing. Not that that is critical in this type of string with fixed length components. Also std::strtol has easy error checking.
Also using iterators and algorithms helps to keep things neat and tidy.
This is about as "elegant" as I can make this using low level constructs:
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
void parse(const char* cDate, int& day, int& month, int& year)
{
static const char* months[] =
{
"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
};
char* pos;
day = std::strtol(cDate, &pos, 10);
if(std::distance(cDate, static_cast<const char*>(pos)) != 2)
throw std::runtime_error("bad date format");
char mon[4] = {};
for(auto m = std::begin(mon); m != std::end(mon) - 1; ++m)
*m = std::toupper(*pos++);
auto found = std::find_if(std::begin(months), std::end(months),
[&](const char* m){ return !std::strcmp(mon, m); });
if(found == std::end(months))
throw std::runtime_error("bad month format");
month = std::distance(months, found) + 1;
char* end;
year = std::strtol(pos, &end, 10);
if(std::distance(pos, end) != 4)
throw std::runtime_error("bad year format");
}
int main()
{
try
{
auto s = "25Dec2016";
int d, m, y;
parse(s, d, m, y);
std::cout << d << "/" << m << "/" << y << '\n';
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
N.B. Not heavily tested may contain bugs.
Ok so my C++ knowledge is so little, i've been slowly piecing together a code but in all honesty i'm surprised i've got this far.
Just to outline my task. The user is asked to enter in several notes (musical notes, C-B including Sharps, across 9 octaves) to create a melody line and then again but a bass line. After a note has been entered, a note length must also be
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int notenumber;
struct noteStorage
{
string noteName;
int midiNumber;
int noteLength;
};
///////////////////////////////////////////////////////////////////////////////////////////////
//VALIDATION FOR NOTE NAME
bool ValidateNote(string note)
{
// Step 1: If note name length is less than 2 OR more than 3, return false
if (note.length() <2 || note.length() >3)
{
cout<<"Note length must be 2 or 3 characters\n";
return false;
}
//Step 2: If true, the note must be/(or be) between A and G
else if(tolower(note[0])<'a' || tolower(note[0]) >'g')
{
cout<<"Note must be A-G\n";
return false;
}
//Step 3: If true, the last character must be a digit
else if(isdigit(note[note.length()-1]) == false)
{
cout<<"Last character must be a digit\n";
return false;
}
//Step 4: If note length is 3 note[1] (character 2) must be '#'.
else if(note.length() == 3 && note[1] != '#')
{
"Invalid sharp note\n";
return false;
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////
//VALIDATION FOR NOTE LENGTH
bool ValidateNoteLength (int length)
//Step 1 - If notelength is not a digit, return FALSE
{
if (length == false)
{
cout<<"Note length must be a digit/number, please re-enter";
return false;
}
//Step 2 - If notelength is less than or equal to 0 or more than 16, return FALSE
if (length <= 0 || length > 16)
{
cout<<"Note length value cannot be less than 1 or more than 16, please re-enter";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
int CalculateNoteNumber(string tempName)
{
int Octave;
int Note;
tempName[0] = toupper(tempName[0]);
Octave = ((tempName[tempName.length()-1]) -48) * 12;
if (tempName.length() == 2)
{
if(tempName[0] == 'C')
{
return notenumber = 0;
}
else if(tempName[0] == 'D')
{
return notenumber = 2;
}
else if(tempName[0] == 'E')
{
return notenumber = 4;
}
else if(tempName[0] == 'F')
{
return notenumber = 5;
}
else if(tempName[0] == 'G')
{
return notenumber = 7;
}
else if(tempName[0] == 'A')
{
return notenumber = 9;
}
else
{
return notenumber = 11;
}
}
else if (tempName.length() == 3)
{
if(tempName[0] == 'C')
{
return notenumber = 1;
}
else if(tempName[0] == 'D')
{
return notenumber = 3;
}
else if(tempName[0] == 'F')
{
return notenumber = 6;
}
else if(tempName[0] == 'G')
{
return notenumber = 8;
}
else
{
return notenumber = 10;
}
}
int main();
{
noteStorage noteData[8];
//string note;
for (int i = 0; i < 8; i++)
{
cout<<"Please enter note: " << i << ": ";
while (1)
{
string tempName;
cin>>tempName;
int noteNumber = CalculateNoteNumber(tempName);
if (ValidateNote(tempName) == true)
{
noteData[i].noteName = tempName;
break;
}
else
{
cout << "Please enter correctly: ";
}
} //end first while
cout<<"Please enter note length: ";
while (1)
{
int tempLength;
cin>>tempLength;
if (ValidateNoteLength(tempLength) == true)
{
noteData[i].noteLength = tempLength;
break;
}
else
{
cout << "Please enter correctly: ";
}
}//end while 2
cout<<"Thank you\n";
} //end for
cout<<"Your note and note lengths are: "<<endl;
for (int i = 0; i < 8; i++)
{
cout<<noteData[i].noteName<<"Length: ";
cout<<noteData[i].noteLength<<endl;
}
system("pause");
return 0;
}
entered (with a value in milliseconds). Once the note names and note lengths have been entered, the console then converts the notenames to the corresponding midi numbers, and outputs said midi numbers, note length and note names back to the user.
I've been having the same problem for two days now; everytime I build the solution it comes back with the same error:
"Fatal error C1075, end of file found before last brace '{' was
matched".
If anyone could point me the right way to solving this it would be much appreciated!!
You missed a } at the end of int CalculateNoteNumber(string tempName).
You will also have to remove the ; after int main() for your program to compile.
If you would have formatted your code properly you could have fixed these errors on your own.
I am trying to organize my code by grouping functions in seperate header/source files. I've #included the header file in my main .cpp, but the compiler does not see the functions in convertTypes.cpp. What gives? And how do I use my 'key' typedef globally (so also in the seperated function sources)? Lots of code, sorry.
/*
* NoteMaker.cpp
*
* Created on: Sep 4, 2013
* Author: edwinrietmeijer
*/
typedef struct {
int keyNum;
int keyType;
} key;
#include <iostream>
#include <string>
#include <iomanip>
#include "convertTypes.h"
using namespace std;
const int KEYSET[ ] = { 0, 2, 4, 5, 7, 8, 9 };
int* generateNotes( int, key );
void echoNoteList( const int* , const int, const key );
string getKeyStringFromUser();
int main() {
key keyStruct;
int octave;
int nrOfNotes;
string firstNoteName;
// Get inputs
cout << "What key would you like to generate notes in? ( f, cis, es, etc.)" << endl;
firstNoteName = getKeyStringFromUser();
cout << "In what octave would you like to generate notes? (-1 / 9)" << endl;
cin >> octave;
octave += 1;
cout << "How many notes do you wish to generate?" << endl;
cin >> nrOfNotes;
// create a key data struct from input string
keyStruct = convertKeyStringToKeyStruct( firstNoteName );
// add the starting octave nr to the keyStruct
keyStruct.keyNum += octave * 12;
// generate note list
int* noteList = new int[ nrOfNotes ];
noteList = generateNotes( nrOfNotes, keyStruct );
// echo note list to terminal
echoNoteList( noteList , nrOfNotes, keyStruct);
cin.get();
}
int* generateNotes( int notes, key keyStruct) {
int* newList = new int [notes];
int currNote = keyStruct.keyNum + keyStruct.keyType;
int currDist = 0;
newList[0] = currNote;
for (int i=1; i < notes; i ++) {
currDist = i % 7;
if ( currDist == 0 || currDist == 3 ) // half step or whole step?
{ currNote = currNote + 1; }
else
{ currNote = currNote + 2; }
newList[ i ] = currNote;
}
cout << "Generated list." << endl;
return newList;
}
void echoNoteList( const int* noteList, const int nrOfNotes, const key thisKeyStruct )
{
int currNote;
for (int i = 0; i < nrOfNotes ; i ++) {
currNote = noteList[ i ] % 12;
if ( currNote < 0 )
currNote += 12;
cout << left;
cout << setw(5) << noteList[ i ] << setw( 5 ) << convertToNoteName( currNote, thisKeyStruct.keyType ) << endl;
}
}
string getKeyStringFromUser() {
bool correctInput = false;
string getKeyName;
int keyNum;
while ( ! correctInput ) {
cin >> getKeyName;
cout << endl;
keyNum = getKeyName[ 0 ];
if ( keyNum > 96 && keyNum < 104 ) {
correctInput = true;
}
else
{
cout << "Wrong input. Try again." << endl;
}
}
return getKeyName;
}
convertTypes.h
#ifdef CONVERTTYPES_H
#define CONVERTTYPES_H
std::string convertToNoteName( int, int );
key convertKeyStringToKeyStruct( std::string );
#endif
convertTypes.cpp
/*
* convertTypes.cpp
*
* Created on: Sep 5, 2013
* Author: edwinrietmeijer
*/
#include <string>
#include "convertTypes.h"
using namespace std;
typedef struct {
int keyNum;
int keyType;
} key;
key convertKeyStringToKeyStruct( string firstNote ) {
int stringSize;
int keyType = 0;
char keyChar;
key thisKey;
keyChar = firstNote[ 0 ];
// get key type (flat, sharp, normal)
stringSize = firstNote.size( );
if (stringSize > 1 ) {
switch( firstNote[ 1 ] ) {
case 'e':
keyType = -1; break;
case 's':
keyType = -1; break;
case 'i':
keyType = 1; break;
default:
keyType = 0; break;
}
}
// convert key char to ascii code
int ASkey = keyChar;
thisKey.keyNum = KEYSET[ ASkey - 99 ];
thisKey.keyType = keyType;
return thisKey;
}
string convertToNoteName( int thisNote, int thisKeyType = 0) {
string noteName;
char addKeyType;
switch( thisKeyType ) {
case -1:
addKeyType = 'b'; break;
case 0:
addKeyType =' '; break;
case 1:
addKeyType = '#'; break;
}
switch( thisNote ) {
case 0:
noteName = "C"; break;
case 1:
if( thisKeyType == 1)
noteName = string ("C") + addKeyType;
else
noteName = string("D") + addKeyType; break;
case 2:
noteName = "D"; break;
case 3:
if( thisKeyType == 1)
noteName = string ("D") + addKeyType;
else
noteName = string("E") + addKeyType; break;
case 4:
noteName = "E"; break;
case 5:
noteName = "F"; break;
case 6:
if( thisKeyType == 1)
noteName = string ("F") + addKeyType;
else
noteName = string("G") + addKeyType; break;
case 7:
noteName = "G"; break;
case 8:
if( thisKeyType == 1)
noteName = string ("G") + addKeyType;
else
noteName = string("A") + addKeyType; break;
case 9:
noteName = "A"; break;
case 10:
if( thisKeyType == 1)
noteName = string ("A") + addKeyType;
else
noteName = string("B") + addKeyType; break;
case 11:
noteName = "B"; break;
default:
noteName = "!"; break;
}
return noteName;
}
Change:
#ifdef CONVERTTYPES_H
to:
#ifndef CONVERTTYPES_H
You are effectively compiling-out your definitions.
As to your second point, move this:
typedef struct {
int keyNum;
int keyType;
} key;
into the header file (before its first use there).
However I would warn against using a name like key as it's commonly used as a variable name. I would go for key_t or MySpecialKeyForStuffImDoing (or somesuch).
In addition to #trojanfor's anwer: also create a new NoteMaker.h containing the key structure definition or move the stuct definition to convertTypes.hso that you don't duplicate it in multiple places
After much error shooting, I've got a compiling program that runs, but encounters an issue.
It will hang at a certain point, when it seems to call a function.
Main.ccp
#include <cstdlib>
#include <iostream>
#include "Date.h"
using namespace ::std;
string weekday(Date date);
int main() {
int day, month, year;
cout << "What date (d m y)? ";
cin >> day >> month >> year;
Date event (day, month, year);
cout << ("That was a " + weekday(event));
return 0;
}
string weekday(Date date) {
const string days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"};
Date trial (1, 1, 1);
int weekday = 6;
if (date.precedes(trial)) {
return "Mysteryday";
} else {
while (trial.precedes(date)) {
trial.advance();
weekday = (weekday + 1) % 7;
}
return days[weekday];
}
}
Date.ccp
#include "Date.h"
Date::Date(int day, int month, int year) {
day_ = day;
month_ = month;
year_ = year;
}
int Date::getDay () {
return day_;
}
void Date::setDay (int day) {
day_ = day;
}
int Date::getMonth () {
return month_;
}
void Date::setMonth (int month) {
month_ = month;
}
int Date::getYear () {
return year_;
}
void Date::setYear (int year) {
year_ = year;
}
bool Date::isLeapYear () {
bool lear = false;
if (this->year_ <= 1752) {
if (this->year_ % 4 == 0) {
lear = true;
}
}
else {
lear = false;
}
if (this->year_ > 1752) {
if (this->year_ % 4 == 0 && this->year_ % 100 != 0) {
lear = true;
}
else if (this->year_ % 4 == 0 && this->year_ % 100 == 0 && this->year_ % 400 == 0) {
lear = true;
}
else {
lear = false;
}
}
return lear;
}
int Date::daysInMonth () {
switch (this->month_) {
case 9 :
case 4 :
case 6 :
case 11 :
return 30;
default :
return 31;
case 2 :
return this->isLeapYear() ? 29 : 28;
}
}
void Date::advance () {
this->day_;
if (this->day_ == 3 && this->month_ == 9 && this->year_ == 1752) {
day_ = 14;
month_ = 9;
year_ = 1752;
}
if (this->day_ > this->daysInMonth()) {
this->day_ = 1;
this->month_++;
}
if (this->month_ > 12) {
this->month_ = 1;
this->year_++;
}
}
bool Date::precedes (Date date) {
return this->year_ < date.year_
|| this->year_ == date.year_ && this->month_ < date.month_
|| this->year_ == date.year_ && this->month_ == date.month_ && this->day_ < date.day_;
}
Date.h
#ifndef DATE_H
#define DATE_H
class Date {
public:
Date (int day, int month, int year);
int getDay();
void setDay(int day);
int getMonth();
void setMonth(int month);
int getYear();
void setYear(int year);
bool isLeapYear();
int daysInMonth();
void advance();
bool precedes(Date date);
private:
int day_;
int month_;
int year_;
};
#endif /* DATE_H */
Under the main.ccp, the program seems to hang at this point:
} else {
while (trial.precedes(date)) {
trial.advance();
weekday = (weekday + 1) % 7;
}
If I enter values before 'trial', (Trial is 1 1 1). If I enter 0 0 0, the correct output is displayed of 'Mystery day'.
However any values after the trial value, the program will hang.
It seems to me there is an error somewhere in the Date.ccp on one of the functions such as isLeapYear, advance, or daysInMonth.
Any ideas?
Seems like the advance function
void Date::advance () {
this->day_;
// lots of checks
}
doesn't advance anything.
That will cause the while-condition to never become true.