0

I have an assignment where we need to create a class object that has many different variables, one of which being a struct. I can't figure out how to fill the struct from my setter function. I've attached some codes snippets I've pulled out of my code. my count_file_line function returns an int value of however many lines are in a txt file. I'm also really new to coding and have been struggling so if it's an obvious answer, sorry

When I run the program and try to cout teachers[I].password from within the setter function, nothing shows up (the "Flag" does show up)

struct teacher{
  int id;
  string password;
  string first_name;
  string last_name;
};

void University::set_teachers(ifstream& inFile){
    int amountOfTeachers = count_file_lines(inFile);
    this->teachers = new teacher[amountOfTeachers];

    for(int i = 0; i < amountOfTeachers; i++){
        inFile >> teachers[i].password;
        inFile >> teachers[i].first_name;
        inFile >> teachers[i].last_name;
cout << "Flag" << endl;

    }
}

  • Wouldn't this depend on the way in which you generated the file? ... – einpoklum Oct 26 '19 at 20:45
  • How do you calculate number of lines? Using `std::getline` in a loop and incrementing a number? Do you rewind the read position using `seekg()` before attempting to read file again or open another `std::fstream`? – Yksisarvinen Oct 26 '19 at 20:51
  • No another student asked about vectors and she said not to use those yet. The number of lines function works fine, tested it and used it in other functions in my program without any problems so that's not the issue. the txt file I'm reading from is "password first_name last_name /n" format. – IsMrBrightside Oct 26 '19 at 21:11
  • Yksisarvinen means that `count_file_lines` has side effect that shifts the cursor position of the stream. So, after it’s completed the `inFile` is in “end of file state”. Further read operators read nothing. – 4xy Oct 26 '19 at 21:52
  • Prefer "Tell, Don't ask". From https://martinfowler.com/bliki/TellDontAsk.html, "Tell-Don't-Ask is a principle that helps people remember that object-orientation is about bundling data with the functions that operate on that data. It reminds us that rather than asking an object for data and acting on that data, we should instead tell an object what to do." Thus, consider removing the setter function (i.e. set_teachers), and instead, refactor the code, add function 'read()' to struct teacher, then use "teacher::read()" to tell each new 'teacher' object to read it's initial data from the file. – 2785528 Oct 26 '19 at 22:20
  • That's totally it! My function was reading to eof then I had to clear the stream. Thank you so much! basically did inFile.clear() then inFile.seekg(0) and that fixed the problem. – IsMrBrightside Oct 27 '19 at 01:12

2 Answers2

0

What you're trying to accomplish is a "de-serialization" of a sequence of teacher objects.

You may be interested in:

Is it possible to serialize and deserialize a class in C++?

for some general-purpose solutions. Note those may (or may not) be a bit "heavy-weight" for what you need to achieve.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
0

Example of using tell, don't ask:

#include <iostream>
using std::cout, std::cerr, std::endl;

#include <iomanip>
using std::setw, std::setfill;

#include <fstream>
using std::ifstream, std::istream; // std::ofstream;

#include <sstream>
using std::stringstream;

#include <string>
using std::string, std::to_string;

#include <cstdint>

#include <cassert>


// stub - this function implemented and tested elsewhere
int count_file_lines(ifstream& inFile)
{
   if (!inFile.good())
      cerr << "\n  !infile.good()" << endl;
   return 5; // for test purposes
}


struct teacher
{
private:
   int    id;       // unique number in record order
   string password;
   string first_name;
   string last_name;
   static int ID;  // init value below

   // note: On my system each string is 32 bytes in this object,
   //       regardless of char count: the chars are in dynamic memory

public:
   teacher() : id(++ID) // password, first_name, last_name
      { }               //        default init is empty string

   ~teacher() = default; // do nothing

   void read(istream& inFile)  // tell instance to read next record
      {
         inFile >> password;
         inFile >> first_name;
         inFile >> last_name;
      }

   void show()
      {
         cout << "\n  show  id:" << id
              << "\n  pw      :" << password
              << "\n  fn      :" << first_name
              << "\n  ln      :" << last_name
              << endl;
      }

};

int teacher::ID = 0;  // compute unique ID number for each record

And a demo of input and output (teacher::read(), teacher::show())

Note use of "stringstream ss;". It is filled using a for loop, and passed to each teacher object using "teacher.read()".

Then the teacher values are echo'd to output using "teacher.show()"

class F834_t
{
   teacher* teachers = nullptr; // do not yet know how many
   ifstream inFile;             // declared, but not opened
   uint     amountOfTeachers = 0; 

   stringstream ss;     // for debug / demo use


public:
   // use default ctor, dtor
   F834_t() = default;
   ~F834_t() = default;

   int exec(int , char** )
      {
         // open infile to count lines

         amountOfTeachers = static_cast<uint>(count_file_lines(inFile)); // use working func
         cout << "\n  teacher count: " << amountOfTeachers << "\n ";   // echo

         // init ss with 5 values
         for (uint i=1; i<=amountOfTeachers; ++i)
            ss << " pw" << i << " fn" << i << " ln" << i << "  ";
         cout << ss.str() << endl;

         teachers = new teacher[amountOfTeachers]; // allocate space, invoke default ctor of each
         assert(teachers);
         cout << "\n     teachers: " << setw(4) << sizeof(teachers) << "  (pointer bytes)"
              << "\n    a teacher: " << setw(4) << sizeof(teacher)  << "  (teacher bytes)"
              << "\n  size of all: " << setw(4) << (amountOfTeachers * sizeof(teacher))
              << "  ( " << setw(3) << sizeof(teacher) << " * " <<  setw(3) << amountOfTeachers << ')'
              << endl;

         // reset stream to start of inFIle, maybe close/open inFile

         for (uint i=0;i<amountOfTeachers; ++i)
         {
            assert(ss.good()); // (inFile.good());

            teachers[i].read(ss); // (inFile);  // tell the object to read the file
         }

         for (uint i=0;i<amountOfTeachers; ++i)
         {
            teachers[i].show(); // tell the object to show its contents
         }

         return 0;
      }
}; // class F834_t


int main(int argc, char* argv[])
{
   F834_t f834;

   return f834.exec(argc, argv);
}

Output - note that a much simplified input stream is created on the fly, and is echo'd early in this output.

  teacher count: 5
  pw1 fn1 ln1   pw2 fn2 ln2   pw3 fn3 ln3   pw4 fn4 ln4   pw5 fn5 ln5  

     teachers:    8  (pointer bytes)
    a teacher:  104  (teacher bytes)
  size of all:  520  ( 104 *   5)

  show  id:1
  pw      :pw1
  fn      :fn1
  ln      :ln1

  show  id:2
  pw      :pw2
  fn      :fn2
  ln      :ln2

  show  id:3
  pw      :pw3
  fn      :fn3
  ln      :ln3

  show  id:4
  pw      :pw4
  fn      :fn4
  ln      :ln4

  show  id:5
  pw      :pw5
  fn      :fn5
  ln      :ln5
2785528
  • 5,438
  • 2
  • 18
  • 20
  • Observe that main() (or in this case, F834_t::exec()) need not know any thing about the teacher or file structure. Only how many objects, and the functional idea of "read()" and "show()". teacher.read() is simpler. teacher.show is simple.The struct complexities are bundled in the class ... no other code needs to know these complexities. – 2785528 Oct 26 '19 at 23:40