Repeat a print out value of the last data .

Pages: 12
Currently this code repeats a last value it prints it twice
choiceBB
choiceB
choiceB2
choiceB2
stated I had textfile with:

/*Example of textfile
*
*
My name is the Best thing
MCQ
Hard
1
choiceA
choiceBB
choiceD
choiceD

My name is the Best thing
TF
Hard
1
choiceA
choiceB

My name is the Best thing
MCQ
Hard
1
choiceA
choiceB2
choiceD
choiceD

And if there is a way I can improve this application please let me know.
like methods and data structures and operators to make it fast and effective.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//*Looking forward to add exciting Features to this Console Based application
// 
// If there is any thing one can help with to update and to make this MCQ app great 
// 
// please Address it to me anything or functions I may have to add to make it work well
// 
// I will really appreciate and more 
// 
//Currently planning to add methods like display, play, highscore, addQuestions, 

// Question vary in level and  time based*/

 * */
 //
// Created by Sithesh on 5/27/2021.
//
#include <iostream>
#include <string>
#include <vector>
#include <sstream>

#include "Quiz.h"
using namespace std;
Quiz::Quiz(const string& question, const vector<string>& answers, int correct, const string& level, const string& type) :
        question(question), answers(answers), correct(correct), level(level), type(type) {

}

const string &Quiz::getQuestion() const {
    return question;
}

const string &Quiz::getType() const {
    return type;
}

const vector<string> &Quiz::getAnswers() const {
    return answers;
}

int Quiz::getCorrect() const {
    return correct;
}

const string &Quiz::getLevel() const {
    return level;
}


void load1(istream& in, vector<Quiz>& quiz) {
    string question, A, B, C, D, type, level, emptyLine;
    string correctIndex;
    vector<string> answers;
    if(!in) { //Always test the file open.
        cout<<"Error opening output file"<<endl;
        system("pause");
    }
    while (getline(in, question) && getline(in, type)
           && getline(in, level) && getline(in, correctIndex)){
        //I find this interesting but I am not sure how can I got about it to ensure that thi
        /*Within the text file it can detect which of the questions are true or False and
         * which Are made out of 4 choices and to final add the them in the vector quiz */
        /*getline(in, question);
        getline(in, type );
        getline(in, level);
        getline(in, correctIndex );
        cout<<question<<endl;
        if(type=="TF"){
            getline(in, A);
            getline(in, B);
            cout<<A<<endl;
            answers ={A, B};
        } else{
            getline(in, A );
            getline(in, B);
            getline(in, C);
            getline(in, D);
            answers ={A, B, C, D};
        }
        getline(in, emptyLine);
        quiz.emplace_back(question, answers, stoi(correctIndex),level,type);
*/

//    Trying to use the data from the load(...) method it gives me error of "
       /* terminate called after throwing an instance of 'std::invalid_argument'
        what():  stoi*/

    }
}

void load(istream& in, vector<Quiz>& quiz){
    string question, A, B, C, D, type, level, emptyLine;
    string correctIndex;
    vector<string> answers;
    if(!in) { //Always test the file open.
        cout<<"Error opening output file"<<endl;
        system("pause");
    }
    while(!in.eof()){
        getline(in, question, '\n');
        getline(in, type, '\n');
        getline(in, level, '\n');
        getline(in, correctIndex , '\n');

        if(type=="TF"){
            getline(in, A, '\n');
            getline(in, B, '\n');
            answers ={A, B};

        } else{
            getline(in, A, '\n');
            getline(in, B, '\n');
            getline(in, C, '\n');
            getline(in, D, '\n');
            answers ={A, B, C, D};
        }
        getline(in, emptyLine , '\n');
        quiz.emplace_back(question, answers, stoi(correctIndex),level,type);
        
    }
}

int main(){
    string fileName="example.txt";
    ifstream in(fileName.c_str());
    vector<Quiz> quizzes;
    load(in, quizzes);
    for (int i = 0; i <quizzes.size(); ++i) {
       cout<< quizzes.at(i).getQuestion()<< endl;
       cout<<(quizzes.at(i).getAnswers()).at(quizzes.at(i).getCorrect())<<endl;
    }
    return 0;
}


//The "Quiz.h" File below;
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
//
// Created by Sithe on 5/27/2021.
//

