Discussion:
fstreams
(too old to reply)
Stefan Ram
2024-03-27 15:11:31 UTC
Permalink
This (barring typos) is from a book by Bjarne:

int main()
{
string from, to; /* get source and target file names */
cin >> from >> to;

ifstream is( from.c_str() ); /* open input stream */
ofstream os( to.c_str() ); /* open output stream */

istream_iterator<string> ii(is); /* make input iterator for stream */
istream_iterator<string> eos; /* input sentinel */
ostream_iterator<string> oo(os,"\n"); /* make out it. for stream */

set<string> b(ii, eos); /* b is a set initialized from input */
copy(b.begin(), b.end(), oo); /* copy buffer to output */ }

So, is this good style not checking the stream states after opening?

IIRC, out of the box those streams will not throw an exception
when they cannot open.
Sam
2024-03-27 21:32:12 UTC
Permalink
Post by Stefan Ram
int main()
{
string from, to; /* get source and target file names */
cin >> from >> to;
ifstream is( from.c_str() ); /* open input stream */
ofstream os( to.c_str() ); /* open output stream */
istream_iterator<string> ii(is); /* make input iterator for stream */
istream_iterator<string> eos; /* input sentinel */
ostream_iterator<string> oo(os,"\n"); /* make out it. for stream */
set<string> b(ii, eos); /* b is a set initialized from input */
copy(b.begin(), b.end(), oo); /* copy buffer to output */ }
So, is this good style not checking the stream states after opening?
What's the purpose of the above example? Looks to me like it's demonstrating
some basic stream operation, so error checking is immaterial. I would think
that there'll be a separate discussion about error checking where this topic
would be discussed.

Common sense dictates that: if it's expected to clearly report any errors
then the appropriate logic would be added. But if a missing file or an empty
error should have functionally identical repercussions as an empty file then
there's little to be gained by an extra check.
Scott Lurndal
2024-03-27 22:35:22 UTC
Permalink
Post by Sam
Post by Stefan Ram
int main()
{
string from, to; /* get source and target file names */
cin >> from >> to;
ifstream is( from.c_str() ); /* open input stream */
ofstream os( to.c_str() ); /* open output stream */
istream_iterator<string> ii(is); /* make input iterator for stream */
istream_iterator<string> eos; /* input sentinel */
ostream_iterator<string> oo(os,"\n"); /* make out it. for stream */
set<string> b(ii, eos); /* b is a set initialized from input */
copy(b.begin(), b.end(), oo); /* copy buffer to output */ }
So, is this good style not checking the stream states after opening?
What's the purpose of the above example? Looks to me like it's demonstrating
some basic stream operation, so error checking is immaterial. I would think
that there'll be a separate discussion about error checking where this topic
would be discussed.
It's not clear from which book Stefan found this example. It
doesn't work correctly, however:

$ cat a.cpp
#include <string>
#include <fstream>
#include <iostream>
#include <iterator>
#include <set>

using namespace std;

int main()
{
string from, to; /* get source and target file names */
cin >> from >> to;

ifstream is( from.c_str() ); /* open input stream */
ofstream os( to.c_str() ); /* open output stream */

istream_iterator<string> ii(is); /* make input iterator for stream */
istream_iterator<string> eos; /* input sentinel */
ostream_iterator<string> oo(os,"\n"); /* make out it. for stream */

set<string> b(ii, eos); /* b is a set initialized from input */
copy(b.begin(), b.end(), oo); /* copy buffer to output */
}

$ ./a
i.png
j.png
$ ls -l ?.png
-rw-------. 1 scott scott 2413 Mar 27 13:58 i.png
-rw-rw-r--. 1 scott scott 2411 Mar 27 15:27 j.png

The output file is two bytes shorter than the input file.

The generated code, even with -O2 is horrible, terrible, awful.

