Returning ifstream in a function - c++

Here's probably a very noobish question for you: How (if at all possible) can I return an ifstream from a function?
Basically, I need to obtain the filename of a database from the user, and if the database with that filename does not exist, then I need to create that file for the user. I know how to do that, but only by asking the user to restart the program after creating the file. I wanted to avoid that inconvenience for the user if possible, but the function below does not compile in gcc:
ifstream getFile() {
string fileName;
cout << "Please enter in the name of the file you'd like to open: ";
cin >> fileName;
ifstream first(fileName.c_str());
if(first.fail()) {
cout << "File " << fileName << " not found.\n";
first.close();
ofstream second(fileName.c_str());
cout << "File created.\n";
second.close();
ifstream third(fileName.c_str());
return third; //compiler error here
}
else
return first;
}
EDIT: sorry, forgot to tell you where and what the compiler error was:
main.cpp:45: note: synthesized method ‘std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(const std::basic_ifstream<char, std::char_traits<char> >&)’ first required here
EDIT: I changed the function to return a pointer instead as Remus suggested, and changed the line in main() to "ifstream database = *getFile()"; now I get this error again, but this time in the line in main():
main.cpp:27: note: synthesized method ‘std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(const std::basic_ifstream<char, std::char_traits<char> >&)’ first required here

No, not really. ifstream doesn't have a copy constructor, and if you try to return one, that means copying the instance in your function out to wherever the return needs to go.
The usual workaround is to pass in a reference to one, and modify that reference in your function.
Edit: while that will allow your code to work, it won't fix the basic problem. Right now, you're mixing two rather different responsibilities into a single function: 1) obtain a file name, 2) open or create that file. I think if you separate those, the code will be simpler, and make it much easier to eliminate the source of the problem you're seeing.
Edit 2: Using a reference like this works perfectly well without an operator=. The general idea is something like:
int open_file(char const *name, fstream &stream) {
stream.open(name);
}
The assignment operator is neither necessary nor useful in this case -- we simply use the existing fstream via the reference. An operator= would be necessary if and only if we had to pass the argument to the ctor. With a stream, we can default construct a stream that doesn't connect to a file, and then use open to connect to the file after the fact.