#ifndef COMP315FINALPROJECT_QUIZ_H
#define COMP315FINALPROJECT_QUIZ_H


class Quiz{
public:
    Quiz(){
    }
    Quiz(const string&, const vector<string>&, int, const string& , const string&);
    void askQuestion();
    vector<string> getChoices();
    void display();
    bool getFileContent(string,vector<string>&);

    const string &getQuestion() const;

    const string &getType() const;

    const vector<string> &getAnswers() const;

    int getCorrect() const;

    const string &getLevel() const;

private:
    string question, type;
    vector<string> answers;
    int correct;
    string level;
};


#endif //COMP315FINALPROJECT_QUIZ_H
Last edited on
its the !eof.
you need to check it more often, before each getline, or look into that logic somehow.
you just assume that if you read something, the rest of the data is going to be there, but if you read an empty line at the end of a file, it will jump right into reading nothing from the file, and that has a habit of repeating the last thing you read from the file.

one way to do it is to shove all the lines into a vector up front, eg
while(getline(...))
lines.push_back(line);
..
then
for( auto a:lines) //your old while loop becomes this.
//handle any screwy lines in here.

all that the above does is ensure that your getlines are not attempting to keep going after file hits eof. it moves all the reads out of the logic, and keeps them together, and tests each one.
Last edited on
The quick & dirty 'fix' is to replace L 44-45 with

1
2
3
4
if (in) {
    quiz.emplace_back(question, answers, stoi(correctIndex),level,type);
    cout<<answers.at(1)<<endl;
}


Note that the default term char for getline() is '\n', so there's no need to specify \n.

The constructor is doing a lot of unnecessary copying. Perhaps something like:

1
2
Quiz::Quiz(const string& question_, const vector<string>& answers_, int correct_, const string& level_, const string& type_) :
	question(question_), answers(answers_), correct(correct_), level(level_), type(type_) {}

Last edited on
o



Last edited on
Hello SITHESH,

Thank you for using code tags.

While going over your program in my IDE I 1st noticed you did not post the "Quiz.h" header file. The program will not compile for me with out it.

Then I noticed your "#include"s. "istream" may be working for you, but you should be including "iostream". I find this format works well:
1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <string>
#include <vector>

#include <sstream>
#include <fstream>

#include "Quiz.h"

using namespace std;  // <--- Best not to use. 

The 1st group tends to me the most used includes for a program. And I found that the alphabetical order helpful to remind you what you may have missed.

The 2nd group would be any includes needed for this program.

And the 3rd group is any includes in double quotes.

On one hand the order makes no difference, but I find this to be helpful.

In a way it follows the concept of:
seeplus once wrote:

any required #define statements
std:: headers (eg iostream)
c headers (eg conio.h)
windows headers (eg windows.h)
3rd party headers
user headers



I am having a little problem with the overloaded ctor defined in "main". Not having the proper class definition my guess is not working. Then again you should be using the member initialization list in the class.

In the load function after you define your variables then you check if the file stream is open. First you are checking to late. And 2nd if the if statement is true you continue with the program as if everything is OK when it is not.

In "main" I would suggest this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main()
{
    const string fileName = "example.txt";

    ifstream in(fileName.c_str());

    if (!in)
    { //Always test the file open.
        cout << "\n     Error opening output file\n";

        system("pause");

        return 1;
    }

    vector<Quiz> quizzes;

    load(in, quizzes);

    return 0;
}

Notice how the blank lines make it easier to read.

"in" is nice, but "inFile" and "outFile" is more descriptive for the variable name and easier to follow.

If your IDE and compiler is old you may have to use the ".c_str()" function.
From C++11 on a std::string" will work just fine.

I added the "return 1" because you do not need to continue with the program until you fix the problem.

As jonnin pointed out in the while loop of the "load" function it does not work the way that you are thinking.

Basically you are testing for "eof" before "eof" would be set.

The more common way to use the while loop would be:
1
2
while (getline(in, question) && getline(in, type)
           && getline(in, level) && getline(in, correctIndex))

Or you could just use:
 
while (getline(in, question))

Either way would work.

This way when the read sets the "eof" bit the while loop will fail and you will continue with the next line following the while loop.

I suspect that your compiler is so old that the standard it is using will leave the variables containing the last read and give you 1 extra or a duplicate of the last read.