0000000000400f90 <main>:
400f90: 41 55 push %r13
400f92: bf 00 31 60 00 mov $0x603100,%edi
400f97: 41 54 push %r12
400f99: 55 push %rbp
400f9a: 53 push %rbx
400f9b: 48 81 ec 38 05 00 00 sub $0x538,%rsp
400fa2: 48 8d 74 24 10 lea 0x10(%rsp),%rsi
400fa7: 48 c7 44 24 10 38 32 movq $0x603238,0x10(%rsp)
400fae: 60 00
400fb0: 48 c7 44 24 20 38 32 movq $0x603238,0x20(%rsp)
400fb7: 60 00
400fb9: e8 d2 fe ff ff callq 400e90 <std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char>, std::allocator<char> >(std::basic_istream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&)@plt>
400fbe: 48 8d 74 24 20 lea 0x20(%rsp),%rsi
400fc3: 48 89 c7 mov %rax,%rdi
400fc6: e8 c5 fe ff ff callq 400e90 <std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char>, std::allocator<char> >(std::basic_istream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&)@plt>
400fcb: 4c 8d a4 24 20 03 00 lea 0x320(%rsp),%r12
400fd2: 00
400fd3: 48 8b 74 24 10 mov 0x10(%rsp),%rsi
400fd8: ba 08 00 00 00 mov $0x8,%edx
400fdd: 4c 89 e7 mov %r12,%rdi
400fe0: e8 2b ff ff ff callq 400f10 <std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(char const*, std::_Ios_Openmode)@plt>
400fe5: 48 8b 74 24 20 mov 0x20(%rsp),%rsi
400fea: 48 8d bc 24 20 01 00 lea 0x120(%rsp),%rdi
400ff1: 00
400ff2: ba 30 00 00 00 mov $0x30,%edx
400ff7: e8 24 ff ff ff callq 400f20 <std::basic_ofstream<char, std::char_traits<char> >::basic_ofstream(char const*, std::_Ios_Openmode)@plt>
400ffc: 48 8b 84 24 20 03 00 mov 0x320(%rsp),%rax
401003: 00
401004: 4c 89 64 24 30 mov %r12,0x30(%rsp)
401009: 48 c7 44 24 38 38 32 movq $0x603238,0x38(%rsp)
401010: 60 00
401012: 48 8b 40 e8 mov -0x18(%rax),%rax
401016: f6 84 04 40 03 00 00 testb $0x5,0x340(%rsp,%rax,1)
40101d: 05
40101e: 0f 84 6a 02 00 00 je 40128e <main+0x2fe>
401024: c6 44 24 40 00 movb $0x0,0x40(%rsp)
401029: 48 8d bc 24 98 00 00 lea 0x98(%rsp),%rdi
401030: 00
401031: 48 8d 74 24 58 lea 0x58(%rsp),%rsi
401036: 48 c7 44 24 50 00 00 movq $0x0,0x50(%rsp)
40103d: 00 00
40103f: 48 c7 44 24 58 38 32 movq $0x603238,0x58(%rsp)
401046: 60 00
401048: c6 44 24 60 00 movb $0x0,0x60(%rsp)
40104d: 48 c7 84 24 90 00 00 movq $0x0,0x90(%rsp)
401054: 00 00 00 00 00
401059: e8 02 fe ff ff callq 400e60 <std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&)@plt>
40105e: 0f b6 44 24 60 movzbl 0x60(%rsp),%eax
401063: 48 8d 7c 24 78 lea 0x78(%rsp),%rdi
401068: 48 8d 74 24 38 lea 0x38(%rsp),%rsi
40106d: 88 84 24 a0 00 00 00 mov %al,0xa0(%rsp)
401074: 48 8b 44 24 30 mov 0x30(%rsp),%rax
401079: 48 89 44 24 70 mov %rax,0x70(%rsp)
40107e: e8 dd fd ff ff callq 400e60 <std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&)@plt>
401083: 0f b6 44 24 40 movzbl 0x40(%rsp),%eax
401088: 48 8d b4 24 98 00 00 lea 0x98(%rsp),%rsi
40108f: 00
40108f: 00
401090: 48 8d bc 24 d8 00 00 lea 0xd8(%rsp),%rdi
401097: 00
401098: c7 84 24 f8 00 00 00 movl $0x0,0xf8(%rsp)
40109f: 00 00 00 00
4010a3: 48 c7 84 24 00 01 00 movq $0x0,0x100(%rsp)
4010aa: 00 00 00 00 00
4010af: 48 c7 84 24 18 01 00 movq $0x0,0x118(%rsp)
4010b6: 00 00 00 00 00
4010bb: 88 84 24 80 00 00 00 mov %al,0x80(%rsp)
4010c2: 48 8d 84 24 f8 00 00 lea 0xf8(%rsp),%rax
4010c9: 00
4010ca: 48 89 84 24 08 01 00 mov %rax,0x108(%rsp)
4010d1: 00
4010d2: 48 89 84 24 10 01 00 mov %rax,0x110(%rsp)
4010d9: 00
4010da: 48 8b 84 24 90 00 00 mov 0x90(%rsp),%rax
4010e1: 00
4010e2: 48 89 84 24 d0 00 00 mov %rax,0xd0(%rsp)
4010e9: 00
4010ea: e8 71 fd ff ff callq 400e60 <std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&)@plt>
4010ef: 0f b6 84 24 a0 00 00 movzbl 0xa0(%rsp),%eax
4010f6: 00
4010f7: 48 8d 74 24 78 lea 0x78(%rsp),%rsi
4010fc: 48 8d bc 24 b8 00 00 lea 0xb8(%rsp),%rdi
401103: 00
401104: 88 84 24 e0 00 00 00 mov %al,0xe0(%rsp)
40110b: 48 8b 44 24 70 mov 0x70(%rsp),%rax
401110: 48 89 84 24 b0 00 00 mov %rax,0xb0(%rsp)
401117: 00
401118: e8 43 fd ff ff callq 400e60 <std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&)@plt>
40111d: 0f b6 84 24 80 00 00 movzbl 0x80(%rsp),%eax
401124: 00
401125: 48 8d 94 24 d0 00 00 lea 0xd0(%rsp),%rdx
40112c: 00
40112d: 48 8d b4 24 b0 00 00 lea 0xb0(%rsp),%rsi
401134: 00
401135: 48 8d bc 24 f0 00 00 lea 0xf0(%rsp),%rdi
40113c: 00
40113d: 88 84 24 c0 00 00 00 mov %al,0xc0(%rsp)
401144: e8 27 08 00 00 callq 401970 <void std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> >::_M_insert_unique<std::istream_iterator<std::string, char, std::char_traits<char>, long> >(std::istream_iterator<std::string, char, std::char_traits<char>, long>, std::istream_iterator<std::string, char, std::char_traits<char>, long>)>
401149: 48 8b 84 24 b8 00 00 mov 0xb8(%rsp),%rax
401150: 00
401151: 4c 8d 6c 24 0f lea 0xf(%rsp),%r13
401156: 48 8d ac 24 f8 00 00 lea 0xf8(%rsp),%rbp
40115d: 00
40115e: 4c 89 ee mov %r13,%rsi
401161: 48 8d 78 e8 lea -0x18(%rax),%rdi
401165: e8 a6 fc ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
40116a: 48 8b 84 24 d8 00 00 mov 0xd8(%rsp),%rax
401171: 00
401172: 48 8d b4 24 b0 00 00 lea 0xb0(%rsp),%rsi
401179: 00
40117a: 48 8d 78 e8 lea -0x18(%rax),%rdi
40117e: e8 8d fc ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
401183: 48 8b 44 24 78 mov 0x78(%rsp),%rax
401188: 48 8d b4 24 d0 00 00 lea 0xd0(%rsp),%rsi
40118f: 00
401190: 48 8d 78 e8 lea -0x18(%rax),%rdi
401194: e8 77 fc ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
401199: 48 8b 84 24 98 00 00 mov 0x98(%rsp),%rax
4011a0: 00
4011a1: 48 8d b4 24 d0 00 00 lea 0xd0(%rsp),%rsi
4011a8: 00
4011a9: 48 8d 78 e8 lea -0x18(%rax),%rdi
4011ad: e8 5e fc ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
4011b2: 48 8b 9c 24 08 01 00 mov 0x108(%rsp),%rbx
4011b9: 00
4011ba: 48 39 eb cmp %rbp,%rbx
4011bd: 74 3d je 4011fc <main+0x26c>
4011bf: 90 nop
4011c0: 48 8b 73 20 mov 0x20(%rbx),%rsi
4011c4: 48 8d bc 24 20 01 00 lea 0x120(%rsp),%rdi
4011cb: 00
4011cc: 48 8b 56 e8 mov -0x18(%rsi),%rdx
4011d0: e8 0b fd ff ff callq 400ee0 <std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)@plt>
4011d5: 48 8d bc 24 20 01 00 lea 0x120(%rsp),%rdi
4011dc: 00
4011dd: ba 01 00 00 00 mov $0x1,%edx
4011e2: be b0 1b 40 00 mov $0x401bb0,%esi
4011e7: e8 f4 fc ff ff callq 400ee0 <std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)@plt>
4011ec: 48 89 df mov %rbx,%rdi
4011ef: e8 bc fc ff ff callq 400eb0 <std::_Rb_tree_increment(std::_Rb_tree_node_base const*)@plt>
4011f4: 48 39 e8 cmp %rbp,%rax
4011f7: 48 89 c3 mov %rax,%rbx
4011fa: 75 c4 jne 4011c0 <main+0x230>
4011fc: 48 8b b4 24 00 01 00 mov 0x100(%rsp),%rsi
401203: 00
401204: 48 8d bc 24 f0 00 00 lea 0xf0(%rsp),%rdi
40120b: 00
40120c: e8 1f 03 00 00 callq 401530 <std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> >::_M_erase(std::_Rb_tree_node<std::string>*)>
401211: 48 8b 44 24 58 mov 0x58(%rsp),%rax
401216: 48 8d b4 24 d0 00 00 lea 0xd0(%rsp),%rsi
40121d: 00
40121e: 48 8d 78 e8 lea -0x18(%rax),%rdi
401222: e8 e9 fb ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
401227: 48 8b 44 24 38 mov 0x38(%rsp),%rax
40122c: 48 8d b4 24 d0 00 00 lea 0xd0(%rsp),%rsi
401233: 00
401234: 48 8d 78 e8 lea -0x18(%rax),%rdi
401238: e8 d3 fb ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
40123d: 48 8d bc 24 20 01 00 lea 0x120(%rsp),%rdi
401244: 00
401245: e8 86 fc ff ff callq 400ed0 <std::basic_ofstream<char, std::char_traits<char> >::~basic_ofstream()@plt>
40124a: 4c 89 e7 mov %r12,%rdi
40124d: e8 6e fc ff ff callq 400ec0 <std::basic_ifstream<char, std::char_traits<char> >::~basic_ifstream()@plt>
401252: 48 8b 44 24 20 mov 0x20(%rsp),%rax
401257: 48 8d b4 24 d0 00 00 lea 0xd0(%rsp),%rsi
40125e: 00
40125f: 48 8d 78 e8 lea -0x18(%rax),%rdi
401263: e8 a8 fb ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
401268: 48 8b 44 24 10 mov 0x10(%rsp),%rax
40126d: 48 8d b4 24 d0 00 00 lea 0xd0(%rsp),%rsi
401274: 00
401275: 48 8d 78 e8 lea -0x18(%rax),%rdi
401279: e8 92 fb ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
40127e: 48 81 c4 38 05 00 00 add $0x538,%rsp
401285: 31 c0 xor %eax,%eax
401287: 5b pop %rbx
401288: 5d pop %rbp
401289: 41 5c pop %r12
40128b: 41 5d pop %r13
40128d: c3 retq
40128e: 48 8d 74 24 38 lea 0x38(%rsp),%rsi
401293: 4c 89 e7 mov %r12,%rdi
401296: c6 44 24 40 01 movb $0x1,0x40(%rsp)
40129b: e8 f0 fb ff ff callq 400e90 <std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char>, std::allocator<char> >(std::basic_istream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&)@plt>
4012a0: 48 8b 54 24 30 mov 0x30(%rsp),%rdx
4012a5: 48 8b 02 mov (%rdx),%rax
4012a8: 48 03 50 e8 add -0x18(%rax),%rdx
4012ac: 31 c0 xor %eax,%eax
4012ae: f6 42 20 05 testb $0x5,0x20(%rdx)
4012b2: 48 0f 44 c2 cmove %rdx,%rax
4012b6: 48 85 c0 test %rax,%rax
4012b9: 0f 95 44 24 40 setne 0x40(%rsp)
4012be: e9 66 fd ff ff jmpq 401029 <main+0x99>
4012c3: 48 89 c3 mov %rax,%rbx
4012c6: 48 8b 44 24 38 mov 0x38(%rsp),%rax
4012cb: 48 8d 74 24 0f lea 0xf(%rsp),%rsi
4012d0: 48 8d 78 e8 lea -0x18(%rax),%rdi
4012d4: e8 37 fb ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
4012d9: 48 8d bc 24 20 01 00 lea 0x120(%rsp),%rdi
4012e0: 00
4012e1: e8 ea fb ff ff callq 400ed0 <std::basic_ofstream<char, std::char_traits<char> >::~basic_ofstream()@plt>
4012e6: 4c 89 e7 mov %r12,%rdi
4012e9: e8 d2 fb ff ff callq 400ec0 <std::basic_ifstream<char, std::char_traits<char> >::~basic_ifstream()@plt>
4012ee: 48 8b 44 24 20 mov 0x20(%rsp),%rax
4012f3: 48 8d 74 24 0e lea 0xe(%rsp),%rsi
4012f8: 48 8d 78 e8 lea -0x18(%rax),%rdi
4012fc: e8 0f fb ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
401301: 48 8b 44 24 10 mov 0x10(%rsp),%rax
401306: 48 8d 74 24 0e lea 0xe(%rsp),%rsi
40130b: 48 8d 78 e8 lea -0x18(%rax),%rdi
40130f: e8 fc fa ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
401314: 48 89 df mov %rbx,%rdi
401317: e8 64 fc ff ff callq 400f80 <***@plt>
40131c: 48 8b b4 24 00 01 00 mov 0x100(%rsp),%rsi
401323: 00
401324: 48 8d bc 24 f0 00 00 lea 0xf0(%rsp),%rdi
40132b: 00
40132c: 48 89 c3 mov %rax,%rbx
40132f: e8 fc 01 00 00 callq 401530 <std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> >::_M_erase(std::_Rb_tree_node<std::string>*)>
401334: 48 8b 44 24 58 mov 0x58(%rsp),%rax
401339: 4c 89 ee mov %r13,%rsi
40133c: 48 8d 78 e8 lea -0x18(%rax),%rdi
401340: e8 cb fa ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
401345: 48 8b 44 24 38 mov 0x38(%rsp),%rax
40134a: 4c 89 ee mov %r13,%rsi
40134d: 48 8d 78 e8 lea -0x18(%rax),%rdi
401351: e8 ba fa ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
401356: eb 81 jmp 4012d9 <main+0x349>
401358: 48 89 c3 mov %rax,%rbx
40135b: 48 8b 84 24 b8 00 00 mov 0xb8(%rsp),%rax
401362: 00
401363: 4c 8d 6c 24 0f lea 0xf(%rsp),%r13
401368: 4c 89 ee mov %r13,%rsi
40136b: 48 8d 78 e8 lea -0x18(%rax),%rdi
40136f: e8 9c fa ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
401374: 48 8b 84 24 d8 00 00 mov 0xd8(%rsp),%rax
40137b: 00
40137c: 4c 89 ee mov %r13,%rsi
40137f: 48 8d 78 e8 lea -0x18(%rax),%rdi
401383: e8 88 fa ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
401388: 48 8b b4 24 00 01 00 mov 0x100(%rsp),%rsi
40138f: 00
401390: 48 8d bc 24 f0 00 00 lea 0xf0(%rsp),%rdi
401397: 00
401398: e8 93 01 00 00 callq 401530 <std::_Rb_tree<std::string, std::string, std::_Identity<std::string>, std::less<std::string>, std::allocator<std::string> >::_M_erase(std::_Rb_tree_node<std::string>*)>
40139d: 48 8b 44 24 78 mov 0x78(%rsp),%rax
4013a2: 4c 89 ee mov %r13,%rsi
4013a5: 48 8d 78 e8 lea -0x18(%rax),%rdi
4013a9: e8 62 fa ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
4013ae: 48 8b 84 24 98 00 00 mov 0x98(%rsp),%rax
4013b5: 00
4013b6: 4c 89 ee mov %r13,%rsi
4013b9: 48 8d 78 e8 lea -0x18(%rax),%rdi
4013bd: e8 4e fa ff ff callq 400e10 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
4013c2: e9 6d ff ff ff jmpq 401334 <main+0x3a4>
4013c7: 48 89 c3 mov %rax,%rbx
4013ca: 4c 8d 6c 24 0f lea 0xf(%rsp),%r13
4013cf: eb a3 jmp 401374 <main+0x3e4>
4013d1: 48 89 c3 mov %rax,%rbx
4013d4: 4c 8d 6c 24 0f lea 0xf(%rsp),%r13
4013d9: eb ad jmp 401388 <main+0x3f8>
4013db: 48 89 c3 mov %rax,%rbx
4013de: 4c 8d 6c 24 0f lea 0xf(%rsp),%r13
4013e3: eb c9 jmp 4013ae <main+0x41e>
4013e5: 48 89 c3 mov %rax,%rbx
4013e8: 4c 8d 6c 24 0f lea 0xf(%rsp),%r13
4013ed: e9 42 ff ff ff jmpq 401334 <main+0x3a4>
4013f2: 48 89 c3 mov %rax,%rbx
4013f5: e9 ec fe ff ff jmpq 4012e6 <main+0x356>
4013fa: 48 89 c3 mov %rax,%rbx
4013fd: e9 ec fe ff ff jmpq 4012ee <main+0x35e>
401402: 66 66 66 66 66 2e 0f data32 data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)
401409: 1f 84 00 00 00 00 00
Post by Sam
Common sense dictates that: if it's expected to clearly report any errors
then the appropriate logic would be added. But if a missing file or an empty
error should have functionally identical repercussions as an empty file then
there's little to be gained by an extra check.
Yes, if you run it and the first file doesn't exist, it just creates
an empty output file with no error message.
Michael S
2024-03-27 23:23:31 UTC
Permalink
On Wed, 27 Mar 2024 22:35:22 GMT
Post by Scott Lurndal
The generated code, even with -O2 is horrible, terrible, awful.
Why would you expect that such awfully designed library as C++
iostream would generate anything but an awfull code?
Chris M. Thomasson
2024-03-28 01:41:43 UTC
Permalink
Post by Michael S
On Wed, 27 Mar 2024 22:35:22 GMT
Post by Scott Lurndal
The generated code, even with -O2 is horrible, terrible, awful.
Why would you expect that such awfully designed library as C++
iostream would generate anything but an awfull code?
;^o
Ben Bacarisse
2024-03-28 09:27:30 UTC
Permalink
Post by Scott Lurndal
Post by Sam
Post by Stefan Ram
int main()
{
string from, to; /* get source and target file names */
cin >> from >> to;
ifstream is( from.c_str() ); /* open input stream */
ofstream os( to.c_str() ); /* open output stream */
istream_iterator<string> ii(is); /* make input iterator for stream */
istream_iterator<string> eos; /* input sentinel */
ostream_iterator<string> oo(os,"\n"); /* make out it. for stream */
set<string> b(ii, eos); /* b is a set initialized from input */
copy(b.begin(), b.end(), oo); /* copy buffer to output */ }
So, is this good style not checking the stream states after opening?
What's the purpose of the above example? Looks to me like it's demonstrating
some basic stream operation, so error checking is immaterial. I would think
that there'll be a separate discussion about error checking where this topic
would be discussed.
It's not clear from which book Stefan found this example. It
$ cat a.cpp
#include <string>
#include <fstream>
#include <iostream>
#include <iterator>
#include <set>
using namespace std;
int main()
{
string from, to; /* get source and target file names */
cin >> from >> to;
ifstream is( from.c_str() ); /* open input stream */
ofstream os( to.c_str() ); /* open output stream */
istream_iterator<string> ii(is); /* make input iterator for stream */
istream_iterator<string> eos; /* input sentinel */
ostream_iterator<string> oo(os,"\n"); /* make out it. for stream */
set<string> b(ii, eos); /* b is a set initialized from input */
copy(b.begin(), b.end(), oo); /* copy buffer to output */
}
$ ./a
i.png
j.png
$ ls -l ?.png
-rw-------. 1 scott scott 2413 Mar 27 13:58 i.png
-rw-rw-r--. 1 scott scott 2411 Mar 27 15:27 j.png
The output file is two bytes shorter than the input file.
How do you know it is not working correctly?.

