I have to:
Define a File_handle class with constructor that takes a string argument (file name), opens the file in the constructor, and closes it in the destructor.
As I understand it, this class is used to provide RAII and I am trying to implement the class using FILE* as basic data structure where my goal basically is to make FILE* a smart pointer:
fileHandler.h:
// Class CFile_handler based on FILE*
class CFile_handler {
public:
CFile_handler(); // default constructor
CFile_handler(const std::string& fileName, // constructor
const std::string& mode);
~CFile_handler (); // destructor
// modifying member function
void open_file(const std::string& fileName,
const std::string& mode);
protected:
typedef FILE* ptr;
private:
CFile_handler(const CFile_handler&); // prevent copy creation
CFile_handler& operator= (const CFile_handler&); // prevent copy assignment
ptr c_style_stream; // data member
};
fileHandler.cpp:
// Class CFile_handler member implementations
// default constuctor
CFile_handler::CFile_handler() {
}
// constructor
CFile_handler::CFile_handler(const std::string& fileName, const std::string& mode = "r")
: c_style_stream( fopen( fileName.c_str(), mode.c_str() ) )
{
}
// destructor
CFile_handler::~CFile_handler() {
if (c_style_stream) fclose(c_style_stream);
}
// Modifying member functions
void CFile_handler::open_file(const std::string& fileName, const std::string& mode) {
c_style_stream = ( fopen( fileName.c_str(), mode.c_str() ) );
}
However, I'm having difficulties in overloading I/O operators<< / >>, as I can't figure out how to implement either of them.
How to overload operator<< such that the class works with iostream objects?
Edit:
As it was proposed by #LokiAstari, it would be better strategy to inherit from istream and define own streambuf.
Could someone give an example or directions for the implementation of streambuf that handles FILE*?
What I want to provide is:
CFile_handler fh("filename.txt", "r");
std::string file_text;
fh >> file_text;
or:
CFile_handler fh("filename.txt", "w");
fh << "write this to file";
You can derive types of the std::streams using std::streambuf to handle the FILE*
#include <iostream>
#include <stdio.h>
class OutputFilePointerStream: public std::ostream
{
class OutputFilePointerStreamBuf: public std::streambuf
{
FILE* buffer;
public:
OutputFilePointerStreamBuf(std::string const& fileName)
{
buffer = fopen(fileName.c_str(), "w");
}
~OutputFilePointerStreamBuf()
{
fclose(buffer);
}
virtual std::streamsize xsputn(const char* s, std::streamsize n) override
{
static char format[30];
sprintf(format, "%%.%lds", n);
fprintf(buffer, format, s);
return n;
}
};
OutputFilePointerStreamBuf buffer;
public:
OutputFilePointerStream(std::string const& fileName)
: std::ostream(nullptr)
, buffer(fileName)
{
rdbuf(&buffer);
}
};
int main()
{
OutputFilePointerStream fileStream("Test");
fileStream << "Testing: " << 5 << "><\n";
fileStream << "Line Again\n";
}
Your operator<< function is to output a CFile_handler object to a C++ output stream, it's not for outputting to a CFile_handler object.
To output to a CFile_handler object you have two choices:
As a member function
CFile_handler& CFile_handler::operator<<(int value)
{
// Output an integer to the contained file
return *this;
}
Or as a non-member function which takes a CFile_handler reference as first argument:
CFile_handler& operator<<(CFile_handler& file, int value)
{
// Output an integer to the file contained in `file`
return file;
}
For both of the above variants, you can then do e.g.
CFile_handler my_file(...);
my_file << 1234;
Related
I have this next class:
#include "OutStream.h"
#include "FileStream.h"
class Logger
{
OutStream _os;
FileStream _fs;
bool _logToScreen;
static unsigned int _line;
public:
Logger(const char *filename, bool logToScreen);
~Logger();
void print(const char *msg);
};
The class OutStream has a default constructor but the class FileStream doesn't have a default constructor.
When I'm trying to code the constructor for Logger i get an error that says "No default constructor exists for class 'FileStream'"
Here is the code of the Logger constructor, where I construct _fs also:
unsigned int Logger::_line = 0;
Logger::Logger(const char* filename, bool logToScreen)
{
// Opening the given file in the FileStream class
_fs = FileStream(filename);
// Declaring the value of _logToScreen
_logToScreen = logToScreen;
}
How can I fix this issue while keeping the _fs private in Logger?
Use the mem-initializer-list. Define the constructor like
Logger::Logger(const char* filename, bool logToScreen) : _fs( filename ), _logToScreen( logToScreen )
{
}
I would like to define something like a new cout, which I can use to log my data:
some_type cout2( Message_type message ){
cout << message;
logfile.save(message);
}
so I will use it with
cout2 << "some message" << endl;
Up to now I was not able to find out how the code above has to look like exactly.
Thanks for your help.
You can create your own logger like:
class Logger {
public:
Logger(std::string const& filename)
: stream_(filename, std::ofstream::out | std::ofstream::app)
{
if (!stream_) {
// Error...
}
}
Logger& operator<<(std::string const& str) {
std::cout << str;
stream_ << str;
return *this;
}
private:
std::ofstream stream_;
};
Generally speaking, classes from C++ standard library are not designed to be derived, and that is true for stream classes.
So IMHO, it is better to specify what method you will need on your cout2 object, and then:
design a class containing a ostream& object, initialized in ctor
delegate actual output to that internal object
do whatever log you need in your methods
You should use templated a operator << to be able to easily process any class that std::ostream can process.
class LogStream {
std::ostream& out;
Logfile logfile;
LogStream(std::ostream& out, /* param for logfile initialization */ ...)
: out(out), logfile(...) {}
... // other useful methods for state
};
template<typename T>
LogStream& operator << (LogStream& out, T val) {
out.out << message;
// should first test whether T is manipulator, and choose whether and how it should be logged
logfile.save(message);
}
You don't want to modify std::cout.
Instead, you want to create a specialised std::streambuf that writes to two buffers rather than one. For example;
#include <streambuf>
template <typename char_type,
typename traits = std::char_traits<char_type> >
class basic_teebuf:
public std::basic_streambuf<char_type, traits>
{
public:
typedef typename traits::int_type int_type;
basic_teebuf(std::basic_streambuf<char_type, traits> * sb1,
std::basic_streambuf<char_type, traits> * sb2)
: sb1(sb1)
, sb2(sb2)
{
}
protected: // override virtuals inherited from std::basic_streambuf
virtual int sync()
{
int const r1 = sb1->pubsync();
int const r2 = sb2->pubsync();
return r1 == 0 && r2 == 0 ? 0 : -1;
}
virtual int_type overflow(int_type c)
{
int_type const eof = traits::eof();
if (traits::eq_int_type(c, eof))
{
return traits::not_eof(c);
}
else
{
char_type const ch = traits::to_char_type(c);
int_type const r1 = sb1->sputc(ch);
int_type const r2 = sb2->sputc(ch);
return
traits::eq_int_type(r1, eof) ||
traits::eq_int_type(r2, eof) ? eof : c;
}
}
private:
std::basic_streambuf<char_type, traits> * sb1;
std::basic_streambuf<char_type, traits> * sb2;
};
typedef basic_teebuf<char> teebuf;
Then you need to create a specialised ostream which uses such a buffer
#include <ostream>
class teestream : public std::ostream
{
public:
// Construct an ostream which tees output to the supplied
// ostreams.
teestream(std::ostream & o1, std::ostream & o2);
private:
teebuf tbuf;
};
teestream::teestream(std::ostream & o1, std::ostream & o2)
: std::ostream(&tbuf)
, tbuf(o1.rdbuf(), o2.rdbuf())
{
}
All the above does is create a specialised std::ostream that uses our specialised buffer, which in turn makes use of two buffers.
Now, our teestream needs to be initialised using two streams. For example
#include <fstream>
#include <iostream>
// include the preceding definition of teestream here
int main()
{
std::ofstream logfile("hello-world.log");
teestream tee(std::cout, logfile);
// tee is now a stream that writes the same output to std::cout and logfile
tee << "Hello, world!\n";
return 0;
}
The advantage of this is that all stream insertions (operator <<) will work with our teestream - even for classes with overloaded versions.
When main() returns, the streams will also be closed cleanly.
I've written the specalised streambuf as a template (std::streambuf is a specialisation of a templated class named std::basic_streambuf). For generality, it would probably be better to do the same with the stream (using the fact that std::ostream is also a specialisation of a templated std::basic_ostream). I leave that sort of generalisation as an exercise.
The logging systems I've seen, built on this idea look something like this:
#define MY_PRINT(x, ...) \
{ \
fprintf(logFile, x, ##__VA_ARGS__); \
fflush(acuLogFile); \
}
And you would use it like:
MY_PRINT("this is just a test\n");
Even though this is the C way of doing things, it is very versatile and work in C++ as well.
You just have to use your newly define print function everywhere in the code.
Maybe you should have an instance based logger with a .log() method, that, depending on implementation can log to file, write to stdout etc, rather than trying to overload free functions from std namespace?
Your intent will be clearer and it will be more object orientated.
In fact here is an example of a pretty cookie-cutter event logger I wrote recently, if that helps you to understand the sort of thing I'm talking about. My design allows for dependnacy injection, as well as keeping boring decisions about how something should be formatted as output and where it should go (stdout or file etc) in the logger.
Of course you can define your own cout.
First you need a class which can handle the << operator and contains an fstream and an ostream (like cout).
class logstream
{
private:
fstream *filestream;
ostream *cout;
public:
logstream(fstream* filestream, ostream* cout)
{
this->cout = cout;
this->filestream = filestream;
}
string operator<< (char* s)
{
*cout << s;
*filestream << s;
return s;
}
};
To use this, simply initialize it with cout and a fstream.
fstream lout = fstream("Filename", ios::app);
logstream cout = logstream(&lout, &std::cout);
And now you have what you wanted.
cout << "message";
EDIT: Don't forget to include
#include <iostream>
#include <fstream>
and
using namespace std;
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.
I'm trying to create a class wrapping fopen() / fclose() / f* methods. I want to use this method for other purposes, that's why I don't want to use smart pointers.
The problem is I don't know when to call fclose() or other 'end of life' functions. Destructor could be called but in the meantime FILE * was copied to another object for example by copy constructor.
I tried writing 'Reference Counter' class (that will be base class of all classes) but unfortunately I'm not able to call pure virtual methods from constructor / destructor.
This is what I've tried:
class ReferenceCounter
{
public:
ReferenceCounter()
{
ReferenceCount = new unsigned int(0);
AddRef();
}
ReferenceCounter(const ReferenceCounter & CopyFrom)
{
ReferenceCount = CopyFrom.ReferenceCount;
AddRef();
}
ReferenceCounter & operator = (const ReferenceCounter & AssignFrom)
{
RemoveRef();
ReferenceCount = AssignFrom.ReferenceCount;
AddRef();
return *this;
}
~ReferenceCounter()
{
RemoveRef();
}
virtual void OnInit() = 0;
virtual void OnDestruct() = 0;
private:
unsigned int * ReferenceCount;
void AddRef()
{
if(++*ReferenceCount == 1)
OnInit();
}
void RemoveRef()
{
if(--*ReferenceCount == 0)
{
OnDestruct();
delete ReferenceCount;
}
}
};
Maybe there is a way to 'overwrite' or 'overlay' one class over another?
Example:
class File
{
public:
File(std::string FileName)
{
F = fopen(FileName.c_str(), ...);
}
~File()
{
fclose(F);
}
private:
FILE * F;
};
int main()
{
File File1("a.txt");
auto File2 = File1;
//SegFault = fclose called twice for File1 and File2
}
There are two solutions here, which work in tandem.
First, don't allow assignment or copying of your "file handle" class.1
class File
{
// C++11 solution: use =delete
public:
File(File & const) = delete;
File & operator=(File & const) = delete;
// C++ < 11 solution: make them private and *don't implement them*:
private:
File(File & const);
File & operator=(File & const);
};
Second, consider only passing references to a single File object. (The compiler won't let you copy File objects anymore, so if you are doing this by accident you will get a compiler error -- this is good, because it will help you identify areas you need to fix.)
If it's too difficult to establish a single point of ownership, then consider instead passing instances using std::shared_ptr<File> which does exactly the kind of reference counting you are trying to implement -- the File will be deleted (and therefore its destructor called) when the last std::shared_ptr is itself destructed.
auto file = std::make_shared(new File{"a.txt"});
auto file2 = file;
// file.use_count() and file2.use_count() are now both 2.
//
// When file2 is destructed this will drop to 1; when file is destructed this will
// drop to 0, and the File object will be deleted.
1 Note that you could probably implement copying using dup(), though semantics for assignment may be a bit trickier -- should assignment close the existing handle and dup() the handle being assigned? If you do implement dup() functionality I would be more inclined to make it a member function instead so that its usage is explicit instead of happening automatically when you may not intend it to.
You can use a shared pointer with fclose as deleter:
#include <cstdio>
#include <memory>
#include <stdexcept>
class File
{
private:
typedef std::shared_ptr<FILE> shared_file;
public:
// You might consider const char*
File(const std::string& FileName, const std::string& Mode)
: F(std::fopen(FileName.c_str(), Mode.c_str()), fclose)
{
if( ! F.get()) {
throw std::runtime_error("File Open Failure");
}
}
private:
shared_file F;
};
int main()
{
File File1("/tmp/test.txt", "r");
auto File2 = File1;
}
I have class Writer that has two ofstream members.
Both streams are associated with the same output file. I'd like to use both streams in Writer::write method, but to make sure that each stream writes to the end of the real output file.
Code
class my_ofstream1:
public ofstream
{
// implement some functions.
// using internal, extended type of streambuf
};
class my_ofstream2:
public ofstream
{
// implement some functions.
// using internal, extended type of streambuf
// (not the same type as found in my_ofstream1)
};
class Writer
{
public:
void open(string path)
{
f1.open(path.c_str(),ios_base::out); f2.open(path.c_str(),ios_base::out);
}
void close()
{
f1.close(); f2.close();
}
void write()
{
string s1 = "some string 1";
string s2 = "some string 2";
f1.write(s1.c_str(), s1.size());
// TBD - ensure stream f2 writes to the end of the actual output file
assert(f1.tellp() == f2.tellp());
f2.write(s2.c_str(), s2.size());
}
private:
my_ofstream1 f1;
my_ofstream1 f2;
};
void main()
{
Writer w;
w.open("some_file.txt");
w.write();
w.close();
}
Questions
How to ensure f2 is in sync with f1? meaning, before writing, stream offset of f2 must be in sync with stream offset of f1 and vice versa?
I can't use function std::ios::rdbuf since each ofstream uses special derived streambuf. so by using rdbuf() I'll lose the necessary functionality.
I tried using some of the techniques found in Synchronizing Streams topic but could not make it happen.
This looks like both of your classes use the filtering streambuf
idiom. In any case, don't derive your classes from
std::ofstream, but directly from ostream, and have them both
use the same std::filebuf object. If you are using the
filtering streambuf idiom, don't let your filtering
streambuf's buffer; leave that to the final std::filebuf.
In other words, your "internal, extended type of streambuf"
should contain a pointer to the final sink, which will be
a filebuf (but your filtering streambufs don't need to know
this). Functions like sync, they just pass on to the final
destination, and they should never establish a buffer
themselves, but pass everything on to the filebuf.
Is this not what you are looking for? This could be easily modified to work with ostreams rather the ofstreams, which is nicer - the actual issue is synchronisation of the buffers. In this code I have simply made the filebuf bf unbuffered and it works fine. Alternatively leave it buffered but include calls to pubsync when switching between my_ofstream's. I don't understand why ios:rdbuf is not available. Are you creating your own streambuf?
#include <iostream>
#include <fstream>
#include <assert.h>
using namespace std;
class my_ofstream1 : public ofstream
{
public:
my_ofstream1& write (const char_type* s, streamsize n)
{
ofstream::write (s, n);
//rdbuf()->pubsync();
return *this;
}
void attach (filebuf* bf){
ios::rdbuf(bf);
}
};
class my_ofstream2 : public ofstream
{
public:
my_ofstream2& write (const char_type* s, streamsize n)
{
ofstream::write (s, n);
//rdbuf()->pubsync();
return *this;
}
void attach (filebuf* bf){
ios::rdbuf(bf);
}
};
class Writer
{
filebuf bf;
my_ofstream1 f1;
my_ofstream1 f2;
public:
void open(string path)
{
bf.open(path.c_str(),ios_base::out);
bf.pubsetbuf(0,0); //unbufferred
f1.attach(&bf); f2.attach(&bf);
}
void close()
{
f1.close(); f2.close();
}
void write()
{
string s1 = "some string 1";
string s2 = "some string 2";
f1.write(s1.c_str(), s1.size());
assert(f1.tellp() == f2.tellp());
f2.write(s2.c_str(), s2.size());
}
};
int main()
{
Writer w;
w.open("some_file.txt");
w.write();
w.close();
return 0;
}