0

so basically I was trying to save a class inside a .dat file but in my code but it says this error No matching member function for call to 'open' but I put fstream header. I don't know if I'm writing something wrong. I use Xcode 10.

class memberinformation
{
    string name; //name
    long int phonenumber; // phone number
    int memberid; // member id
    public :
    memberinformation()
    { name="not assigned" ;
        phonenumber=0;
        memberid=0;
    }
    int option3();
    int option2();
    int option1();
    int option4();
};



void wrt_file() //file function
{
    memberinformation k;
    fstream f;
    f.open ("information.dat",ios::app,ios::binary) //this is where I get the error. 
            f.write((char*)&k,sizeof(k));




}
  • Well, for one thing the f.open() is missing a semicolon – doug Mar 13 '19 at 19:00
  • 2
    `open` takes 2 parameters, you are passing 3, I guess you wanted to pass `ios::app | ios::binary` instead of `ios::app,ios::binary` as second param. Read about serialization methods, you cannot store `string` object into binary file by `write()`. – rafix07 Mar 13 '19 at 19:00
  • "but it says this error " - which has *what* to do with a `.dat` file? What *is* a `.dat` file? The file extension means *nothing*. You can store a GIF image in a file named `.doc` if you want to. What is your *real* question? – Jesper Juhl Mar 13 '19 at 19:02
  • Also note that when you have succeeded in compiling and running this program and next try to read from the file to restore your object, nasal demons will come and bite you. – Ted Lyngmo Mar 13 '19 at 19:04
  • if you are lucky you just get the `phonenumber` of a nasal demon. In that case, dont call it :P – 463035818_is_not_an_ai Mar 13 '19 at 20:05
  • @user463035818 :-) – Ted Lyngmo Mar 13 '19 at 20:12

4 Answers4

3

You are lucky to have been stopped by a simple error. @Alex44 has already shown how to get rid of the error:

f.open ("information.dat",ios::app|ios::binary); //this is where I get the error. 

But the following line is even worse:

f.write((char*)&k,sizeof(k));

because the compiler will not show any error, while the content of the string will not be saved in the file. std::string is not trivially copiable and because of that, the memberinformation class is not either. So you should not try to write it to a file as raw bytes.

You should instead write a serialization function that writes to a binary stream (just a possible serialization way):

  • phonenumber as a long int (no problem there)
  • memberid as an int (no problem there)
  • name.size as a size_t
  • name.data as name.size bytes
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • I am pretty new to this. I have written a program like this before but it was a very old version of c++. Can you suggest or teach how do i save the class in .dat file which i suppose is a data file. My main goal is create a program which can hold the information of a multiple members. – Vibhor Sagar Mar 13 '19 at 19:29
  • @VibhorSagar I would avoid using binary representation of your data. It's more compact but its more brittle (and harder to debug/understand). Most applications will serialize to a human readable format unless they have a absolute need for speed/space or both. Look into the concept of serialization and define the `operator>>` and `operator<<` for your class `memberinformation` – Martin York Mar 13 '19 at 19:48
2

The other two answers have answered:

  1. Why its not compiling.
  2. Why its a bad idea to write binary objects.

I would suggest that you serialize the object via the standard C++ technique of using the stream operators. This makes writting/reading the objects trivial and usually makes debugging problems easy.

Using the format suggested by @serge-ballesta in his post:

class memberinformation
{
    string    name; //name
    long int  phonenumber; // phone number
    int       memberid; // member id
    public :
      // OLD STUFF GOES HERE