The program is not file copy program. SR's first language is not
English so "copy buffer to output" is understandably not the clearest
description. The "copy" is done "through" a set to list the unique
strings in the input.
--
Ben.
Stefan Ram
2024-03-28 08:13:00 UTC
Permalink
Post by Sam
What's the purpose of the above example? Looks to me like it's demonstrating
some basic stream operation, so error checking is immaterial. I would think
that there'll be a separate discussion about error checking where this topic
would be discussed.
It might have been to showcase sets.

For my C++ tutorial, I want to have "canonical" examples of
how to read and write files in C++ that can serve as "role
models" (templates in the general sense) for code.

I thought I could get the best example code from books by Bjarne.

Now, I have asked a chatbot. Me,

|Please show me code in C++ that opens a file and writes
|"Hello" to it. The C++ version should be as modern as
|possible and the program should have perfect error checking.

; the chatbot,

|C++ Code to Write "Hello" to a File
|
|Here's a modern C++ code snippet that opens a file and writes
|"Hello" to it, with robust error checking:
|
|cpp
|
|#include <iostream>
|#include <fstream>
|#include <filesystem>
|
|int main() {
| try {
| // Create the file path
| std::filesystem::path filePath = "output.txt";
|
| // Open the file for writing
| std::ofstream outputFile(filePath, std::ios::out);
|
| // Check if the file was opened successfully
| if (!outputFile.is_open()) {
| throw std::runtime_error("Failed to open the file: " + filePath.string());
| }
|
| // Write "Hello" to the file
| outputFile << "Hello";
|
| // Close the file
| outputFile.close();
|
| std::cout << "File written successfully: " << filePath.string() << std::endl;
| } catch (const std::exception& e) {
| std::cerr << "Error: " << e.what() << std::endl;
| return 1;
| }
|
| return 0;
|}
|
|Here's a breakdown of the code:
|
|The program includes the necessary header files: <iostream>
|for standard input/output, <fstream> for file operations, and
|<filesystem> for file path management.
|
|The main() function is the entry point of the program.
|
|The code is wrapped in a try-catch block to handle any
|exceptions that may occur during the file operations.
|
|Inside the try block, the program creates the file path using
|std::filesystem::path.
|
|An std::ofstream object is created to open the file for
|writing. The std::ios::out flag is used to indicate that the
|file should be opened for output.
|
|The code checks if the file was opened successfully by
|checking the is_open() method of the outputFile object. If the
|file couldn't be opened, an exception is thrown with an error
|message.
|
|The program writes the string "Hello" to the file using the <<
|operator.
|
|The file is then closed using the close() method.
|
|If the file was written successfully, a success message is
|printed to the console.
|
|If any exceptions are caught, the error message is printed to
|the standard error stream (std::cerr), and the program returns
|a non-zero exit code to indicate an error.
|
|This code uses modern C++ features, such as the
|std::filesystem library for file path management, and robust
|error handling with exceptions. The program will create the
|output file in the current working directory, and the file
|path can be modified as needed.

