1

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";
Ziezi
  • 6,375
  • 3
  • 39
  • 49
  • You need to be more specific about what you're trying to do. Are you trying to put things *into* the CFile_handler with <<, like `file << 1;`? Or are you trying to output the CFile_handler itself into an iostream, like `std::cout << file`? – Puppy Feb 06 '16 at 12:25
  • @Puppy Yes, I am trying to implement both of the above operations. I've included only the declaration of `operator<<` to show how I think it might look, to avoid "what have you tried" questions. – Ziezi Feb 06 '16 at 12:32
  • Why the down vote and close vote? Could you add a comment? – Ziezi Feb 06 '16 at 12:35
  • @simplicisveritatis Well, usually you don't inherit classes from the standard library, but just use them. It's still unclear for me what you want to achieve in particular with your `CFile_handler` class. Wrapping `FILE*` would only make sense if you need these passed from outside your class at construction. – πάντα ῥεῖ Feb 06 '16 at 13:05
  • @πάνταῥεῖ the task is a part of Chapter 19 of C++ Programming: P & P. The previous exercise is _"implement a smart pointer"_, thus I use `FILE*` in the context of smart pointers and automatic resource management. – Ziezi Feb 06 '16 at 13:13
  • 1
    If you want it to work with all existing stream functionality (and not write your own operator<< for everything) then you should be deriving from [istream](http://www.cplusplus.com/reference/iolibrary/) and defining your own [streambuf](http://www.cplusplus.com/reference/streambuf/streambuf/) internally to actually handle the FILE*. – Martin York Feb 06 '16 at 13:30
  • @LokiAstari appreciate your comment! Could you be so kind to formulate it as an answer with a small example or few implementation directions? – Ziezi Feb 06 '16 at 14:21

2 Answers2

1

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:

  1. As a member function

    CFile_handler& CFile_handler::operator<<(int value)
    {
        // Output an integer to the contained file
        return *this;
    }
    
  2. 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;
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Thank you for your time and help. I've edited the question to clarify what functionality I want to achieve. Could you give some remarks in the direction of implementation, i.e. what functions to use to write to the data member `ptr`? – Ziezi Feb 06 '16 at 12:43
  • @simplicisveritatis Assuming that `ptr` is `FILE*`, then in the examples show just do e.g. `fprintf(c_style_stream, "%d", value);` in the function. – Some programmer dude Feb 06 '16 at 12:45
  • @simplicisveritatis For reading, just do the opposite (e.g. `operator>>` instead, take the `value` argument by reference so you can change it, use `fscanf`). – Some programmer dude Feb 06 '16 at 12:46
  • @simplicisveritatis A C++ stream class is nothing more than a simple class with lots of `operator<<` and `operator>>` overloads. Your `CFile_handler` *is* a stream in that regards, and if I understand your assignment then your class should be *the* "stream". – Some programmer dude Feb 06 '16 at 12:48
  • Sorry for the misunderstanding, so I'll have to write `operator<< / >>`s for all the build in types (`int`, `char`, `double`, etc) as a wrapper of C functions (`fprintf()`, `fscanf()`) with the respective formatting settings(`"%d"`, etc), right ? – Ziezi Feb 06 '16 at 12:51
1

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";
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • It works great! I suppose `xsputn` concerns internal buffer mechanics, that probably have to do with writing to the output, which logically implies that I have to write something like `xgetn` to read from input for the `"r"` mode. Anyway, thank you for your helpful example! – Ziezi Feb 06 '16 at 15:46
  • 1
    @simplicisveritatis: see [streambuf](http://www.cplusplus.com/reference/streambuf/streambuf/) look at **Virtual protected member functions** – Martin York Feb 06 '16 at 16:02