I noticed with the C++14 standards the when "eof" is set the variables are set:
std::strings are set to empty and numeric variables, and I believe "char"s, are set to (0)zero, but you still get 1 extra.

Without the proper "Quiz.h" file that is as far as I can get with testing.

Andy
I have updated as per your Guides @Ceeplus and Andy Thank you.
I have added the "Quiz.h" now sorry for that.


Trying to get the method to use the while loop it gives me error;
Here is my attempt

I am again not sure why I should not use...
using namespace std.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void load1(istream& in, vector<Quiz>& quiz) {
    string question, A, B, C, D, type, level, emptyLine;
    string correctIndex;
    vector<string> answers;
    if(!in) { //Always test the file open.
        cout<<"Error opening output file"<<endl;
        system("pause");
    }
    while (getline(in, question) && getline(in, type)
           && getline(in, level) && getline(in, correctIndex)){
        //I find this interesting but I am not sure how can I got about it to ensure that thi
        /*Within the text file it can detect which of the questions are true or False and
         * which Are made out of 4 choices and to final add the them in the vector quiz */
        /*getline(in, question);
        getline(in, type );
        getline(in, level);
        getline(in, correctIndex );
        cout<<question<<endl;
        if(type=="TF"){
            getline(in, A);
            getline(in, B);
            cout<<A<<endl;
            answers ={A, B};
        } else{
            getline(in, A );
            getline(in, B);
            getline(in, C);
            getline(in, D);
            answers ={A, B, C, D};
        }
        getline(in, emptyLine);
        quiz.emplace_back(question, answers, stoi(correctIndex),level,type);
*/
        

       
    }
}


terminate called after throwing an instance of 'std::invalid_argument'
  what():  stoi



Last edited on
See my 'fix' above. Unless you want to do a lot more work, that will do the job.

Normally/usually you wouldn't use .eof(). You'd overload operator>> to read a record from the file into a struct corresponding to the file record layout. eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct record {
// data members here
};

std::istream& operator>>(std::istream& is, record& rec)
{
    if (is) {
        //read and process 1 record into rec
    }

    return is;
}

// then in load1
for (record rec; in >> rec; ) {
    // process record rec
}

This method or operator overload seems fine I think I should do research on how it works..

I did initially think of a struct to use but then after some time but now I was more concerned about how will I then add the functions which are relevant like getAnswer, getQustion.

Your method Above @ceeplus was very great and I have used the advice you gave me.
Hello SITHESH,

Your program works fine as is. There are a few small fixes that are needed and some big things that you should not do.

In the header file you should NEVER put using namespace std; in a header file. You are just asking for trouble. Read this:
http://www.lonecpluspluscoder.com/2012/09/22/i-dont-want-to-see-another-using-namespace-xxx-in-a-header-file-ever-again/

And these should help with explaining "using namespace std;":
https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice

https://www.geeksforgeeks.org/using-namespace-std-considered-bad-practice/

Consider this for your header file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
//#include <fstream>  // <--- Not used in this file. Should be included where it is used, the main file.
#include <string>
#include <vector>

//using namespace std;

//
// Created by Sithe on 5/27/2021.
//

#ifndef COMP315FINALPROJECT_QUIZ_H
#define COMP315FINALPROJECT_QUIZ_H

class Quiz
{
    public:
        Quiz(){}
        Quiz(const string&, const vector<string>&, int, const string&, const string&);
        // <--- Your overloaded ctor should be here not in "main", but it does work the way it is.
        
        void askQuestion();

        vector<string> getChoices();

        void display();

        bool getFileContent(string, vector<string>&);

        const string &getQuestion() const;

        const string &getType() const;

        const vector<string> &getAnswers() const;

        int getCorrect() const;

        const string &getLevel() const;

    private:
        string question, type;
        vector<string> answers;
        int correct;
        string level;
};

#endif //COMP315FINALPROJECT_QUIZ_H 


In the main file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <string>
#include <vector>

//#include <sstream>  // <--- Not necessary for what you have here. And not used.
#include <fstream>

using namespace std;  // <--- Moved up to cover the next header file. But should not be used.

#include "Quiz.h"

// <--- This should be in the header file not here.
Quiz::Quiz(const string& question, const vector<string>& answers, int correct, const string& level, const string& type) :
    question(question), answers(answers), correct(correct), level(level), type(type) {}