. This was what the chatbot answered.
Ralf Fassel
2024-03-28 09:37:20 UTC
Permalink
* ***@zedat.fu-berlin.de (Stefan Ram)
| Now, I have asked a chatbot. Me,
| |Please show me code in C++ that opens a file and writes
| |"Hello" to it. The C++ version should be as modern as
| |possible and the program should have perfect error checking.
| ; the chatbot,
| |C++ Code to Write "Hello" to a File
--<snip-snip>--
| | // Write "Hello" to the file
| | outputFile << "Hello";
| |
| | // Close the file
| | outputFile.close();
| |
| | std::cout << "File written successfully: " << filePath.string() << std::endl;
--<snip-snip>--
| . This was what the chatbot answered.

As I understand, chatbots 'learn' from examples published on the net.
Seems like not many examples handle the case that even after successful
opening of a file the writing to it may still fail.
Somewhat similar to

if (exists(file)) {
assume_open_will_succeed(file);
...
}

R'
Sam
2024-03-28 11:24:13 UTC
Permalink
Post by Stefan Ram
Post by Sam
What's the purpose of the above example? Looks to me like it's demonstrating
some basic stream operation, so error checking is immaterial. I would think
that there'll be a separate discussion about error checking where this topic
would be discussed.
It might have been to showcase sets.
For my C++ tutorial, I want to have "canonical" examples of
how to read and write files in C++ that can serve as "role
models" (templates in the general sense) for code.
I thought I could get the best example code from books by Bjarne.
Well, without being directly familiar with the book in question: these
introductory books tend to focus on individual topics. Such as formatting
output and reading input. The details of opening files would be an ancillary
subject, at that time.

