4

I have the following code:

#include <iostream>
#include <fstream>

int main(int argc, char *argv[]) {
    if (argc != 2) {
        std::cout << "Usage: basics <file>" << std::endl;
        return 0;
    }

    std::basic_fstream<unsigned char> stream;

    stream.open(argv[1], std::fstream::out);
    stream.put('T');
    stream.put('E');
    stream.put('S');
    stream.put('T');
    stream.flush();
    stream.close();

    return 0;
}

The file is being created but there is nothing inside it when I open it in an editor. My compiler is gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1).
What's wrong with it?

edmz
  • 8,220
  • 2
  • 26
  • 45
Lukas
  • 101
  • 1
  • 3
  • 1
    You should probably check that the writing actually succeeds. – chris Feb 04 '15 at 15:44
  • Interestingly, changing from `unsigned char` template parameter to just `char` (as `std::fstream` is typedefed) solves the issue. I'm not 100% sure why though; I'd have to delve into the STL bowels to figure that one out. – inetknght Feb 04 '15 at 15:47
  • 1
    Standard streams are only guaranteed to have specializations with `char` and `wchar_t`. – David G Feb 04 '15 at 15:49
  • You don't need to flush and close, the stream does that when it dies. – molbdnilo Feb 04 '15 at 15:52
  • The template argument should be `char` not `unsigned char` if your printing characters, see [what is an unsigned char](http://stackoverflow.com/questions/75191/what-is-an-unsigned-char) – James Moore Feb 04 '15 at 15:58
  • works with Visual Studio 2013 – Matt Feb 04 '15 at 16:02
  • @JamesMoore it is unsigned char in order to be able to use binary data. I cut out all that stuff to have a lightweight example. The real code works with binary data and I don't only store characters, I store literally everything since it is intended to become a database when it's finished. I would use char if I knew a cast from unsigned to signed that works lossless in terms of a 1:1 binary layout of the data. – Lukas Feb 04 '15 at 16:17
  • 1
    @molbdnilo I added the flush because I thought it was worth trying. So in order to prevent comments like 'try flushing' I left it in here :P – Lukas Feb 04 '15 at 16:19
  • @JamesMoore I just read [this](http://stackoverflow.com/questions/5040920/converting-from-signed-char-to-unsigned-char-and-back-again). It describes how to cast `char` to `unsigned char` and vice versa. – Lukas Feb 04 '15 at 16:22
  • Never the less I think not being able to use anything except `char` and `wchar_t` somewhat violates the paradigm of streams. – Lukas Feb 04 '15 at 16:25

1 Answers1

0

Quoting std::basic_ostream::put

Unlike formatted output functions, this function does not set the failbit if the output fails.

Even if you check the stream state there won't be any error shown. However, if you try to write something with a formatted output operation, a std::bad_cast exception will be thrown because, among the possible reasons, there is no facet corresponding for that locale, which is checked when that operation is done.

Raw operations do not care about localization therefore it can't fail for that reason, while it would if e.g the raw write failed.

std::has_facet

Exceptions
std::bad_cast if std::has_facet(loc) == false.

Indeed, std::ctype is specialized for char and wchar_t not unsigned char which you will have to manually do. For en_GB.utf8, std::has_facet<std::ctype<unsigned char>>( std::locale("en_GB.utf8") ) == false

edmz
  • 8,220
  • 2
  • 26
  • 45