How to make my own cin from istream

I want to make an object of the class std::istream and manually make it point to the console screen, just like std::cin.

I want to do this because I'm planning to make a class which inherits std::istream and overrides some functions.

An OS-specific way would do, but a non-OS-specific way would be better. I use Windows 7.
You may be able to use the ios::rdbuf method to redirect the buffer to your class.
Yes, the aim of streams is that you can swap out which polymorphic read/write buffers they use. You can just grab the one that std::cin uses.
It's not working.

Actually the main intention of my program was to write everything inputted to std::cin to a file. Here is the code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<iostream>
#include<string>
#include<fstream>
using namespace std;

class myistream:public std::istream
{public:
	myistream(streambuf* sb):std::istream(sb){}
	void func()const
	//func() is supposed to write to file.
	//I'll write the correct implementation later.
	{cout<<"Hi"<<endl;}
	template<class typ>
	myistream& operator>>(typ& t)
	{
		((std::istream)(*this))>>t;
		func();
		return *this;
	}
};

int main()
{
	myistream mycin(std::cin.rdbuf());
	string str;
	int x;
	cout<<"Enter a number ";
	mycin>>x;
	cout<<"Enter a string ";
	mycin>>str;
	return 0;
}


Initially I had not put a constructor in my class. The compiler said
error C2512: 'myistream' : no appropriate default constructor available


Then I checked the list of available constructors of std::istream on this site. There was just one:
explicit basic_istream (basic_streambuf<char_type,traits_type>* sb);

So I have now put the constructor in my class but the compiler says
error C2248: 'std::basic_ios<_Elem,_Traits>::basic_ios' :
cannot access private member declared in class  std::basic_ios<_Elem,_Traits>'


I have no idea what to do now.

Why did the authors of <ios> make basic_ios's constructor private?
Last edited on
Could you explain your use case and intended result in more detail?

I *think* you're building an input stream that uses cin's input buffer for input, and automatically writes everything passed through it to a file, but if that's the case, the use case would be more like:

1
2
3
4
5
6
7
8
9
10
11
int main()
{
	myistream mycin("logfile.txt");
	int x;
	cout<<"Enter a number ";
	mycin>>x; // stores x in the file logfile.txt simultaneously

	string str;
	cout<<"Enter a string ";
	mycin>>str;  // stores str in the file logfile.txt simultaneously
}


If that's right, you'd need to write a buffer (not a stream) class and implement an underflow() which would write to the file. Your myistream will just have the constructor to forward the file name to the buffer's constructor (which is what ifstream does)
Cubbi, You guessed my intent correctly and the example explains it right. However, it is difficult to implement it.

I couldn't completely understand this:
you'd need to write a buffer (not a stream) class and implement an underflow() which would write to the file. Your myistream will just have the constructor to forward the file name to the buffer's constructor (which is what ifstream does)

What does writing a buffer mean?
What is underflow()?

Another failed (but not completely failed) attempt:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class pipe
{
	std::istream* isp;	//input stream pointer
	std::ostream* osp;	//output stream pointer
	std::istringstream str_stream;
	std::string str;

public:

	pipe(std::istream* input,std::ostream* output):isp(input),osp(output){}

	template<class typ>
	pipe& operator>>(typ& obj)
	{
		(*isp)>>str;
		(*osp)<<str<<'\n';
		str_stream.clear();
		str_stream.str(str);
		str_stream>>obj;
		return *this;
	}

};

int main()
{
	ofstream ofile("tile.txt");
	pipe mycin(&std::cin,&ofile);
	string str;
	int x;
	cout<<"Enter a number ";
	mycin>>x;
	cout<<x<<endl;
	cout<<"Enter a string ";
	mycin>>str;
	cout<<str<<endl;
}


This attempt works fine but now I'll have to write functions like gcount(), good(),eof(),etc. I don't want to do this. I want to write only those functions which have changed functionality. Hence, I have to inherit from std::istream.

But this poses a problem. std::istream doesn't have a default constructor. It only has this constructor:
explicit basic_istream (basic_streambuf<char_type,traits_type>* sb);

Hence, I must call istream's constructor from within myistream's constructor. Before writing code to output things to a file, I wanted to make sure that no compile errors occur in this constructor thing.

So I made a simple constructor for myistream to just pass its parameter to istream's constructor. Also I didn't write func()'s actual implementation. I haven't written much code yet. I've just written the bare minimum to test this constructor issue. When this issue gets resolved, I will write myistream(const char*) so that Cubbi's example works.
Last edited on
What does writing a buffer mean?

Writing a class derived from basic_streambuf<T> or one of its children.

What is underflow()?

underflow() is the virtual function you need to override to change the behavior of a read.
Note that member operator>>, gcount, and other functions you mentioned aren't even virtual (and also note that a lot of classes rely on non-member operator>>s)

If you were logging cout, you could just use boost's tee_device In this case, but I don't think there's a ready-made solution to tap into an input stream.
Last edited on
Here's a very simple demo of what i'm talking about

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <string>
#include <stdexcept>
#include <fstream>
#include <iostream>

class tapped_streambuf : public std::streambuf
{
    std::streambuf* src;
    std::filebuf tap;
    char ch; // single-byte buffer

    int underflow()
    {
        int n = src->sbumpc(); // read one char from the source
        if(n != EOF)
        {
            ch = n;
            setg(&ch, &ch, &ch+1);
            tap.sputc(ch); // log it
        }
        return n;
    }

 public:
    tapped_streambuf(const std::string& fname)
            : src(std::cin.rdbuf()) // source is hardcoded to std::cin
    {
        tap.open(fname.c_str(), std::ios::out);
        if(!tap.is_open()) throw std::runtime_error("Could not open log file " + fname);
        setg(&ch, &ch+1, &ch+1); // buffer starts out full
    }
};

class logging_cin : public std::istream
{
    tapped_streambuf buf;
 public:
    logging_cin(const std::string& fname) : std::istream(&buf), buf(fname) { }
};

int main()
{
    {
        logging_cin lcin("logfile.txt");
        int x;
        std::cout << "Enter a number ";
        lcin >> x; // stores x in the file logfile.txt simultaneously

        std::string str;
        std::cout << "Enter a string ";
        lcin >> str;  // stores str in the file logfile.txt simultaneously

        std::cout << "You entered " << x << " and '" << str << "'\n";
    } // close the output file, logging_cin could use a .flush() function

    std::cout << "The log file contains:\n";
    std::ifstream test("logfile.txt");
    std::cout << test.rdbuf() << '\n';
}


Note that the derived stream class is nothing but a convenience wrapper - it's how it usually is.
Last edited on
Thank you Cubbi. It is working perfectly.

When I started thinking about this problem, even I thought that I should override that function which actually takes raw input from the console (so that I don't have to override getline, operator>>,ignore, etc. seperately), but I didn't know what function that was. You've done it exactly the way I wanted.

Where did you learn all that from? Even I want to learn all this stuff.
Last edited on
I've just been working with C++ long enough. If you want to learn all this stuff fast, Josuttis has a good book on the C++ library: http://amzn.to/X22SHO which includes I/O streams (it helped me understand a couple things about xalloc, for example). There's also an in-depth book on I/O streams and Locales specifically: http://amzn.to/12zVGvp
Topic archived. No new replies allowed.