-1

How to create objects during runtime based on input from file.

I have few entries in a file in an organized way separated by a space

entryname type satus
entry1 type1  yes
entry2 type2  no
...

I would like to create objects of below class based on number of entries.

#include <iostream>
#include <string>
#include <vector>
#include <fstream>

class Entry{
    std::string name;
    std::string type;
    std::string availability;
};

void getEntriesInfo(std::vector<std::string> &info)
{
    std::string line;
    std::fstream myfile("entries.txt",std::ios::in);
    if (myfile.is_open())
    {
        //remove first line from file.
        getline(myfile,line);
        while ( getline(myfile,line) )
           info.push_back(line);
        myfile.close();
    }
    else std::cout << "Unable to open file"; 
}

int main()
{
    std::vector<std::string> entries_info;
    getEntriesInfo(entries_info);

    for(auto &e: entries_info)
    std::cout<<e<<std::endl;
    return 0;
}

EDIT 1:

I have restructured code a bit using maps and vectors, can it be made more elegant.

If you look at the code , I have not used constructors & destructors properly, (I could not figure out a good way to use), also the setters and not written all kinds of constructors(deep copy or move) etc..

#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
//#include <istream>
#include <map>

class AItem;
AItem makeItem(std::string );
void displayItemInfo(std::vector<std::string> info);

class Item {
    public:
    virtual ~Item() = default;
    virtual std::string name() const = 0 ;
    virtual std::string quota() const = 0 ;
    virtual std::string make() const = 0 ;
};

class AItem : Item{
private:
    std:: string _name;
    std:: string _quota;
    std:: string _make;
public:
    virtual std::string name() const { return _name;} 
    virtual std::string quota() const { return _quota;}
    virtual std::string make() const {return _make;}
    void setName();
    void setQuota();
    void setMake();
    AItem(){}
    AItem(std::string name,std::string quota,std::string make) :_name(name),_quota(quota),_make(make) {}
    friend std::istream& operator>>(std::istream& in,AItem& item);
    friend std::ostream & operator << (std::ostream &out, const AItem & item);
};

void displayItemInfo(std::vector<std::string> info)
{
        for(auto &i_it:info)
            std::cout<<i_it<<std::endl;
}

std::vector<std::string> readItemInfo(const char *str)
{
    std::string line;
    std::vector<std::string> itemInfo;
    std::ifstream file(str, std::ios::in);
    if(file.is_open()){
        getline(file,line);
        while(getline(file,line))
            itemInfo.push_back(line);
        file.close();
    }else{
        std::cout<<"Unable to open"<<str<<std::endl;
    }
   return itemInfo;
}

std::ostream &operator << (std::ostream &out,const AItem& item)
{
    //std::cout <<item.name()<<" "<<item.quota()<<" "<<item.make() <<std::endl;
    return out <<item.name()<<" "<<item.quota()<<" "<<item.make() <<std::endl;
}

std::istream& operator >> (std::istream& in,AItem& item) {
     //in >> item._name >> item._quota >> item._make;
     return in >> item._name >> item._quota >> item._make;
}

AItem makeItem(std::string aitem)
{
    std::stringstream ss(aitem);
    std::vector<std::string> arguments;
    std::string entry;

    while (ss >> entry) {
            arguments.push_back(entry);
    }
    AItem aItem_obj(arguments.at(0),arguments.at(1),arguments.at(2));
    return aItem_obj;
}

std::map<std::string,AItem> createItems(std::vector<std::string> item)
{
   std::map<std::string,AItem> ItemMap;
    for(auto &i_it:item) {
        AItem a_item = makeItem(i_it); 
        ItemMap.insert(std::make_pair(a_item.name(),a_item));
    }
  //  for(auto &m:ItemMap)
  //      std::cout<<m.first<<m.second<<std::endl;
        
    return ItemMap;
}

int main(int argc, char*argv[])
{
    char *str = "player_info.txt";
    std::vector<std::string> info;
    std::map<std::string,AItem> ItemMap;
   // info = readItemInfo(str);
   // displayItemInfo(info);    
   //#if 0
    ItemMap = createItems(readItemInfo(str));
    for(auto &item:ItemMap)
        std::cout<<item.first<<item.second<<std::endl;
   //#endif
        
    return 0;
}

EDIT 2

I have done this way

entry.h

#include <iostream>
#include <string>

class Entry{
private:
    std::string name;
    std::string type;
    std::string availability;
public:
    std::string getName(void) const;
    std::string getType(void) const;
    std::string getAvailability(void) const;

    void setName(const std::string n);
    void setType(const std::string t);
    void setAvailability(const std::string y);

    Entry();
    ~Entry();

    friend std::ostream &operator << (std::ostream &out,const Entry &e);
};

entry.cpp

#include "entry.h"

