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 (
\n
delineates 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