0

When I tried to run this code on c++ 17 it worked but then on c++ it doesn't. While running the first time it just gives me the string value of the last entered elements but when I run it the second time it shows the segmentation fault(core dumped) error. Can someone help me with this?

Here is my code:

#include<iostream>
#include<string>
#include<fstream>
#include<vector>
#include<iomanip>
#include <stdlib.h>
using namespace std;
void show1(string name,string type,string price,string quantity){
  cout<<"=======================================\n";
  cout<<setw(5)<<name<<setw(15)<<type<<setw(10)<<price<<setw(15)<<quantity<<"\n";
  cout<<"========================================\n";
}
template<class T>
void Readfile(T &obj,string fname,string name,string type,string price,string quantity){
  int i=1;
  ifstream f;
  f.open(fname,ios::in);
  f.seekg(0);
  Display(name,type,price,quantity);
  while(!f.eof()){
    f.read((char*)&obj, sizeof(obj));
    
    if(!f.eof())      {
      if(f.tellg()<0)
        {   i=0; break;}
          obj.putdata();
     }
    }
        
  f.close();
}

class Items{
    string name;
    double price;
    int quantity;
    public:
    string itemObj;
        
    void getdata(string obj){
        cout<<"Enter name, price and quantity: \n";
        cin>>name>>price>>quantity;
        itemObj=obj;
    }
    void putdata(){
      cout<<setw(5)<<name<<setw(10)<<itemObj<<setw(12)<<price<<setw(10)<<quantity<<"\n";
    }
    
    void writeToFile(int n,string obj);
    void ReadFile_item();
    int check(string temp_name);
    void refill_Item(int qty);
    double checkout_price(int qty);

    
}item;

class CD:public Items{
 public:
  void Add_CD();
  
}cd;




double Items::checkout_price(int qty){
  if(quantity>=qty){
    quantity-=qty;
    cout<<"File Updated";
    return price*qty;
  }
  else{
    cout<<"\nNot enough stock";
    return 0;
  }
}  
void Items::writeToFile(int n,string obj){
    ofstream f;
    f.open("stock.txt",ios::out|ios::app);
       
    for(int i=0;i<n;i++){
      item.getdata(obj);
      f.write((char*)&item, sizeof(item));
      cout<<"File Updated\n";
    }
    f.close();
       
}

void Items:: ReadFile_item(){
   Readfile(item,"stock.txt","Name","Item type"," Price ","Available stock");
}



void Add_item_obj(string obj){
  int n;
  cout<<"Enter no. of "<<obj<<"'s to add: \n";
  cin>>n;
  item.writeToFile(n,obj);
}
void CD::Add_CD(){
  Add_item_obj("CD");
}



void main_menu(){
  int i=0;
  do{
    cout<<"============================\n";
    cout<<"-------MAIN MENU------------\n";
    cout<<"============================\n";
    cout<<"1. Sell Items\n";
    cout<<"2. Add Items\n";
    cout<<"4. View stock file\n";
    cout<<"5. Exit \n";
    
    cout<<"Enter your choice: ";
    cin>>i;

    switch(i){
      case 1: sell_items();
              break;
      
      case 2: writetoFile(1,"CD");
              break;

      case 3: item.ReadFile_item();
              break;

      

      default: cout<<"Exiting... \n";
                exit(0);
    }
  }while(i!=0);
}