std::string Entry ::getName(void) const{
    return name;
}
std::string Entry ::getType(void) const{
    return type;
}
std::string Entry ::getAvailability(void) const{
    return availability;
}
void Entry ::setName(const std::string n){
    name = n;
}
void Entry ::setType(const std::string t){
    type = t;
}
void Entry ::setAvailability(const std::string y){
    availability = y;
}
Entry::Entry(){
}
Entry::~Entry(){
}
std::ostream &operator << (std::ostream &out,const Entry &e){
        return out << e.getName()<<" " <<e.getType()<<" "<<e.getAvailability()<<std::endl;
}

main.cpp

#include <fstream>
#include <vector>
#include "entry.h"

void makeEntry(std::string line,std::vector<Entry> &entries)
{
    Entry P1;
    P1.setName(line.substr(0, line.find(' ')));
    P1.setType(line.substr(P1.getName().size(), line.find(' ')));
    P1.setAvailability(line.substr(P1.getName().size()+P1.getType().size(), line.find(' ')));
    //std::cout<<"C:"<<P1.getName()<<P1.getType()<<P1.getAvailability()<<std::endl;
    entries.push_back(P1);
}
void getEntriesInfo(std::vector<std::string> &info)
{
    std::string line;
    std::fstream myfile("entries.txt",std::ios::in);
    if (myfile.is_open())
    {
        //remove first line from file.
        getline(myfile,line);
        while ( getline(myfile,line) )
           info.push_back(line);
        myfile.close();
    }
    else std::cout << "Unable to open file"; 
}

int main()
{
    std::vector<std::string> entries_info;
    std::vector<Entry> entries;
    getEntriesInfo(entries_info);

    for(auto &e: entries_info) {
        std::cout<<e<<std::endl;
        makeEntry(e,entries);
    }
    std::cout<<"OUT"<<std::endl;

    for(auto &e: entries) {
        std::cout<<e<<std::endl;
    }
    return 0;
}
csavvy
  • 761
  • 4
  • 13
  • 3
    Does `entryname` or `type` ever have a space? If not, then simply split the string on spaces, and then you have your three fields needed. – ChrisMM Jan 07 '20 at 12:24
  • I would just have your parsing function return the vector of objects instead. I feel like that's a neater approach. – Rietty Jan 07 '20 at 12:36
  • must master std::map to have all object named array created in run time –  Jan 07 '20 at 12:54
  • So your problem is that you don't know how to parse a text? If so, simply search for that on google, plenty results: https://www.google.com/search?q=how+to+parse+csv , like https://stackoverflow.com/questions/1120140/how-can-i-read-and-parse-csv-files-in-c or https://thispointer.com/how-to-read-data-from-a-csv-file-in-c/ . That said, as a fallback that you can always use in this situations, you can iterate over the characters in a string and then assemble something based on the current character. In such a case, note the existence of `std::string::substr`. But don't reinvent the wheel. – Aziuth Jan 07 '20 at 13:08
  • @Rietty, Is returning vector of objects good approach? As the size of object is generally big and all, we will be returning a collection of objects, I am not sure how the returned values are handled C++. – csavvy Jan 09 '20 at 14:06

1 Answers1

1

You can provide an overload for the stream extraction operator:

std::istream& operator>>(std::istream& in,Entry& e) {
     in >> e.name >> e.type >> e.availability;
}

This works, when the individual entries in the file do not contain spaces. Ie it would fail for

entryName LastName type1 yes

In this case you would need to have a seperator (eg ;) and use std::getline to parse the lines.

Reading entries into a vector is then (assuming you know the number of entries beforehand):

std::vector<Entry> readFromFile(std::istream& myfile,size_t n_entries) {
    std::vector<Entry> entries;
    data.resize( n_entries );
    for (auto& e : entries ) myfile >> e;
    return entries;
}
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • just wondering which approach is better, reading using a global function or make a class and put the information in an object? – csavvy Jan 09 '20 at 12:19
  • @sravs not sure if I understand. If you can you should make a function a free function. What I missed in my answer is that all members of your `Entry` are public and there is no way to access them. So you might want to declare the `operator>>` as `friend` and then you can write its definition inside the class definition (even though it is not a member function) – 463035818_is_not_an_ai Jan 09 '20 at 12:23
  • @sravs actually thats exactly what you have in your own answer. – 463035818_is_not_an_ai Jan 09 '20 at 12:24
  • sorry, I must have mixed up!!, I am not talking about your answer particularly, in general when you have the scenario like read info from file and make objects out of it, what is the good approach? using a global function and put in any container OR make the information as part of some object. example: class { vector info } – csavvy Jan 09 '20 at 12:28
  • @sravs I am not sure what you mean with "global function". Of course you should put functions is some appropriate namespace, never in the global namespace. And what I said above applies in general, when a function can be a free function then prefer to make it a free function instead of a member function. For the objects you read from the file of course you want to define some type – 463035818_is_not_an_ai Jan 09 '20 at 12:30