But I would expect that prior to that there would be a discussion focused on
opening files. An appropriate example, with error checking, will be
presented and explained at that time.

But you won't find a "canonical example" of everything, from start to
finish, in there.
Post by Stefan Ram
Now, I have asked a chatbot. Me,
[ … ]
. This was what the chatbot answered.
Bleh.
Stefan Ram
2024-03-28 12:03:22 UTC
Permalink
Post by Sam
Well, without being directly familiar with the book in question: these
introductory books tend to focus on individual topics. Such as formatting
output and reading input. The details of opening files would be an ancillary
subject, at that time.
|I have never, ever, ever seen a great software developer who
|does not have amazing attention to detail.
Rob Walling

The book was written by Bjarne to be used for teaching programming
to undergraduates. He complains bitterly that his book is being
ignored, and instead people are still taught C++ beginning with
the C parts of the language.
Stefan Ram
2024-03-28 12:46:27 UTC
Permalink
Post by Stefan Ram
The book was written by Bjarne to be used for teaching programming
to undergraduates.
I used this book by searching for an /arbitrary/ use of fstreams.

Now, I have looked again and found:

There is a separate chapter dedicated to "Input and Output Streams"
and a section "I/O error handling". Now, I will look at that chapter
and section.

Still, I was/am thinking: If there is a proper way to use I/O streams,
then it should be visible in /every/ example program.

