7

I think I'll get right into it and start with the code:

#include <iostream>
#include <fstream>
#include <string>

class test : public std::ofstream
{
    public:
        test(const std::string& filename) { this->open(gen_filename(filename)); };
        test(const test&) = delete;
        //test(test&& old) = default; // Didn't compile
        test(test&& old) {};
    private:
        std::string gen_filename(const std::string& filename) 
        { return filename + ".tmp"; }
};

int main()
{
    auto os = test("testfile");
    os << "Test1\n";
    os << "Test2\n";
}

Basically, I need to return an ofstream. Of course you can't copy an ofstream, so I fiddled around with the code in the class test, and I got the above to compile and work as you would expect (on gcc 4.5).

But I have a bad feeling this is just due to my compiler doing "Return Value Optimization" (RTO) on "auto os = test()". Indeed, if modify to the following:

int main()
{
    auto os = test("testfile");
    os << "Test1\n";
    auto os2 = std::move(os);
    os2 << "Test2\n";
}

I no longer get both Test1 and Test2 in the output.

The thing is, the class "test" isn't copyable, so there's no chance of the ofstream being duplicated. I just want to be able to return it from a function. And I seem to be able to do that with GCC.

I'd rather not have dereference smart pointers to a heap allocated ofstream, or reopen the file, as it currently works without doing those things. I just have a feeling I'm being a little "non-standard" in my approach, so a standard way of doing what I've described would be great.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Clinton
  • 22,361
  • 15
  • 67
  • 163

3 Answers3

16

I'm going to answer my own question here:

In the GCC C++0x Library Features page, have a look at item 27.9, which reads:

27.9 - File-based streams - Partial - Missing move and swap operations

I guess that's probably the issue I'm having with gcc.

Clinton
  • 22,361
  • 15
  • 67
  • 163
2

The problem is with this:

test(test&& old) {};

This lets you construct a new test from an rvalue test, yes, but it says nothing about your base, which is simply being default constructed (no open file). What you want is this:

test(test&& old) : std::ofstream(std::move(old)) {};

Which will move the stream from old into the base.

GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • 1
    Your answer gives the following compile error on gcc 4.5: "'std::ios_base::ios_base(const std::ios_base&)' is private". Are you sure you got this to compile, and if so, on what compiler? – Clinton Jan 28 '11 at 09:57
  • @Clinton: I haven't tried compiling your code, though I've done this many times before. (Note "it compiles" isn't necessarily test for it being correct C++). You should edit your question to include the new code you're trying and all the errors. – GManNickG Jan 28 '11 at 10:10
  • Are you sure ofstream has a rvalue constructor? – Clinton Jan 28 '11 at 11:35
  • @Clinton: Yup, in C++0x. Make sure you're compiling with it enabled. – GManNickG Jan 28 '11 at 11:54
  • In C++0x you should be able to return or move the ofstream directly, with no need to wrap it up in your test. Though with the corrected test move constructor, the wrapping is also correct. – Howard Hinnant Jan 28 '11 at 14:18
0

Does the caller need to know that you are returning an ofstream, or would it make more sense to return a streambuf, and let the caller wrap it inside a stream?

Simon Richter
  • 28,572
  • 1
  • 42
  • 64