1

How to write an object which contains multiple char pointers to a file and read back?

class Student {
        public:
                char* name;
                char* age;
                int size;

                Student(char* _name, char* _age) {
                        name = _name;
                        age = _age;
                        size = 0;
                        size = strlen(_name) + strlen(_age);
                }
};

I am having an vector array of Student objects ( vector<Student*> ). How to write this vector to a file and then read back ?

Can somebody please help on this? Can we do this without the help of boost::serialization ?

Krishna M
  • 1,135
  • 2
  • 16
  • 32
  • You can of course implement serialization yourself. `boost::serialization` isn't made from magic pixie dust; it's just code written by people. – molbdnilo Dec 09 '15 at 08:46
  • As long as your class Student is a POD, you can iterate over the vector and write the student struct into file as a binary stream and go on appending it to file. While reading you can read sizeof(Student) from the file, cast it to Student struct till EOF. – Arunmu Dec 09 '15 at 08:47
  • 1
    @Arunmu Not a good idea. As you can see, it contains pointers. – molbdnilo Dec 09 '15 at 08:49
  • @molbdnilo Ofcourse he will have dereference it :). He need not serialize the vector, just the data. – Arunmu Dec 09 '15 at 08:50
  • @Arunmu Did you even look at the provided definition of `Student`? – molbdnilo Dec 09 '15 at 08:58
  • @molbdnilo Yes, I do see it uses pointers. Copying the pointed data is what any serialization would do. Both the endpoints, the one serializing it and the one deserializing it should know when and how to free up those resources. – Arunmu Dec 09 '15 at 09:02
  • @molbdnilo Ofcourse, serialization libraries would use different encoding techniques to make it generic, but in this case, I am assuming he needs it to do just for the class Student and handling the cases of extended serialized struct and all is of secondary concern. – Arunmu Dec 09 '15 at 09:03
  • Use standard containers like `std::vector` – Basile Starynkevitch Dec 09 '15 at 09:29
  • A Little OT, why are using a char* for ages in the struct? – Bob__ Dec 09 '15 at 15:11

1 Answers1

2

First of all prefer std::string over char*. You don't need the size then anymore. Also prefer initialization in constructors over assigning values in the constructor body:

class Student {
    public:
        std::string name;
        std::string age;

        Student(const std::string& _name, const std::string& _age) : 
            name{_name}, age{_age}
        {};

        auto size() const -> size_t {
            return name.size() + age.size();
        };
};

Coming back to your question. Before you try to store a complex structure, you have to define the format. In this case I chose a simple CSV with semicolons as field separators and no quotes.

void writeStudents(std::ostream& o, const std::vector<Student*>& students) {
    for(auto s: students) {
        o << s->name << ';' << s->age << '\n';
    }
}
void writeStudents(std::ostream&& o, const std::vector<Student*>& students) {
    writeStudents(o, students);
}

Now you can produce that csv with a `std::ofstream``:

writeStudents(std::ofstream{"myFilename"}, students);

To read it back, you'll have to parse the format you have defined before:

auto readStudents(std::istream& i) -> std::vector<Student*> {
    auto students = std::vector<Student*>;
    auto line = std::string{};

    while( std::getline(i, line) ) {
        auto pos = line.find_first_of(';');
        students.push_back(new Student{std::string{line, 0, pos},
                                       std::string{line, pos+1}});
    }

    return students;
}
auto readStudents(std::istream&& i) -> std::vector<Student*> {
    return readStudents(i);
}

Now you can read your file with an std::ifstream:

auto students = readStudents(std::ifstream{"myFilename"});

Note: All code in this answer is untested. Beware of bugs, of course. I also left the error handling for you, because I did not want to pollute the answer with that noise.

BTW.: How are you making sure, that the memory for your students is managed correctly? Consider to use std::unique_ptr<> or std::shared_ptr<> instead of raw pointers.

cdonat
  • 2,748
  • 16
  • 24
  • You have to take temporary by const reference, it wont compile otherwise. – Arunmu Dec 10 '15 at 06:04
  • @Arunmu good catch. In this case the streams can not be const of course. I changed my anser to have variables for the streams instead of temporaries. – cdonat Dec 10 '15 at 10:04
  • too bad ostream cannot be moved – Arunmu Dec 10 '15 at 11:20
  • @Arunmu Oh, you are correct. `istream` and `ostream` can be moved. So we can have the more beautiful API. Thanks. – cdonat Dec 10 '15 at 11:53
  • No, I wanted to say ostream cannot be moved. http://stackoverflow.com/questions/20774587/why-cant-stdostream-be-moved – Arunmu Dec 10 '15 at 13:10
  • @Arunmu I just tried for this answer. It compiles and runs as expected. – cdonat Dec 10 '15 at 13:48
  • @Arunmu please note, that my code never moves an `std::istream` or `std::ostream`. It just assigns a name to a temporary, so that it can be used as an lvalue reference. – cdonat Dec 11 '15 at 08:53