The class function definitions and code work better in their own ".cpp" file, but OK if you leave then above "main".

The "load1' function works, but needs some comment fixes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
void load1(istream& in, vector<Quiz>& quiz)
{
    string question, A, B, C, D, type, level, emptyLine;
    string correctIndex;
    vector<string> answers;

    if (!in)  // <--- Should be in "main" not here. It is to late to find out that the stream did not open.
    { //Always test the file open.
        cout << "Error opening output file" << endl;
        system("pause");
    }

    while (getline(in, question) && getline(in, type)
           && getline(in, level) && getline(in, correctIndex))
    {
/*
        I find this interesting but I am not sure how can I got about it to ensure that thi
        Within the text file it can detect which of the questions are true or False and
        * which Are made out of 4 choices and to final add the them in the vector quiz
*/

/*
         getline(in, question);
         getline(in, type );
         getline(in, level);
         getline(in, correctIndex );

         cout<<question<<endl;
*/

         if(type=="TF")
         {
             getline(in, A);
             //getline(in, B);

             //cout<<A<<endl;

             answers ={A/*, B*/};
         }
         else
         {
             getline(in, A );
             getline(in, B);
             getline(in, C);
             getline(in, D);
             answers ={A, B, C, D};
         }

         getline(in, emptyLine);

         quiz.emplace_back(question, answers, stoi(correctIndex),level,type);
 
/*
        Trying to use the data from the load(...) method it gives me error of "
        terminate called after throwing an instance of 'std::invalid_argument'
        what():  stoi
*/
    }
}

As a learning experience when using block comments, (/* */), put the opening and closing on a line by its-self until you get use to it.

As the comment says checking that the file is open and usable is being done to late. But it really makes no difference because your program continues whether the file stream is open or not.

The while condition here is the same as lines 22 - 25. The difference between the 2 is that in the while condition "getline(in, question)" should be what causes the "eof" bit on the stream to be set. But if one of the others sets the "eof" bit then you have a problem.


/*
I find this interesting but I am not sure how can I got about it to ensure that thi
Within the text file it can detect which of the questions are true or False and
* which Are made out of 4 choices and to final add the them in the vector quiz
*/


The same you did with the original "load" function.


/*
Trying to use the data from the load(...) method it gives me error of "
terminate called after throwing an instance of 'std::invalid_argument'
what(): stoi
*/


This means that something is off with your reading of the file and that "correctIndex" has a non numeric value which you are trying to a numeric answer.

The "stoi" function is about the only time I use a try/catch, so that the program does not terminate. The try/catch is not really necessary here as long as the input file is consistent and you are reading it properly.

I put a comment on line 34 because the input file does not need 2 lines for a T/F type question. What is in the input file should only be the correct answer, so your vector here will have only 1 element to work with.

If I have missed anything that is a problem let me know.

Andy
This will read the data file and display the data:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>

class Quiz {
public:
	//void askQuestion();
	std::vector<std::string> getChoices() const;
	std::string getQuestion() const;
	std::string getType() const;
	int getCorrect() const;
	std::string getLevel() const;

	friend std::istream& operator>>(std::istream&, Quiz&);
	friend std::ostream& operator<<(std::ostream&, const Quiz&);

private:
	std::string question, type;
	std::vector<std::string> choices;
	int correct {};
	std::string level;
};

std::string Quiz::getQuestion() const { return question; }
std::string Quiz::getType() const { return type; }
std::vector<std::string> Quiz::getChoices() const { return choices; }
int Quiz::getCorrect() const { return correct; }
std::string Quiz::getLevel() const { return level; }

std::istream& operator>>(std::istream& ifs, Quiz& qz) {
	if (ifs) {
		std::getline(ifs, qz.question);
		std::getline(ifs, qz.type);
		std::getline(ifs, qz.level);
		ifs >> qz.correct >> std::ws;

		if (ifs) {
			qz.choices.clear();
			for (std::string ans; std::getline(ifs, ans) && !ans.empty(); qz.choices.emplace_back(ans));
			ifs.clear();
		}
	}

	return ifs;
}

std::ostream& operator<<(std::ostream& os, const Quiz& qz) {
	os << qz.question << '\n' << qz.type << '\n' << qz.level << '\n' << qz.correct << '\n';

	for (const auto& a : qz.choices)
		os << a << "  ";

	return os << '\n';
}