int main(){
   main_menu();
    
    return 0;
}  
  • I haven't read your code, but would suggest you try to compile it with the address sanitizer. If your using gcc or clang, simply add -fsanitize=address to compiler and linker. If you work with MSVC, there is an experimental feature in Visual Studio. The Adress sanitizer will give you some extra information on why there was a segfault – Alexander Daum Jan 05 '21 at 08:34
  • Can you provide a sample of `stock.txt` and `sales.txt`? – D-RAJ Jan 05 '21 at 08:36
  • I think the `Items::name` member is not handled correctly, or not necessarily. If the string is long enough, then the actual string is stored on the heap and only a pointer to that memory is stored in the string. When you read a object, then you might read the pointer value without actually having the string at that memory location. See more about heap here: https://stackoverflow.com/a/80113/6639989 – János Benjamin Antal Jan 05 '21 at 08:39
  • @DhirajWishal sales.txt will be opened only when we sell some item and the info is taken from the stock.txt file. However, a sample of stock.txt would be: HarryPotter 10 20 – shane abraham Jan 05 '21 at 08:47
  • @AlexanderDaum I am currently working with cygwin. So is it possible to use -fsanitize on it? – shane abraham Jan 05 '21 at 08:48
  • @shaneabraham I think its an issue with your trying to load the `HarryPotter` text data to the string object. If its the case, you cannot do it like that. – D-RAJ Jan 05 '21 at 08:56
  • @DhirajWishal So should I use char instead of string or just use getline for input? – shane abraham Jan 05 '21 at 08:59
  • @shaneabraham `f.read((char*)&obj, sizeof(obj));` -- `f.read((char*)&item,sizeof(item));` -- Why do so many new C++ programmers use this broken method of reading (and writing) objects? I see so many questions with this issue, it begs the question of where this is being taught. You cannot read or write non-trivially-copyable objects this way. – PaulMcKenzie Jan 05 '21 at 09:00
  • @shaneabraham You'll have to systematically load the data to and from the text files. You cant just dump all data and retrieve data by passing in the class pointer. You could do that only if you have everything flat on the object (meaning you don't have pointers, except for static arrays). String is basically a pointer to a string which is stored in memory and that's why you get the segmentation fault; the object doesn't have the memory to load the text file's content and even if you do, the memory would be corrupted. – D-RAJ Jan 05 '21 at 09:05
  • @shaneabraham `#include void Readfile(T &obj,string fname,string name,string type,string price,string quantity){ static_assert(std::is_trivially_copyable(), "sorry, you can't read or write T using this function");` -- Add that `static_assert`, and you will see why your code does not work when the compiler refuses to compile the code due to `T` not being trivially copyable. – PaulMcKenzie Jan 05 '21 at 09:07
  • @DhirajWishal Can u provide me with a small example of what u mean....as I only am a beginner to c++? Thank You – shane abraham Jan 05 '21 at 09:50
  • @PaulMcKenzie Do you know any other ways of writing objects to file? – shane abraham Jan 05 '21 at 09:51
  • @shaneabraham This would be a good start: https://www.geeksforgeeks.org/readwrite-class-objects-fromto-file-c/ – D-RAJ Jan 05 '21 at 10:01
  • This is why geeksforgeeks is not a good site to learn C++ from. It maybe an ok site to learn the basic data structures and/or algorithms, but it is an absolutely horrible site to learn how to write proper C++. At that link, it took someone knowledgeable in C++ to straighten out the contributor, which means there is little to no quality control there. – PaulMcKenzie Jan 05 '21 at 14:45

1 Answers1

2
f.read((char*)&obj, sizeof(obj));

This cast is nonsensical. There are two ways to see why this can't possibly work:

  1. Say obj is a std::string. This code must read a fixed number of bytes (otherwise, what is it passing to read?). But what number of bytes is sufficient for any possible contents of a string? Clearly, this can't possibly work. Whatever size gets passed to read, the string's logical can be larger than that.

  2. The contents of memory may contain information only meaningful in the context of the current process. For example const char *foo = "hello"; makes foo hold the address of the string constant "hello". What is the point of writing that address to a file? How would any subsequent execution of any subsequent process no what was stored at that particular address while this process was running?

Your code reads and writes the physical contents of objects to and from files. You need to read and write the logical contents of the objects, not the particular way those contents happen to be encoded in memory during this execution of this process.

To use an analogy, I might remember a particular restaurant as "the place where I ate for my twentieth birthday". But if I'm communicating that restaurant to someone else, telling them how I remember that restaurant isn't helpful. I have to encode it in a way that I know they will understand. That's what file formats do.

To write data to a file, you have to decide on a file format and implement code to convert to and from that format. For things like strings, you either have to decide on a maximum size you will support and always read and write that number of bytes or use some format that permits a variable size.

Have a look at well-known file formats with existing libraries. Text is easiest, but there's also things like XML and JSON.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278