Construct C++ Objects From Stdin
C++
Objects From Stdin: C++
This project is an example of how stdin data can be used to construct objects in C++ under Linux.
Data is input via stdin either through the terminal or by means of file redirection.
You could use this as boilerplate for importing data stored in a file into C++ data structures. In this case, data is imported on a line-by-line basis, with each line providing data for a separate object.
A vector of unique_ptr to objects is used to store objects.
Use Case
I put this project together to better understand input streams in C++. The aim was to accept input by means of file redirection or terminal input. I also wanted to switch to terminal input for additional interaction in the event of file redirection input of the main body of data.
One interesting result of this approach is that you can input data either by means of file redirection or by means of terminal input in the default stdin way.
File Redirection
Redirection in Linux involves changing standard input/output devices.
Usually, the standard input device stdin is the keyboard and the standard output device stdout is the screen.
This means that in C++ std::cin >> input; accepts input from the keyboard, and std::cout << output outputs data to the screen.
To input data from a file, redirection can be used:
./myProg < input.txt
In this case, the shell streams in the contents of input.txt to stdin instead of accepting keyboard input from the terminal.
Terminal Input
If you run the binary file without supplying data by means of file redirection, the user is required to supply data at the terminal. Data is entered:
- Line-by-line, with each line representing a separate object (
\ndelineates objects). - Individual scores are separated by spaces.
- Input is ended by entering Ctrl-D which is the Linux end-of-file signal.
Switching from File Redirection to Terminal
When input is redirected from a file, std::cin refers to the redirected file. If you need to get input from the terminal at a later stage in the programme, you can assign another input buffer using std::cin.rdbuf().
// Define an input stream as the terminal for the current process (/dev/tty)
std::fstream in("/dev/tty");
// Set the streambuf of std::cin as the streambuf of /dev/tty.
std::cin.rdbuf(in.rdbuf());
// stdin is now /dev/tty - keyboard input.
For a working example, see below:
#include <ostream>
#include "record.h" // Defines Record, a custom data type (class)
int main()
{
// Create a vector of *Record unique pointers
std::vector<std::unique_ptr<Record>> records;
while (1) {
// Make a unique pointer to a Record
auto s = std::make_unique<Record>();
// Input data to this object - reset object if EOF is indicated
if (s->inputFromStdin() == -1) {
s.reset();
break;
}
// Move the object to the array
records.push_back(std::move(s));
}
// Reset stdin to terminal
std::fstream in("/dev/tty");
std::cin.rdbuf(in.rdbuf());
for (auto& record : records) {
std::cout << *record;
}
try {
int index;
// Output to tty
std::streambuf *backup = std::cout.rdbuf();
std::ofstream out("/dev/tty");
std::cout.rdbuf(out.rdbuf());
// This should print to screen even if output is redirected to file.
std::cout << "Enter index: ";
std::cin >> index;
std::cout.rdbuf(backup); // Reset the original streambuf to std::cout
Record s = *(records[0]);
std::cout << "value: " << s[index] << '\n';
} catch (const IndexOutOfBoundsException& e) {
std::cout << "ERROR: index out of bounds." << '\n';
}
}
std::fstream is the input/output file stream class. The code above associates the /dev/tty (terminal device) stream with the variable in.
The next line sets the std::streambuf for std::cin to the std::streambuf of in which has been set to dev/tty.
The function std::ios::rdbuf() is used to get and set a stream buffer:
streambuf* rdbuf() const; // get - returns a pointer to the stream buffer of the object associated with stream
streambuf* rdbuf (streambuf* sb); // sets the object pointed at by sb, clears error flags.
Headers: <ios> <iostream>.
Streams & Stream Buffers
A stream is a communication channel between a programme and it’s environment.
Streams involve data flowing from a source to a destination - for example, data may flow from the programme to the screen, or may be read from the user’s keyboard into the programme.
A stream buffer is a memory buffer for the stream - the streambuf is an interface that defines a mapping from a stream to a device, file or memory.
- The stream is responsible for converting data into an appropriate format.
- The streambuf actually communicates the data.
References
comments powered by Disqus