int main()
{
	std::ifstream ifs("example.txt");

	if (!ifs)
		return (std::cout << "Cannot open file\n"), 1;

	std::vector<Quiz> quizzes;

	for (Quiz qz; ifs >> qz; quizzes.emplace_back(qz));

	for (const auto& q : quizzes)
		std::cout << q << '\n';
}



My name is the Best thing
MCQ
Hard
1
choiceA  choiceBB  choiceD  choiceD

My name is the Best thing
TF
Hard
1
choiceA  choiceB

My name is the Best thing
MCQ
Hard
1
choiceA  choiceB2  choiceD  choiceD

Last edited on
@Andy Thank you very much for your input in all and I have greatly learned a lot you did not just help me but ensured that I get the Idea around my implementation thus I am very grateful thank you and yeah NOW I know no "using namespace std";

Well, in line 34 I may have made a mistake in my textfile a
such a True or False aka TF must have A Question, the index of the correct answer in the vector, and the two choices... So line 34 will be necessary.

@seeplus

I have looked very close to your code and I am still not familiar with the operators, thus I will have to go deeper with them as they seem to make the work short and very effective. So thank you very much I really appreciate your assistance and please get tired not as I embark on this journey of C++.

@Thank you. All for your contribution. As edit the project and implement the methods.
👏👏

What don't you understand?
Here are a few changes based on seeplus's code.
- A change the << and >> operators to read() and write() methods since you were having trouble understanding << and >>. But I want to stress that << and >> are the right ways to go.

I changed the name of the class from Quiz to Question. After all, the class represents a single question, not an entire quiz.

Added code for the ask() method.