    void swap(memberinformation& other) noexcept
    {
         using std::swap;
         swap(name,        other.name);
         swap(phonenumber, other.phonenumber);
         swap(memberid,    other.memberid);
    }
    friend std::ostream& operator<<(std::ostream& str, memberinformation const& data)
    {
         return str << data.phonenumber << " "
                    << data.memberid    << " "
                    << data.name.size() << " "
                    << data.name        << " ";
    }
    friend std::istream& operator<<(std::istream& str, memberinformation& data)
    {
         memberinformation tmp;
         std::size_t       nameSize
         if (str >> tmp.phonenumber >> tmp.memberid >> nameSize) {
             // All sizes were read correctly.
             tmp.name.resize(nameSize);
             if (str.ignore(1).read(&tmp.name[0], nameSize)) {
                 // ignored the space and read the name correctly.
                 // So now we update the "data" object
                 tmp.swap(data);
             }
         }
         return str;
    }
};

Now in your code:

int main()
{
    memberinformation   object;

    std::cout << object;
    std::cin  >> object;

    std::ofstream  file("Data.dat");
    file << object;
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • I have other functions declared for receive the information for the class. also can you tell me if every time I call the function to input the data in the class, it'll save it and store it like multiple data for multiple members. thank you – Vibhor Sagar Mar 16 '19 at 16:13
  • Also if I decide to setup a function for search up the members, how would you suggest I do? – Vibhor Sagar Mar 16 '19 at 16:17
  • @VibhorSagar I would write a loop to load all the `"memberinformation"` into a `vector< memberinformation>` then you can simply loop over vector to search for it. If you want a more sophisticated search you can use `std::map<>` rather than `std::vector<>` but that is a cuople of steps ahead of you. You can any working code reviewed at https://codereview.stackexchange.com – Martin York Mar 16 '19 at 16:30
1

You miss a semicolon and you need to "bitwise or" your flags:

void wrt_file() //file function
{
    memberinformation k;
    fstream f;
    f.open ("information.dat",ios::app|ios::binary); //this is where I get the error. 
    ...
}
Alex44
  • 3,597
  • 7
  • 39
  • 56
-1

The answers above address your initial problem. I'm going to talk about two more.

First, you probably should f.close() at the end of your method. It may be perfectly fine to let it drop out of scope and clean up from there, but I personally think that's ugly, and I wouldn't count on it.

Second, I wouldn't store the data in binary unless there's a really good reason to do it. It won't be portable. Serge above suggests a serialization method. I'd consider an alternate approach. I'd write to the file in a human readable form such as JSON. Yes, it's a little more work, but...

-If you change your class, your old files will still be readable -They are portable across environments -You can actually look at them and readily understand what they contain

So Serge's suggestions above aren't horrible, but I'd pick a more modern serialization / deserialization style.

Note that your f.write won't work because your object contains other objects, you don't know how they work under the hood. That string, for instance, almost certainly can't be dumped the way you're trying to do it. Plus you aren't only dumping your data.

Also, you should printf the sizeof(k). You might find it interesting information. Try to account for every byte. You could printf the sizeof(k.name) to help you work some of it out.

I'm almost positive the information doing so would surprise you, but I haven't actually done it myself, because I would never try to raw memory copy C++ objects, and that's in effect what you're trying to do.

Joseph Larson
  • 8,530
  • 1
  • 19
  • 36
  • the default sorting criteria is votes, but depening on how I sort, "answers above" can be different ;) – 463035818_is_not_an_ai Mar 13 '19 at 20:07
  • Regarding [close](https://en.cppreference.com/w/cpp/io/basic_ofstream/close): _This function is called by the destructor of basic_ofstream when the stream object goes out of scope and is not usually invoked directly._ – Ted Lyngmo Mar 13 '19 at 20:08
  • 2
    I would consider it bad practice to manually call `close()` That's the whole point of destructors. – Martin York Mar 13 '19 at 20:10
  • Though in general I would not use JSON for simple serialization. It is definately worth the effort if you need any type of cross compatabiity with other tools. Though if you are going to serialize/deserialize via JSON I would not do this manually it is just to much boilerplate. In this case I would use a serialization library like: [ThorsSerializer](https://github.com/Loki-Astari/ThorsSerializer) – Martin York Mar 13 '19 at 20:13
  • "..but I personally think that's ugly, and I wouldn't count on it." I dont think it is ugly at all, but your opinion, but it is definitely something you **should** count on. RAII is a powerful concept and the way to write exception safe code – 463035818_is_not_an_ai Mar 13 '19 at 20:18
  • @user463035818 Good point about sort order of answers. I should have been more specific. Regarding close() -- part of the reason I don't like to count on destructors calling close is because it's sloppy, and there are too many environments where it doesn't work that way., or doesn't work when you think it should. I prefer calling them directly because then I *know* it's done, and when it's done. But ultimately it's a style choice. – Joseph Larson Mar 13 '19 at 20:48
  • @MartinYork The whole point of destructors is to clean up after your classes. Correct. I personally don't want destruction to be the time when heavyweight things like fflush() get called, or worse, when exceptions can be thrown due to stuff like out of disk space. But hey, that's just me. – Joseph Larson Mar 13 '19 at 20:50
  • @JosephLarson what environments do you mean? There must be some misunderstanding, not sure if me or you ;). Destructors being called deterministically is at the heart of c++, it is the essence of RAII, it is the feature of c++ i love most, you actually do know when it is done (even when there are exceptions and no matter how you leave the scope). In an environment without RAII it is not possible to write the c++ i know – 463035818_is_not_an_ai Mar 13 '19 at 20:52
  • i also disagree that it is a style choice. Consider `File f; f.open(); throw "ups"; f.close();`. A well behaved `File` has to `close()` in its destructor, otherwise I'd consider it as broken – 463035818_is_not_an_ai Mar 13 '19 at 20:56
  • @user463035818 In some languages, destructors are called when the garbage collector comes around, not when an object falls out of scope. If one gets used to fstream, but then uses plain old C file methods (open / fopen), there are no classes with destructors to handle this for you. If he extends the method, then the file contents are dangling during additional code. If this code gets copied to a main() method, then the object may never fall out of scope, and the file is never written. If the program aborts for some reason... It might be safe here, but it's not universally safe. – Joseph Larson Mar 13 '19 at 20:56
  • @user463035818 Yes, a well-designed File class cleans up itself. Sure. But open/fopen are not well designed, for instance. The call to close doesn't cost a single thing, but it's a habit I prefer. You don't like it. It's a style difference. We can agree to disagree. – Joseph Larson Mar 13 '19 at 20:58
  • of course it is not univerally safe. Languages are different, writing language agnostic code is ... well its going offtopic, and yes we can agree to disagree :) – 463035818_is_not_an_ai Mar 13 '19 at 20:58
  • Because I end up programming in so many different environments, using so many different libraries, I end up using various design patterns. We all do. File open, file write, explicit file close is one of those patterns. It avoids problems when switching to other environments. – Joseph Larson Mar 13 '19 at 21:12
  • 1
    Should read: https://stackoverflow.com/q/748014/14065 You bring up exceptions. Thats a good point. Because `close()` can generate exceptions. **BUT** the destructor will call close discard any exceptions generated (for std::fstream objects). So still the best way is to let RAII handle it (it does the correct thing). The only time I would argue otherwise is when you have an interactive user interface and providing that information does something useful. – Martin York Mar 13 '19 at 22:28
  • 2
    Yes using the same patterns across multiple languages is a bad habiit (you should stop that). You should learn how to use each language it is supposed to be used. Applying the rules for Java or C# to C++ is going to make you use bad patterns like this. PS Garbage collection is the poor mans resource management (it is far inferior to C++ scope based management). – Martin York Mar 13 '19 at 22:31
  • @MartinYork Ooh, a new debate. If you're programming in Java, you're using the garbage collector. There's nothing wrong with file.close(). It's not ONLY about memory manage / garbage collection, but I also listed other reasons I do it. I wasn't trying to argue. I was trying to offer an alternate perspective. We can disagree. Remember: put any two programmers to solving a problem, and you'll probably get six solutions. :-) – Joseph Larson Mar 14 '19 at 16:44
  • @JosephLarson: Probably. :-) – Martin York Mar 14 '19 at 19:18