License : Creative Commons Attribution 4.0 International (CC BY-NC-SA 4.0)
Copyright :
Hervé Frezza-Buet,
CentraleSupelec
Last modified : April 19, 2024 10:22
Link to the source : streams.md
struct X {...};
X operator+(const X& arg1, const X& arg2) {...; return res;}
X a,b,c;
c = a + b; // c = operator+(a, b);
struct X {
X operator+(const X& arg2) const {const X& arg1 = *this; ...; return res;}
}
X a,b,c;
c = a + b; // c = a.operator+(b);
Almost all which already exist in c++ (see here).
Nota, since C++20 :
// From en.cppreference.com/w/cpp
struct Record
{
std::string name;
unsigned int floor;
double weight;
auto operator<=>(const Record&) const = default;
};
// records can now be compared with ==, !=, <, <=, >, and >=
Operators new
and delete
can be overloaded.
Use them when the clarify the syntax. For example, in the Eigen library:
Matrix3f m;
m << 1, 2, 3,
4, 5, 6,
7, 8, 9;
Let us provide a library for defining successive postures of a robotic arm. We would like to have this code:
#include "arm.hpp"
#include <iostream>
#include <iomanip>
int main(int argc, char* argv[]) {
arm::Device manipulator;
int s1,e1,w1;
int s2,e2,w2;
manipulator <= arm::go,
arm::wrist(10), arm::go,
arm::shoulder(45), arm::elbow(120), arm::go,
arm::read(s1,e1,w1),
arm::wrist(10), arm::elbow(90), arm::go,
arm::elbow(0), arm::go,
arm::read(s2,e2,w2);
std::cout << std::endl
<< "Readings : " << std::endl
<< " " << std::setw(3) << s1 << ", "
<< " " << std::setw(3) << e1 << ", "
<< " " << std::setw(3) << w1 << std::endl
<< " " << std::setw(3) << s2 << ", "
<< " " << std::setw(3) << e2 << ", "
<< " " << std::setw(3) << w2 << std::endl;
return 0;
}
See the file arm.hpp for the implementation.
#include <iostream>
class X;
std::ostream& operator<<(std::ostream&, const X&);
std::istream& operator>>(std::istream&, X&);
class X {
private:
friend std::ostream& operator<<(std::ostream&, const X&);
friend std::istream& operator>>(std::istream&, X&);
};
std::ostream& operator<<(std::ostream& os, const X& x) {os << ... ; return os;}
std::istream& operator>>(std::istream& is, X& x) {is >> ... ; return is;}
...
X a, b, c;
std::cin >> a >> b >> c;
std::cout << a << ' ' << b << ", " << c << std::endl;
#include <iostream>
#include <iomanip>
#include <sstream>
#include <fstream>
unsigned int choice;
std::cout << "Enter a number: " << std::flush; // Flush forces the display without a new line.
std::cin >> choice;
std::ostringstream file_name;
file_name << "Toto-"
<< std::setw(6) << std::setfill('0') << choice // writes 000003 for choice == 3.
<< ".data";
{
std::ofstream file(file_name.str()); // Opens "Toto-000003.data" for choice == 3.
if(file) file << ....
} // File is closed when it is destructed.
#include <iostream>
#include <sstream>
#include <string>
#include <stdexcept>
#include <num.hpp>
/* The **predictive** grammar is for nums is :
NUM := <int> | STRING_NUM
STRING_NUM := STRING STRING_END
STRING := ' <charlist> '
STRING_END := <empty> | = <int> */
// Parsing functions.
num NUM (std::istream&);
num STRING_NUM(std::istream&);
std::string STRING (std::istream&);
int main(int argc, char* argv[]) {
std::string nums = "3 'a'=4 'b is my name' = -832 'no value' 578 'c'= 4 314 ";
std::istringstream ifs(nums);
ifs.exceptions(std::ios::failbit | std::ios::badbit | std::ios::eofbit); // We set exception handling.
num x {"x", 0};
___;
try{
while(true) {
x = NUM(ifs);
___;
}
}
catch(const std::exception& e) {/* Nothing to be done here */}
return 0;
}
num NUM(std::istream& is) {
char c;
is >> std::ws // Eats white spaces (tab, space, return...). Not mandatory here.
>> c;
// We have peeked the next non white char, we put it back in the stream.
// We can choose what to do next according to c... the grammar is predictive.
is.putback(c);
switch(c) {
case '\'' : return STRING_NUM(is); break;
default : int val; is >> val; return num(val); break;
}
}
num STRING_NUM(std::istream& is) {
char c;
std::string name = STRING(is);
is >> c; // We check is = is there.
if(c == '=') {
int val; is >> val;
return num(name, val);
}
is.putback(c);
return num(name);
}
std::string STRING(std::istream& is) {
char c;
std::string result;
is >> c; // We eat first '
std::getline(is, result, '\'');
return result;
}
#include <iostream>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <vector>
#include <iterator>
struct point {double x, y;};
std::ostream& operator<<(std::ostream& os, const point& p) { os << '(' << p.x << ", " << p.y << ')'; return os;}
std::istream& operator>>(std::istream& is, point& p) {char sep; is >> sep >> p.x >> sep >> p.y >> sep; return is;}
int main(int argc, char* argv[]) {
std::istringstream is("(1.2, 3.4)(5,6.78) (9.012, 3) (4,5.67)");
std::vector<point> points;
// We read the file into a vector.
std::copy(std::istream_iterator<point>(is), std::istream_iterator<point>(), std::back_inserter(points));
// We dump the vector to a file.
std::ofstream os("points.txt");
if(os) std::copy(points.begin(), points.end(), std::ostream_iterator<point>(os));
return 0;
}
The number 123.4567890
is written with 11 characters, i.e. 11 ASCII-encoded bytes, while the double
representation in memory is sizeof(double) = 8
. So passing ASCII representations in streams makes the file human readable (as in .csv
files), but is is not efficient, in terms of file-size as wall as in terms of computing the binary representation from the decimal ASCII syntax.
This is why, sometimes, binary representations are more efficient.
With binary files, you can handle a reading and a writing head, and call read
or write
to get or put bytes where the heads are pointing. See methods like tellp
, tellg
, seekp
, seekg
.
Here is an example of an emitter written in python that sends a byte stream to a receiver written in C++.
# file emitter.py
import numpy as np
import sys
bunch = np.arange(30).reshape(10,3).astype(np.float) # [[0, 1, 2], [3, 4, 5], ..., [27, 28, 29]]
for i in range(200):
sys.stdout.buffer.write(b'\x01') # means "Data is comming next".
sys.stdout.buffer.write(bunch.tobytes())
bunch += 1000 # fake change of the data points
sys.stdout.buffer.write(b'\x00') # means "I am done".
sys.stdout.flush()
// file receiver.cpp
#include <iostream>
#include <array>
struct datum {double x,y,z;};
using buffer = std::array<datum, 10>;
std::ostream& operator<<(std::ostream& os, const datum& d) {os << '(' << d.x << ", " << d.y << ", " << d.z << ')'; return os;}
std::ostream& operator<<(std::ostream& os, const buffer& d) {
auto it = d.begin();
os << '[' << *(it++);
while(it != d.end()) os << ", " << *(it++);
os << ']';
return os;
}
int main(int argc, char* argv[]) {
buffer buf; // sizeof(buffer) bytes = 10*sizeof(datum) bytes = 10*3*sizeof(double) bytes = 10*3*8 bytes
for(char has_next_tag = std::cin.get(); has_next_tag; has_next_tag = std::cin.get()) {
std::cin.read(reinterpret_cast<char*>(std::data(buf)), sizeof(buffer));
std::cout << buf << std::endl;
}
return 0;
}
mylogin@mymachine:~$ g++ -o reveiver -std=c++17 receiver.cpp
mylogin@mymachine:~$ python3 emitter.py | ./reveiver
[(0, 1, 2), (3, 4, 5), ..., (27, 28, 29)]
[(1000, 1001, 1002), (1003, 1004, 1005), ..., (1027, 1028, 1029)]
[(2000, 2001, 2002), (2003, 2004, 2005), ..., (2027, 2028, 2029)]
...