bool checkFileExistence(const string& filename)
{
ifstream f(filename.c_str());
return f.is_open();
}
string getFileName()
{
string filename;
cout << "Please enter in the name of the file you'd like to open: ";
cin >> filename;
return filename;
}
void getFile(string filename, /*out*/ ifstream& file)
{
const bool file_exists = checkFileExistence(filename);
if (!file_exists) {
cout << "File " << filename << " not found." << endl;
filename = getFileName(); // poor style to reset input parameter though
ofstream dummy(filename.c_str();
if (!dummy.is_open()) {
cerr << "Could not create file." << endl;
return;
}
cout << "File created." << endl;
}
file.open(filename.c_str());
}
int main()
{
// ...
ifstream file;
getFile("filename.ext", file);
if (file.is_open()) {
// do any stuff with file
}
// ...
}

ifstream does not support copy construct semantics (that what the error message basically sais), so you cannot return an ifstream. Return an ifstream* instead, and pass to the caller the responsability to delete the allocate pointer.

As an option, ifstream may be extended and custom constructor added to new class.
I've extended it to create test resource stream, encapsulating test resource lookup inside of it.
// test_utils.h
class TestResourceStream : public std::ifstream {
public:
TestResourceStream(const char* file_path);
};
// test_utils.cpp
namespace fs = std::filesystem;
fs::path test_resource_path(const char* file_path) {
fs::path path{std::string{"tests/resources/"} + file_path};
if (!fs::exists(path))
throw std::runtime_error{std::string{"path "} +
fs::absolute(path).c_str() + " does not exist"};
return path;
}
TestResourceStream::TestResourceStream(const char* file_path)
:std::ifstream{test_resource_path(file_path).c_str()} {}
// usage in test
TEST_CASE("parse") {
std::list<GosDump::Expertise> expertises;
TestResourceStream stream("requests/page_response.json");
GosDump::Json::parse(expertises, stream);
REQUIRE(10 == expertises.size());
}

Related

I'm getting Read access violation while accessing objects from file C++.

In the below class error is in the init function where i load the class object I stored in the file to the vector Items.
class Item
{
std::string item_code;
std::string item_name;
std::string unit_name;
unsigned int price_per_unit;
double discount_rate;
static std::vector<Item> Items;
friend std::ostream& operator<< (std::ostream&, Item&);
public:
static void PrintAll();
static void Init();
~Item();
};
Default constructor is the one which reads data from user and writes into file. Below is the code of default constructor.
Item::Item(int a)
{
std::cout << "Item name : ";
std::getline(std::cin, item_name);
std::cout << "Unit (Kg/g/Qty) : ";
std::getline(std::cin, unit_name);
std::cout << "Price per unit : ";
std::cin >> price_per_unit;
std::cout << "Discount Rate : ";
std::cin >> discount_rate;
std::cin.ignore();
std::cout << "Product code (has to be unique) : ";
std::getline(std::cin, item_code);
std::ofstream outfile;
outfile.open("Files\\Items.txt", std::ios::out | std::ios::app);
outfile.write((char*)&(*this), sizeof(Item));
outfile.close();
}
Below is the Init() function for which read access violation is thrown at.
void Item::Init()
{
std::ifstream infile("Files\\Items.txt", std::ios::in);
if (!infile.is_open())
{
std::cout << "Cannot Open File \n";
infile.close();
return;
}
else
{
Item temp;
while (!infile.eof())
{
infile.read((char*)&temp, sizeof(temp));
Item::Items.push_back(temp);
}
}
infile.close();
}
Even though i am checking for eof, read access violation is thrown. Please give me some advice on this issue.
infile.read((char*)&temp, sizeof(temp));
This fills the temp object with junk from the file. It's supposed to contain valid std::string objects and whatever is in the file, it can't possibly be a valid std::string object. If you don't see why, consider that creating a valid std::string object requires allocating memory to hold the string data -- that's what the std::string constructor does. Reading data from a file can't possibly do this.
A file is a stream of bytes. To write data to a file, you need to define some way to represent that data as a stream of bytes. You need to encode its length if it is variable length. To read it back in, you need to handle the variable length case as well. You need to convert the file data to an appropriate internal representation, such as std::string. This is called "serialization".
std::string size is variable, you can try the following definition
char item_code[20];
char item_name[20];
char unit_name[20];

Reference to an fstream object creating an error

I'm getting an error running a program that demonstrates how file stream objects may be passed by reference to functions. The program fails when making a call to the file.open(name, ios::in) function:
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
//function prototypes
bool openFileIn(fstream &, string);
void showContents(fstream &);
int main(){
fstream dataFile;
if (openFileIn(dataFile, "demofile.txt"))
{
cout << "File opened successfully.\n";
cout << "Now reding data from the file. \n\n";
showContents(dataFile);
dataFile.close();
cout << "\nDone. \n";
}
else
cout <<"File open error! "<< endl;
return 0;
}
//******************************************************************
//Definition of function openFileIn. Accepts a reference to an
//fstream object as an argument. The file is opened for input.
//The function returns true upon success, false upon failure.
//******************************************************************
bool openFileIn(fstream& file, string name)
{
file.open(name, ios::in); //error occurs here
if (file.fail())
return false;
else
return true;
}
//*******************************************************************
//Defintion of function showContents. Accepts an fstream reference
//as its argument. Uses a loop to read each name from the file and
//displays it on the screen.
//*******************************************************************
void showContents(fstream &file)
{
string line;
while(file >> line)
{
cout << line << endl;
}
}
The error occurs in the 'openFileIn()' function. The 'file.open()' call fails with an error.
Here is the stacktrace:
The open function overload taking std::string as parameter, is defined starting from C++11.
You are likely to be using an old compiler, so you need to use the old open signature using const char *.
Try:
file.open(name.c_str(), ios::in);
I'm getting an error running a program that demonstrates how file
stream objects may be passed by reference to functions. The program
fails when making a call to the file.open(name, ios::in) function
Note that your program is not compiling at all. What you see in the output is a compiling error, not a stacktrace.

Referencing cout to a variable location (file or cmd)

I would like to write output either to the cmd window or a log file using one function only. The best I found to do this was this treed here.
So this code (minor changes from the referenced source) works for me to 90%:
void outputTest(){
cout << "Testing a new version of output." << endl;
std::ofstream realOutFile;
bool outFileRequested = true;
if(outFileRequested)
realOutFile.open("foo.txt", std::ios::out);
std::ostream & outFile = (outFileRequested ? realOutFile : std::cout);
outFile << "test" << endl;
keep_window_open();
}
Now instead of "foo.txt" I would like to write the file to another location. So I added the following:
string LogFile = config_.outputFiles+config_.projectName; //+"/RoomData.log"
ofstream realOutFile;
if (logFileRequested && config_.saveLogs){
realOutFile.open(LogFile+"/foo.txt", ios::out);
}
std::ostream & outFile = (logFileRequested ? realOutFile : cout);
I also tried passing only a string but in both cases I get that the function call does not match.
Is there a way to fix that?
Why is passing a string different than passing "string content"?
Thanks for your help.
P.S. Sorry I did not het the C++ code formatted properly.
Please see next link for function prototype: http://www.cplusplus.com/reference/fstream/ofstream/open/
open function receives const char* as 1st parameter.
This way it should work ->
string LogFile = config_.outputFiles+config_.projectName; //+"/RoomData.log"
ofstream realOutFile;
if (logFileRequested && config_.saveLogs){
LogFile += "/foo.txt";
realOutFile.open(LogFile.c_str(), ios::out);
}
std::ostream & outFile = (logFileRequested ? realOutFile : cout);

Can I return an object of ofstream to initinialize another object of ofstream?

I would like to open a file in one function return the open file object to main, and use it another
function to populate the file. It appears the compiler is telling me that I"m trying to access a private member of iostream. Is there a way to do this and how?
ofstream& open_outfile()
{
string outfile;
cout << "Please enter the name of the file:";
cin >> outfile;
ofstream ost(outfile.c_str());
if (!ost) error("can't open out file");
return ost;
}
//...............
int main()
{
//...some code
ofstream ost = open_outfile();//attempt 1
//ofstream ost() = open_outfile();//attempt 2
populate_outfile(ost, readings);
keep_window_open();
}
This syntax which I found in "The c++ programming language" seems to work:
ofstream ost = move(open_outfile());
Which is better? Declaring the object in main and passing by reference ost to both function? Or using the move constructor?
The various stream classes have move constructors in C++11, i.e., you can move a std::ofstream from a function and initialize a std::ofstream from it (trying to initialize a std::ostream from an std::ofstream does not work). That is, assuming you compile with -std=c++11 and the versions of libstdc++ shipping with your version of gcc is updated to support these constructors.
You can pass in a reference to an ofstream object into the function:
void open_outfile(/*out*/ ofstream &ost)
{
string filename;
cout << "Please enter the name of the file:";
cin >> filename;
ost.open(filename.c_str());
if (!ost) error("can't open out file");
}
Then, in main:
int main()
{
ofstream ost;
open_outfile(ost);
populate_outfile(ost, readings);
keep_window_open();
}

Problem reading and writing from same file within same program... C++

I'm working on a program, that needs to load data from a text file upon starting and save data to THE SAME text file upon exit. I have the load working, and i have the save working, but for some reason I cant seem to have them both work within the same program.
This doesnt work...
ifstream loadfile("test.txt");
ofstream savefile("test.txt");
void load()
{
string name;
while(!loadfile.eof())
{
getline(loadfile,name);
cout<<"name " << name<<"\n";
}
}
void save(User &name)
{
savefile << name.getName() << endl;
}
Neither does this...
fstream file("test.txt");
void load()
{
string name;
while(! file.eof())
{
getline(file,name);
cout<<"name " << name<<"\n";
}
}
void save(User &name)
{
file << name.getName() << endl;
}
The thing is, I can save a list of names, which works fine... but as soon as i start the program, all the names from the list delete from the text file.
Also, I know that getline() gets the data from the text file as a string type, but how would i convert that to something like an int.
Thanks
Your files are being opened globally and never closed. Try:
void load()
{
ifstream loadfile("test.txt");
string name;
while(!loadfile.eof())
{
getline(loadfile,name);
cout<<"name " << name<<"\n";
}
}
void save(User &name)
{
ofstream savefile("test.txt");
savefile << name.getName() << endl;
}
ofstream savefile("test.txt");
is equivalent to:
ofstream savefile;
savefile.open("test.txt", ios::out|ios::trunc);
That is, you're truncating the file as you open it. So, move the initialization of savefile to happen after you're done with your load call (I'd suggest doing it as late as possible, because if you crash after that initialization and before you're done saving, the save file is corrupted -- normally one writes to a different file and only does the rename at the very end when everything is safe on disk).
In your first sample, you may be running afoul of OS file locking, preventing you from opening the same file for both read and write. Remember to always check for failure when opening a file.
In the second sample, you don't rewind the file pointer. Use seekg to reset the stream pointer before trying to read. Keep in mind that although there's a seperate seekg and seekp, in practice they may refer to the same pointer, so it's always best to seek before switching between read and write.
void load(){
ifstream loadfile("test.txt");
string name;
while(!loadfile.eof())
{
getline(loadfile,name);
cout<<"name " << name<<"\n";
}
loadfile.close(); // Call close() to free up resources again
}
void save(User &name)
{
ofstream savefile("test.txt");
savefile << name.getName() << endl;
savefile.close(); // Call close() to free up resources again
}
From Cplusplus I/O:
"Once this member function is called, the stream object can be used to open another file, and the file is available again to be opened by other processes."