This makes me think of an utterance by Matt Bonder:

|No matter what you want to say, there is one right way to say it,
|and every other way is wrong. And if you don't already know that
|one right way, then you're probably going to be wrong.
Matt Bonder

. Matt was commenting about his experience learning Japanese.
Scott Lurndal
2024-03-28 14:16:15 UTC
Permalink
Post by Stefan Ram
Post by Stefan Ram
The book was written by Bjarne to be used for teaching programming
to undergraduates.
I used this book by searching for an /arbitrary/ use of fstreams.
There is a separate chapter dedicated to "Input and Output Streams"
and a section "I/O error handling". Now, I will look at that chapter
and section.
Still, I was/am thinking: If there is a proper way to use I/O streams,
then it should be visible in /every/ example program.
The only proper way to use C++ I/O streams is to _not use them_.
wij
2024-03-28 15:02:58 UTC
Permalink
Post by Scott Lurndal
Post by Stefan Ram
The book was written by Bjarne to be used for teaching programming
to undergraduates.
 I used this book by searching for an /arbitrary/ use of fstreams.
 There is a separate chapter dedicated to "Input and Output Streams"
 and a section "I/O error handling". Now, I will look at that chapter
 and section.
 Still, I was/am thinking: If there is a proper way to use I/O streams,
 then it should be visible in /every/ example program.
