I want to write a simple istream object, that would simply transform another istream.
I want to only implement readline (which would read a line from the original stream, would process it, and return the processed line), and have some generic code that upon read would use my read line, cache it, and give the required amount of bytes as output.
Is there any class that would allow me to do that?
For example
struct mystream : istreamByReadLine {
istream& s;
mystream(istream& _s):s(_s){}
virtual string getline() {
string line;
getline(s,line);
f(line);
return line;
}
}
class istreamByReadLine : istream {
... // implementing everything needed to be istream compatible, using my
... // getline() virtual method
}
Have you looked at boost.iostreams? It does most of the grunt work for you (possibly not for your exact use case, but for C++ standard library streams in general).
Are you sure this is the way to go? In similar cases, I've
either defined a class (e.g. Line), with a >> operator which
did what I wanted, and read that, e.g.:
Line line
while ( source >> line ) ...
The class itself can be very simple, with just a std::string
member, and an operator std::string() const function which
returns it. All of the filtering work would be done in the
std::istream& operator>>( std::istream&, Line& dest )
function. Or I've installed a filtering streambuf in front of the
normal streambuf ; Boost iostream has good support for
this.
Related
A restriction of the program I am working on is that it should be invoked as: ./a.out < input.txt > output.txt. The input of this program should be read from the first file, and the output should be written to the second.
So, this redirects standard input and output from and to these two files. I could simply, from main() for example, call std::cin and std::cout. However, I have a dedicated component which adapts my input from a file to an intermediate structure that I use elsewhere in my program.
In order to build this struct I could #include <iostream> in this component and read with std::cin from input.txt. However, I don't like the idea of including iostream here, and I am not sure how I would test this.
My issue comes from the I/O redirect, if the executable were invoked with filenames as strings, I would do something along the lines of
InputAdapter inputAdapter;
ifstream infile;
infile.open(filename ,std::ios_base::in);
auto structHoldingParsedInput = inputAdapter.adapt(infile);
How can I achieve something similar here?
I would suggest you make your adapter parameters std::istream& and std::ostream& so you can pass in either the standard std::cin/std::cout or files you open yourself like std::ifstream.
A bit like this:
class InputAdapter
{
public:
void adapt(std::istream& in)
{
// code to convert input to output here
return created_object;
}
};
// ...
InputAdapter inputAdapter;
std::ifstream in("input_file");
auto structHoldingParsedInput = inputAdapter.adapt(in);
Now you are coding to streams rather than files you can use any stream, for example the standard input stream:
auto structHoldingParsedInput = inputAdapter.adapt(std::cin);
And, for testing you could use std::istringstream:
std::istringstream test_stream(R"(
put your test data in here
)");
auto structHoldingParsedInput = inputAdapter.adapt(test_stream);
I've created an fstream object to write info to files.
I write strings to the new file like
fStreamObject << "New message.\n";
because I want each << to print a string to the next line.
I want to be able to set a property and make a call like
fstreamObject << "New message.";
which will write the string to the next line.
Are there flags/settings for fstream objects that allows this to be done?
I've seen the different file modes (i.e. ofstream::in, ofstream::out, etc.), but I couldn't find one that auto writes to a new line. Also, I'm not looking to write my own solution. I want to be able to use a built in feature.
No, there are no readily configurable capabilities of that sort within the standard streams.
You may have to subclass the stream type and fiddle with operator<< to get this to work the way you want, or do it with a helper function of some description:
fstreamObject << nl("New message.");
(but that's hardly easier than just having the \n in there (for a string, anyway).
It depends on what you mean by "setting the stream". If we consider this to be fairly broad then the answer happens to be "yes"!
Here is how:
Create a stream buffer which inserts a newline every time it is flushed, i.e., when sync() is called. Otherwise it just forwards characters.
Change the file stream's stream buffer to use this stream buffer filtering to the file stream's stream buffer.
Set the flag std::ios_base::unitbuf which causes a flush after every [properly written] output operation.
Here are is the example code to do just that:
#include <iostream>
class newlinebuf
: public std::streambuf {
std::ostream* stream;
std::streambuf* sbuf;
int overflow(int c) { return this->sbuf->sputc(c); }
int sync() {
return (this->sbuf->sputc('\n') == std::char_traits::eof()
|| this->sbuf->pubsync() == -1)? -1: 0;
}
public:
newlinebuf(std::ostream& stream)
: stream(&stream)
, sbuf(stream.rdbuf(this)) {
stream << std::unitbuf;
}
~newlinebuf() { this->stream->rdbuf(this->sbuf); }
};
int main() {
newlinebuf sbuf(std::cout);
std::cout << "hello" << "world";
}
Although this approach work, I would recommend against using it! On problem is that all composite output operators, i.e., those using multiple output operators to do their work, will cause multiple newlines. I'm not aware of anything which can be done to prevent this behavior. There isn't anything in the standard library which enables just configuring the stream to do this: you'll need to insert the newline somehow.
No, the C++ streams do not allow that.
There is no way to decide where one insertion stops and the next starts.
For example for custom types, their stream-inserters are often implemented as calls to other stream-inserters and member-functions.
The only things you can do, is write your own class, which delegates to a stream of your choosing, and does that.
That's of strictly limited utiliy though.
struct alwaysenter {
std::ostream& o;
template<class X> alwaysenter& operator<<(X&& x) {
o<<std::forward<X>(x);
return *this;
}
};
I'd like to input a stream parameter to a method which can be either a <stringstream> or <iostream> as in:
void method(? out); // or
void method(? in);
If ? is <istream> or <ostream> it's straightforward. What I don't know is what to do if the parameter is either <istream> or <stringstream> or is either <ostream> or <stringstream>.
Can this be done?
The streams implementing both std::istream and std::ostream, e.g., std::stringstream and std::fstream derive from std::iostream (since they are all class templates, you'd look for basic_... in the standard). That is, if you really need a stream which is used for both input and output, you'd pass an std::iostream&.
The class std::iostream derives from both std::istream and std::ostream. The appropriate types are straight forward:
for reading only use std::istream&
for writing only use std::ostream&
for reading and writing use std::iostream& (I don't think I ever used this in production code)
Note that you need to seek when switching between reading and writing in case the stream may be a file stream: switching between reading and writing without intervening seek, even if the seek is to the current position, results in undefined behavior.
Both the string stream classes and file stream classes publically-derive from the general stream bases classes std::basic_istream<...> and std::basic_ostream<...>. This means that you can pass (for instance) std::ostringstream objects to functions that take std::ostream:
void test(std::ostream& os);
std::ostringstream buf;
test(buf); // Good!
Unless of course, you need additional functionality specific to the derived stream classes. For example, string streams provide an str() method and file streams provide open()/close() member functions. In the case where you need these, you can have test() take string streams or file streams as parameters.
void test(std::istringstream& iss)
{
std::cout << iss.str(); // Works because the type is a stringstream
}
I have a function that takes in user input via std::cin:
std::getline(std::cin, in);
and creates a corresponding data structure by matching it with a regular expression. The function then returns this data structure.
I'm using boost.test and I want to create a unit test to check that the output data type is correct given some inputs. However I don't know how to go about it since the input isn't passed as an argument to the function.
EDIT: Is there a simple way to create a boost test case that feeds the function a string via standard input?
If you have access to the source code of the function that calls std::getline, then the easiest solution is to rewrite it as a wrapper of another function having the same signature and implementation, but taking an additional std::istream& parameter that is used in place of std::cin. For example, if you currently have:
my_struct my_func()
{
//...
std::getline(std::cin, in);
//...
}
Then rewrite like this:
my_struct my_func(std::istream& is);
inline my_struct my_func()
{
return my_func(std::cin);
}
my_struct my_func(std::istream& is)
{
//...
std::getline(is, in);
//...
}
This way, you will be able to test the core functionality of my_func on constructed input sequences by passing std::istringstream objects into my_func(std::istream&).
If you do not have access to the source code of the function that calls std::getline, then one trick that you can use is to replace the standard in descriptor. See this answer for code that replaces the standard out descriptor and modify accordingly.
I quite recently learned about the C++ classes friend keyword and the uses in serialization and now I need some help in getting it to work.
I have no problem serializing my class to a file, it's working great, however i'm having a hard time trying to read this file into a vector container. I'm sure I need a loop in my code that reads line by line, but since the class has different types I guess I can't use std::getline() and also maybe that approach wouldn't use the istream method i implemented?
A sample output file would be:
Person 1
2009
1
Person 2
2001
0
My code:
class SalesPeople {
friend ostream &operator<<(ostream &stream, SalesPeople salesppl);
friend istream &operator>>(istream &stream, SalesPeople &salesppl);
private:
string fullname;
int employeeID;
int startYear;
bool status;
};
ostream &operator<<(ostream &stream, SalesPeople salesppl)
{
stream << salesppl.fullname << endl;
stream << salesppl.startYear << endl;
stream << salesppl.status << endl;
stream << endl;
return stream;
}
istream &operator>>(istream &stream, SalesPeople &salesppl)
{
stream >> salesppl.fullname;
stream >> salesppl.startYear;
stream >> salesppl.status;
// not sure how to read that empty extra line here ?
return stream;
}
// need some help here trying to read the file into a vector<SalesPeople>
SalesPeople employee;
vector<SalesPeople> employees;
ifstream read("employees.dat", ios::in);
if (!read) {
cerr << "Unable to open input file.\n";
return 1;
}
// i am pretty sure i need a loop here and should go line by line
// to read all the records, however the class has different
// types and im not sure how to use the istream method here.
read >> employee;
employees.push_back(employee);
By the way, I know that the Boost library has a great serialization class, however I'm trying to learn how serialization would work using the STL library for now.
Thanks a lot in advance for any help that you can give me and for getting me in the right track!
It looks like you pretty much have all the code you need already! I copied your code and compiled it with some changes to read the SalesPeople in from a file in a loop. I will include the changes below, but since this is for your homework, you may just want to read and think about the following hints before looking at the code.
For reading the SalesPeople in a
loop, I would recommend that you take
a look at this FAQ. It has an
example of almost exactly what you
need. FAQ 15.4 will also help
you, I believe.
For your question on how to handle
the extra empty line when reading
from the file, check out this
link. You can very simply
extract whitespace this way.
As jfclavette suggested, I would
recommend looking into
std::getline for reading in the
SalesPerson's full name, since you
need everything on that line into one
string.
I have one question for you, though: what about the employeeID? I notice that it is being ignored in your sample code. Is that on purpose?
And now, if you still need help, you can check out the code I wrote to get this to work:
istream &operator>>(istream &stream, SalesPeople &salesppl)
{
//stream >> salesppl.fullname;
getline(stream, salesppl.fullname);
stream >> salesppl.startYear;
stream >> salesppl.status;
// not sure how to read that empty extra line here ?
stream >> ws;
return stream;
}
while(read >> employee)
{
// cout << employee; // to verify the input, uncomment this line
employees.push_back(employee);
}
Also, as jfclavette suggested, it may not be a bad idea to add some input validation (check the stream status after reading from it and verify that it is still good). Although I would recommend using the while() loop for the reasons stated in FAQ 15.5.
Not sure what your problem is. What exactly are you not understanding ? The fact that your names are composed of multiple tokens ? There's no magic way to do it, you might want to get the name trough getline(). Alternatively, you may want to specify the number of tokens when serializing and read the appropriate token count. ie, your file might look like.
2 Person 1
I assumed that Person was the first name and 1 the last name here. You might also enforce the notion that there's one first name, and one last name and just read each one separately.
You'll typically loop while (!ifstream.eof()) and read. Of course, you should always validate the inputs.
Also, why are you adding an extra endl between each record ? Serialized data need not be pretty. :)