In my code, the correct member has values 0 to (# choices - 1). I still read/write values
1 to # choices). This illustrates an important concept: the program should store data that's convenient to the program, even if you store it in a file in a different representation. When you do this, change to/from computer-friendly values as close to when you do the I/O as possible. That way the data in the "computer friendly" format always, or nearly always.

A warning: if this is for a class, it's possible that the professor is looking for a class hierarchy, such as:
1
2
3
class Question { ... };
class TFQuestion: public Question {...};
class MultipleChoiceQuestion: public Question {...};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>

using std::cout;
using std::cin;

// A question in a quiz
class Question
{
public:
    unsigned ask();			// ask the question and return the index of the response
    const std::vector < std::string > &getChoices() const;
    std::string getQuestion() const;
    std::string getType() const;
    int getCorrect() const;	// Get the index of the correct answer
    std::string getLevel() const;

    // Read and write to a file. These are a patching pair: if you write the question
    // with write() then you can read it with read(). NOTE: this is for storing the question
    // to a file, not for asking it. Use the ask() method to ask the user.
    bool read(std::istream &is);
    bool write(std::ostream &os) const;


private:
    std::string question, type;
    std::vector < std::string > choices;
    int correct {};
    std::string level;
};

std::string Question::getQuestion()const
{
    return question;
}

std::string Question::getType()const
{
    return type;
}

const std::vector < std::string > &Question::getChoices()const
{
    return choices;
}

int
Question::getCorrect() const
{
    return correct;
}

std::string Question::getLevel()const
{
    return level;
}

bool Question::read(std::istream & ifs)
{
    if (ifs.eof()) return false;

    std::getline(ifs, question);
    std::getline(ifs, type);
    std::getline(ifs, level);
    ifs >> correct >> std::ws;
    --correct;		// convert from human-friendly 1 to n to computer-friendly 0 to n-1

    // If all of that worked then read the choices
    if (ifs) {
	choices.clear();
	for (std::string ans; std::getline(ifs, ans) && !ans.empty();
	     choices.emplace_back(ans));
	ifs >> std::ws;	// skip the trailing whitespace
    }

    return static_cast<bool>(ifs) || ifs.eof();
}

bool Question::write(std::ostream & os) const 
{
    os << question << '\n' << type << '\n' << level << '\n' << 
	correct+1 << '\n';

    for (const auto & a:choices)
	os << a << '\n';
    cout << '\n';
    return static_cast<bool>(os);
}

unsigned Question::ask()
{
    // print the question
    cout << question << '\n';
    for (size_t i=0; i<choices.size(); ++i) {
	cout << '\t' << i+1 << ": " << choices[i] << '\n';
    }
    cout << '\n';

    // Prompt for an answer
    size_t answer;
    while (true) {
	cout << "Enter your answer: ";
	cin >> answer;
	--answer;		// convert to computer-friendly 0 to n-1
	if (cin && answer < choices.size()) {
	    return answer;
	}
	// Invalid answer
	cout << "Please enter a value between 1 and " << choices.size() << '\n';
	cin.clear();
	cin.ignore(1000000, '\n'); // skip through the end of line
    }
}

int
main()
{
    std::vector < Question > quiz;
    std::ifstream ifs("example.txt");
    for (Question qz; qz.read(ifs); quiz.emplace_back(qz));
    ifs.close();
    
    for (const auto & q: quiz)
	q.write(cout);

    // Now ask the questions
    int correct {0};
    for (Question &q : quiz) {
	cout << "-----------------------------------\n";
	int answer = q.ask();
	if (answer == q.getCorrect()) {
	    cout << "Correct!\n";
	    ++correct;
	} else {
	    cout << "I'm sorry. That answer is incorrect. The correct answer is " << q.getCorrect()+1 << '\n';
	}
    }
	
    cout << '\n';
    cout << "You answered " << correct << " out of "
	 << quiz.size() << " questions correctly\n";
}

@seeplus
I really did not understand how the operators come to manipulate everything but I am on the verge of trying to learn them cause I was indeed told to add them but Was not sure how that was possible but through looking at your methods I see how they simplify and manipulate things easily and I believe through more study I will understand them.
Thank you very much.

@dhayden
Thank you very much for your implementation It is really interesting. Especially the read and write methods which I did know they can be used as pair in this manner I will be using them now to write the questions and answers of each choice then that new file we have to be saved for an Evaluator, in this case, can be a teacher and use the very same file if the user wants to review his attempt.

On the matter of Inheritance, I was looking into it but could not figure out how can I use it n this case more in the different classes as per my thoughts to be TF and the MCQ. But I was blocked by methods that I found to be just repetitive the only that Could be useful so then i settle for less.

1
2
3
class Question { ... };
class TFQuestion: public Question {...};
class MultipleChoiceQuestion: public Question {...};


I thought probably I may just jungle on the line of it though by Just introducing the User and the admin Type...


And I am trying to include the template class not sure if it can be useful by using it to sort the questions based on the Hardness to ensure I print probably 10 easier then 10 average and lastly I go for 10 Hard ones...
And last want it to be limited by time though in every if time ends skip the level to the next.
So in all I will be adding these features then come for review from you all in case you do not mind.

//
Edit have been working on the shuffle method which seems very complicated as Wanted to use the Template class. but It does not gives me actual results;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//
// Created by Sithe on 5/31/2021.
//
#include "error.h"
#include <iostream>
#include <sstream>
#include "Vector.h"
#include "random"
template <typename ValueType>
Vector<ValueType>::Vector()
        : elements(nullptr),
          capacity(0),
          count(0){
    // empty
}

template <typename ValueType>
void Vector<ValueType>::shuffle() {
    for (int i = 0; i < count; i++) {
        int j = i + (rand() % count);
        if (i != j) {
            std::swap(elements[i], elements[j]);
        }
    }
}
template <typename ValueType>
const ValueType& Vector<ValueType>::get(int index) const {
    checkIndex(index, 0, count-1, "get");
    return elements[index];
}
template <typename ValueType>
void Vector<ValueType>::checkIndex(int index, int min, int max, std::string prefix) const {
    if (index < min || index > max) {
        std::ostringstream out;
        out << "Vector::" << prefix << ": index of " << index
            << " is outside of valid range ";
        if (size()<1) {
            out << " (empty vector)";
        } else {
            out << "[";
            if (min < max) {
                out << min << ".." << max;
            } else if (min == max) {
                out << min;
            } // else min > max, no range, empty vector
            out << "]";
        }
        std::cout<<" !Error! ";
    }
}
template <typename ValueType>
Vector<ValueType>::Vector(const std::vector<ValueType>& v){
    count = v.size();
    capacity = v.size();
    elements = new ValueType[count];
    for (int i = 0; i < count; i++) {
        elements[i] = v[i];
    }
}
template <typename T>
void shuffle(Vector<T>& v) {
    v.shuffle();
}

#include "Vector.h"

#include <vector>
#ifndef COMP315FINALPROJECT_VECTOR_H
#define COMP315FINALPROJECT_VECTOR_H


template <typename ValueType>
class Vector {
public:
    Vector();
    Vector(const std::vector<ValueType>& list);
    explicit Vector(int n, ValueType value = ValueType());
    void checkIndex(int index, int min, int max, std::string prefix) const;
    void shuffle();
    int size() const;
    const ValueType& get(int index) const;
private:
    ValueType* elements;
    int capacity;
    int count;
};



Above was creating my own Vector but I get this error
 undefined reference to `Vector<Quiz>::Vector(std::vector<Quiz, std::allocator<Quiz> > const&)'
undefined reference to `Vector<Quiz>::shuffle()'


Real not sure what this error is for.
Last edited on
Perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <vector>
#include <algorithm>
#include <sstream>
#include <cstdlib>

template <typename ValueType>
class Vector {
public:
	Vector();
	Vector(const std::vector<ValueType>& list);
	~Vector() { delete[] elements; }

	// To be provided if needed
	Vector(const Vector&) = delete;
	Vector& operator=(const Vector&) = delete;

	explicit Vector(size_t n, ValueType value = ValueType());
	void checkIndex(size_t index, size_t min, size_t max, const std::string& prefix) const;
	void shuffle();
	size_t size() const { return count; }
	const ValueType& get(size_t index) const;

private:
	ValueType* elements {};
	size_t capacity {};
	size_t count {};
};

template <typename ValueType>
Vector<ValueType>::Vector() { }

template<typename ValueType>
Vector<ValueType>::Vector(size_t n, ValueType value) : capacity(n), count(n), elements(new ValueType[n]) {
	std::fill_n(elements, n, value);
}

template <typename ValueType>
void Vector<ValueType>::shuffle() {
	for (size_t i = 0; i < count; ++i) {
		const auto j {i + (std::rand() % count)};

		if (i != j)
			std::swap(elements[i], elements[j]);
	}
}

template <typename ValueType>
const ValueType& Vector<ValueType>::get(size_t index) const {
	checkIndex(index, 0, count - 1, "get");
	return elements[index];
}

template <typename ValueType>
void Vector<ValueType>::checkIndex(size_t index, size_t min, size_t max, const std::string& prefix) const {
	if (index < min || index > max) {
		std::ostringstream out;

		out << "Vector::" << prefix << ": index of " << index << " is outside of valid range ";
		if (size() < 1)
			out << " (empty vector)";
		else {
			out << "[";

			if (min < max)
				out << min << ".." << max;
			else if (min == max)
				out << min;

			out << "]";
		}

		std::cout << " !Error! " << out.str() << '\n';
	}
}

template <typename ValueType>
Vector<ValueType>::Vector(const std::vector<ValueType>& v) : count(v.size()), capacity(v.capacity()), elements(new ValueType[v.size()]) {
	std::copy_n(v.begin(), count, elements);
}

template <typename T>
void shuffle(Vector<T>& v) {
	v.shuffle();
}

int main()
{
	Vector v(10, 1);
}

Thanks @ seeplus I have been trying to run your method I still get similar error as of mine at the beginning.
My posted code above compiles and runs OK with MS VS2019.

What's your main() function?
42:9: error: no match for 'operator!=' (operand types are 'size_t {aka long unsigned int}' and 'const std::initializer_list<const long unsigned int>')
72:3: error: 'cout' is not a member of 'std'
88:9: error: missing template arguments before 'v'


I presume the middle one is just a cut 'n paste error. The first one is easily avoided.
Last edited on
I have tried to resolve the error in different way but still could not resolve it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main(){
    std::vector<std::string> my ={"A", "B","C", "D"};
    Vector<std::string> v(my);
    v.shuffle();
    std::cout<<v.size();
    std::cout<<v.get(3)<< " "<< std::endl;
    std::cout<<v.get(2);


//Trying to shuffle the above String   
 for (int i = 0; i <my.size() ; ++i) {
        std::cout<<v.get(i)<<std::endl;
    }

}
j is going outside the array bounds in the line
const auto j {i + (std::rand() % count)};
So, anything could happen.

To avoid that, you could change it to
size_t j = ( i + std::rand() ) % count;
(noting which bracket the % sign goes outside).


Also, to avoid a sense of deja vu, I would call std::srand() somewhere, once.
Last edited on
Pages: 12