The only proper way to use C++ I/O streams is to _not use them_.
Agree. Because normal users of C++ I/O streams don't understand how complexity
is involved. IOW, it only useful because it is compatible with C I/O stream.
Or useful for communication in simple case.
Otherwise, write your own, like mine:
https://sourceforge.net/projects/cscall/files/latest/download

If you are new C++ programer, learn C first.
Chris M. Thomasson
2024-03-28 19:44:29 UTC
Permalink
Post by Scott Lurndal
Post by Stefan Ram
Post by Stefan Ram
The book was written by Bjarne to be used for teaching programming
to undergraduates.
I used this book by searching for an /arbitrary/ use of fstreams.
There is a separate chapter dedicated to "Input and Output Streams"
and a section "I/O error handling". Now, I will look at that chapter
and section.
Still, I was/am thinking: If there is a proper way to use I/O streams,
then it should be visible in /every/ example program.
The only proper way to use C++ I/O streams is to _not use them_.
Damn right! :^D

Well, shit happens.
Stefan Ram
2024-03-28 13:49:49 UTC
Permalink
Post by Stefan Ram
ifstream is( from.c_str() ); /* open input stream */
The above from an example program in Bjarnes book.
In the same book he writes:

|Don't forget to test a stream after opening it.

. Yet he did not test "is" after opening it above
(unless I have missed it).

Is this a case of "Don't do as I do, but as I say."?

It could be problematic if prescribed guidelines are
observed only when the examples appear in the section
on those guidelines, but not observed in other sections.

This would be similar to washing your hands while you are in
a course of the Centers for Disease Control and Prevention,
but not outside of it.
Bonita Montero
2024-04-12 13:11:15 UTC
Permalink
Post by Stefan Ram
int main()
{
string from, to; /* get source and target file names */
cin >> from >> to;
ifstream is( from.c_str() ); /* open input stream */
ofstream os( to.c_str() ); /* open output stream */
istream_iterator<string> ii(is); /* make input iterator for stream */
istream_iterator<string> eos; /* input sentinel */
ostream_iterator<string> oo(os,"\n"); /* make out it. for stream */
set<string> b(ii, eos); /* b is a set initialized from input */
copy(b.begin(), b.end(), oo); /* copy buffer to output */ }
So, is this good style not checking the stream states after opening?
Enable exceptions for failbit and badbit.
Post by Stefan Ram
IIRC, out of the box those streams will not throw an exception
when they cannot open.
Loading...