I need to know if there exists a method in ifstream so I can get the name of the file tied to it.
For instance
void some_function(ifstream& fin) {
// here I need get name of file
}
Is there a method in ifstream/ofstream that allows to get that?
As mentioned there's no such method provided by std::fstream and it's derivates. Also std::basic_filebuf doesn't provide such feature.
For simplification I'm using std::fstream instead of std::ifstream/std::ofstream in the following code samples
I would recommend, to manage the underlying file name in a little helper class yourself:
class MyFstream {
public:
MyFstream(const std::string& filename)
: filename_(filename), fs_(filename) {
}
std::fstream& fs() { return fs_; }
const std::string& filename() const { return filename_; }
private:
std::string filename_;
std::fstream fs_;
};
void some_function(MyFstream& fin) {
// here I need get name of file
std::string filename = fin.filename();
}
int main() {
MyFstream fs("MyTextFile.txt");
some_function(fs):
}
Another alternative,- if you can't use another class to pass to some_function() as mentioned above -, may be to use an associative map of fstream* pointers and their associated filenames:
class FileMgr {
public:
std::unique_ptr<std::fstream> createFstream(const std::string& filename) {
std::unique_ptr<std::fstream> newStream(new std::fstream(filename));
fstreamToFilenameMap[newStream.get()] = filename;
return newStream;
}
std::string getFilename(std::fstream* fs) const {
FstreamToFilenameMap::const_iterator found =
fstreamToFilenameMap.find(fs);
if(found != fstreamToFilenameMap.end()) {
return (*found).second;
}
return "";
}
private:
typedef std::map<std::fstream*,std::string> FstreamToFilenameMap;
FstreamToFilenameMap fstreamToFilenameMap;
};
FileMgr fileMgr; // Global instance or singleton
void some_function(std::fstream& fin) {
std::string filename = fileMgr.getFilename(&fin);
}
int main() {
std::unique_ptr<std::fstream> fs = fileMgr.createFstream("MyFile.txt");
some_function(*(fs.get()));
}
No. C++ streams do not save the name or the path of the file.
but, since you need some string to initialize the stream anyway, you can just save it for future use.
No, such a method does not exist.
Related
I have currently a function
int myfun(const int a) {
...
return rval;
}
that performs several actions.
I mean to adapt it to write debug information on its behaviour or not according to some parameter that I can pass.
In the cases I want to write that info, I also want to pass the ofstream to use.
And I want applications that were using myfun to still work with no modifications.
So I would ideally change to
int myfun(const int a, ofstream & ofs) {
...
if (!(ofs == NULL)) {
ofs << ...
}
...
if (!(ofs == NULL)) {
ofs << ...
}
return rval;
}
with a default value similar to &ofs=NULL. I know NULL is not appropriate.
What is an appropriate way of handling this?
Note 1:
I could pass an optional parameter with the output file name, but that is less flexible.
Note 2:
I could also change to
int myfun(const int a, const bool debug_info, ofstream & ofs) {
...
if (debug_info) {
ofs << ...
}
with a default value debug_info=false.
I guess this still requires a default value for ofs as above.
Note 3:
The accepted answer in Default ofstream class argument in function proposes an overload without the ofs parameter.
In my case that may not work, as I mean to not write anything when "ofs=NULL".
Note 4:
This other answer apparently works, but it seems somewhat contrived to me, and I am not sure it provides all the same functionality as with passing an ofs.
Related:
Is there a null std::ostream implementation in C++ or libraries?
I want applications that were using myfun to still work with no modifications.
If so, use an ofs with default nullptr
int myfun(const int a, ofstream *ofs = nullptr)
{
if (ofs != nullptr)
{
// (*ofs) << ... ;
}
// ...
}
You can't use a reference parameter ofstream& ofs for such function because a reference cannot be null.
Make an abstract Logger class. It has a method for logging a message. In derived classes you can add logging to file (ofstream) or simply do nothing. You can use any logger, the implementation of myfun() stays the same.
#include <fstream>
class Logger {
public:
virtual void log(const char*) = 0;
};
class NullLogger: public Logger {
public:
void log(const char*) override {};
};
class FileLogger: public Logger {
public:
FileLogger(std::ofstream& s): ofs(s){}
void log(const char* msg) override {
ofs << msg;
}
private:
std::ofstream& ofs;
};
static NullLogger defaultLogger;
int myfun(const int a, Logger& logger=defaultLogger)
{
logger.log("hello");
// ...
logger.log("asdf");
}
int main(){
std::ofstream ofs;
FileLogger fileLogger(ofs);
NullLogger nullLogger;
myfun(10,fileLogger); // logs to file
myfun(10,nullLogger); // logs nothing
myfun(10); // also logs nothing
return 0;
}
In C++17 there is a solution involving std::optional but since it requires default constructible types, std::reference_wrapper has to be used too.
#include <fstream>
#include <optional>
#include <functional>
int myfun(const int a, std::optional<std::reference_wrapper<std::ofstream>> ofs)
{
if (ofs) {
ofs->get() << "...";
return 1;
}
else{
return 0;
}
}
#include <iostream>
int main(){
std::ofstream file;
//Calling is quite nice.
std::cout<<myfun(10,{file})<<'\n'; //Prints 1
std::cout<<myfun(10,{})<<'\n'; //Prints 0
}
The downside of this solution, although idiomatic, is being verbose and heavy on the syntax in some cases.
I have two classes, Lexer and InputStream. In my main function, I create an instance of Lexer, passing in the string 'filename'. My intention is for Lexer to pass 'filename' into its member variable 'is', so that InputStream::getChar() can read out the characters when Lexer::getString() calls it. I'm not sure how to make this happen, because currently, filename is passed into Lexer just fine, but isn't passed into the constructor of InputStream. How can I get filename into InputStream's constructor?
class InputStream
{
public:
InputStream(string filename)
{
in.open(filename);
}
char getChar()
{
return in.get();
}
char nextChar()
{
return in.peek();
}
private:
ifstream in;
};
class Lexer
{
public:
Lexer(string filename)
{
this->filename = filename;
}
string getString()
{
while (is.nextChar() != EOF)
{
valueSoFar.push_back(is.getChar());
}
}
private:
string valueSoFar;
string filename;
InputStream is{filename};
};
Thanks for your help!
You can initialize Inputstream instance like below within Lexer Class Constructor
class Lexer
{
public:
Lexer(string filename)
: is(filename)
{
this->filename = filename;
}
string getString()
{
valueSoFar.push_back(is.getChar());
}
private:
string valueSoFar;
string filename;
InputStream is;
};
I have a code, which writes a number to std::string using std::ostringstream:
template<class T>
class Converter
{
private:
static std::string s_buffer;
public:
static const char* Out(const T& val)
{
std::ostringstream os;
os << val;
s_buffer = os.str();
return(s_buffer.data());
}
};
The Converter::Out is called a lot. So much that it even shows up in the profiler. And essentially, what happens here is:
An instance of ostringstream is created
It creates a buffer to write to and writes to it
I copy that buffer to the static string and return it
I think, that if I could get the stream to write directly to the static string, thus avoiding the copy, I may get some performance improvement. But how can I do it - std::ostringstream can accept only const std::string in constructor, which would be a preliminary fill, not the buffer to write to.
Maybe Boost has some alternative, though I didn't find one... :(
You can access the buffer of an ostringstream using the rdbuf() method; unfortunately, access to the underlying character buffer is protected. However, you can easily work around that via inheritance:
template<class T>
class Converter
{
private:
static struct Buf : public std::ostringstream, public std::basic_stringbuf<char>
{
Buf() { static_cast<std::basic_ios<char>&>(*this).rdbuf(this); }
void clear() { setp(pbase(), pbase()); }
char const* c_str() { *pptr() = '\0'; return pbase(); }
} s_buf;
public:
static const char* Out(const T& val)
{
s_buf.clear();
s_buf << val;
return s_buf.c_str();
}
};
If Boost is an option, you can use boost::iostreams::filtering_ostream backed by a string or vector<char>: http://lists.boost.org/boost-users/2012/09/75887.php
I have my class which has to have ifstream file in it.
I dont know how to present it in the class header
A:
class MyClass
{
...
ifstream file;
...
}
B:
class MyClass
{
...
ifstream& file;
...
}
I know that ifstream has to get path in the decaleration, so how do I do it?
Also how do I open a file with it?
EDIT:
I want the first way, but how do I use it SYNTAX-ly?
let's say this is the header(part of it)
class MyClass
{
string path;
ifstream file;
public:
MyClass();
void read_from_file();
bool is_file_open();
...
}
funcs
void MyClass::read_from_file()
{
//what do I do to open it???
this->file.open(this->path); //Maybe, IDK
... // ?
}
You more than likely want the first option. The second is a reference to some other ifstream object, rather than an ifstream that belongs to MyClass.
You don't need to give an ifstream a path immediately. You can later call the ifstream's open function and give that a path. However, if you want to open the ifstream immediately on initialisation, you need to use the constructor's initialisation list:
MyClass() : file("filename") { }
If you need the constructor to take the file name, simply do:
MyClass(std::string filename) : file(filename) { }
Initialise it in the constructor:
class my_class {
public:
my_class(char const* path) : file(path) {
}
my_class(std::string const& path) : my_class(path.c_str()) {
}
private:
std::ifstream file;
};
Also see The Definitive C++ Book Guide and List.
I'm trying to make a wrapper for a file - so a small wrapper to fstream. I am in the process of making something that will want to read/write binary and text to file, so I can make model loaders talk in the same way.
I've one question: Why doesn't my file open when I call with this in ObjLoader.cpp ?
Scatterbrain::Log *_file = new Scatterbrain::Log( path, false, true );
if( ! _file->Works() )
std::cout << "Error!!";
Having this in scatterbrain.h ? I'm sure I've included the necessary headers as everything compiles fine, so I figure it must be a semantic problem with the way I wrote the file open call? - it is getting called..
namespace Scatterbrain
{
class Log
{
private:
std::string name;
bool rOnly;
bool isBinary;
int numBytes;
std::fstream file;
protected:
virtual int SizeBytes() { numBytes = (file) ? (int) file->tellg() : 0; return numBytes; }
public:
Log(){}
Log( std::string filename, bool append, bool readOnly )
{
if(FileExists(filename))
{
name = filename;
rOnly = readOnly;
file.open( name.c_str(), ((readOnly) ? int(std::ios::out) : int(std::ios::in |std::ios::out)) | ((append) ? int(std::ios::app) : int(std::ios::trunc)) );
}
}
virtual bool Works() { return (file.is_open() && file.good() ); }
Thanks
There's a lot that could be said about this all, so I'll just put it in comments:
class Log
{
private:
std::string name;
bool rOnly;
std::fstream file;
public:
Log(){}
Log( std::string filename, bool append, bool readOnly)
: name(filename), // Use initializer lists
rOnly(readOnly),
file(filename, (readOnly ? std::ios::out : std::ios::in | std::ios::out) |
(append ? std::ios::app : std::ios::trunc))
{
// Why check if the file exists? Just try to open it...
// Unless, of course, you want to prevent people from creating
// new log files.
}
virtual bool Works()
{
// Just use the fstream's operator bool() to check if it's good
return file;
}
};
In short:
Use member initializer lists
Don't use new... I have no idea why you were in the first place, or why it compiled if it did.
Use the operator bool() function to see if it's "good" or not.