std::ostringstream memory leak

1,772 views
Skip to first unread message

A. Robert S.

unread,
Aug 27, 2013, 10:26:07 AM8/27/13
to cppu...@googlegroups.com
Hi all,
 
Conceivably, when testing C++ code that writes a lot to an output file, using std::ostringstream instead of std:ofstream makes for a very handy spy.
 
The idea is that the object in question accepts a std::ostream in its constructor. The production code then passes either std::ofstream or std::cout, which works fine.
 
The test passes std::ostringstream, which would be fine, except that it leaks memory. Common lore on the Web has it that std:ostringstream never leaks memory.... but there it is. A simple test case:
 
#include "TestHarness.h"
#include <sstream>
class MyClass {
public:
    MyClass(std::ostream& o) : o_(o) {}
    void doStuff() {
        o_ << "stuffystuffstuffstuffydistuff";
    }
private:
    std::ostream& o_;
};
TEST_GROUP(leak_test) {
    std::ostringstream o;
    MyClass* m;
    void setup() {
        m = new MyClass(o);
        m->doStuff();
    }
    void teardown() {
        delete m;
    }
};
TEST(leak_test, leak) {
    STRCMP_CONTAINS("stuffy", o.str().c_str() );
}
 
This will yield the following output:
 
error: Failure in TEST(leak_test, leak)
        Memory leak(s) found.
Leak size: 525 Allocated at: <unknown> and line: 0. Type: "new" Content: "☺"
Total number of leaks:  1
.
It would be nice to have a way to properly dispose of the internal buffer. N.B. memory only leaks if the buffer is actually written to.
Robert
 

A. Robert S.

unread,
Aug 27, 2013, 11:14:23 AM8/27/13
to cppu...@googlegroups.com
I tried to reduce this problem to its simplest form. This will leak:
 
TEST_GROUP(leak_test) {
    std::ostringstream o;
};
TEST(leak_test, leak) {
    o << "stuffystuffstuffstuffydistuff";

    STRCMP_CONTAINS("stuffy", o.str().c_str() );
}
 
And this will not:
 
TEST_GROUP(leak_test) {
};
TEST(leak_test, leak) {
    std::ostringstream o;
    o << "stuffystuffstuffstuffydistuff";

    STRCMP_CONTAINS("stuffy", o.str().c_str() );
}
Now it is starting to look a whole lot more like a CppUTest problem-- but why?

 

Andreas Stenius

unread,
Aug 27, 2013, 11:56:42 AM8/27/13
to cppu...@googlegroups.com
I got an idea:

2013/8/27 A. Robert S. <arst...@googlemail.com>
[...] 
I tried to reduce this problem to its simplest form. This will leak:
 
TEST_GROUP(leak_test) {
    std::ostringstream o;

o here is allocated along with the test group object, which, if I'm not mistaken is a static object, is allocated during startup and not deleted until program exit.
So any memory allocated by o during test execution will be reported as leaks.

};
TEST(leak_test, leak) {
    o << "stuffystuffstuffstuffydistuff";

    STRCMP_CONTAINS("stuffy", o.str().c_str() );
}
 
And this will not:
 
TEST_GROUP(leak_test) {
};
TEST(leak_test, leak) {
    std::ostringstream o;

here o is allocated and free'd during the lifetime of the test function, hence no leak is reported.
 
    o << "stuffystuffstuffstuffydistuff";

    STRCMP_CONTAINS("stuffy", o.str().c_str() );
}


Cheers
 

A. Robert S.

unread,
Aug 27, 2013, 12:10:36 PM8/27/13
to cppu...@googlegroups.com
Hi Andreas,
 
You are totally right -- I had the compiler resolve the macros and arrived pretty much at the same conclusion. Yet, this is annoying, because in the actual test case a whole lot of repetitive stuff is happening that I would have liked to deal with in the setup method.
 
I wonder whether there is a way to tell cpputest not to report this particular "leak"?
 
Actually, each test case resolves to a method. So in the second case, o is allocated on the stack, and the compiler knows it has to go away when the function exits...

A. Robert S.

unread,
Aug 27, 2013, 12:14:33 PM8/27/13
to cppu...@googlegroups.com
Hey, you gave me a new idea ;-)
 
If I make o a static variable to start with, and I initialize it to some ridiculessly voluminous string, then the memory does not get allocated in the context of any text, and no leaks are reported.
 

On Tuesday, August 27, 2013 5:56:42 PM UTC+2, Andreas Stenius wrote:

Andreas Stenius

unread,
Aug 28, 2013, 4:05:21 AM8/28/13
to cppu...@googlegroups.com
2013/8/27 A. Robert S. <arst...@googlemail.com>
Hey, you gave me a new idea ;-)
 
If I make o a static variable to start with, and I initialize it to some ridiculessly voluminous string, then the memory does not get allocated in the context of any text, and no leaks are reported.

well, as this works, it's not very robust.. (and consuming a "ridiculously" amount of memory); better would be to have something like (pseudo code):

TEST_GROUP(leak_test) {
  std::ostringstream *_o;
  std::ostringstream& o() { return *_o; }

  TEST_SETUP() { _o = new std::ostringstream(); }
  TEST_TEARDOWN() { delete _o; }
}

TEST(leak_test, no_leak) {
  o() << "stuff...";
  STRCMP_CONTAINS("stuff", o().str().c_str() );
}


Bas Vodde

unread,
Aug 28, 2013, 5:45:35 AM8/28/13
to cppu...@googlegroups.com

Hiya,

> I got an idea:
>
> 2013/8/27 A. Robert S. <arst...@googlemail.com>
> [...]
> I tried to reduce this problem to its simplest form. This will leak:
>
> TEST_GROUP(leak_test) {
> std::ostringstream o;
>
> o here is allocated along with the test group object, which, if I'm not mistaken is a static object, is allocated during startup and not deleted until program exit.
> So any memory allocated by o during test execution will be reported as leaks.

Actually, this isn't true anymore. The TEST_GROUP is created *just* before the test run.

So, it will depend a bit on the CppUTest version…

Bas

>
> };
> TEST(leak_test, leak) {
> o << "stuffystuffstuffstuffydistuff";
>
> STRCMP_CONTAINS("stuffy", o.str().c_str() );
> }
>
> And this will not:
>
> TEST_GROUP(leak_test) {
> };
> TEST(leak_test, leak) {
> std::ostringstream o;
>
> here o is allocated and free'd during the lifetime of the test function, hence no leak is reported.
>
> o << "stuffystuffstuffstuffydistuff";
>
> STRCMP_CONTAINS("stuffy", o.str().c_str() );
> }
>
>
> Cheers
>
>
> --
> You received this message because you are subscribed to the Google Groups "cpputest" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to cpputest+u...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.

Bas Vodde

unread,
Aug 28, 2013, 5:46:59 AM8/28/13
to cppu...@googlegroups.com

Hiya,

In latest CppUTest, this shouldn't be needed.

We've changed this about a year ago…

Bas

Andreas Stenius

unread,
Aug 28, 2013, 6:14:07 AM8/28/13
to cppu...@googlegroups.com

2013/8/28 Bas Vodde <ba...@odd-e.com>
[...]

We've changed this about a year ago…

Heh... haven't updated in a while... (don't fix what ain't broken) :p


Bas Vodde

unread,
Aug 29, 2013, 2:57:59 AM8/29/13
to cppu...@googlegroups.com

Hi Robert,

Were both the tests is the same executable?

The problem you are encountering seems to relate to static data in STL. This is a common problem. STL often allocates buffers and then it keeps them for re-use later (for efficiency sake) but this causes leaks. So, for example, when you create an ostringstream, it might allocate a buffer and put it to a static variable. That means, it won't deallocate it anymore at the end of the test and thus is shows up as a memory leak.

The best trick I know is to create an ostringstream in the main. This would cause the static allocation but it isn't in a test so it won't be causing memory leaks…

Bas

A. Robert S.

unread,
Sep 2, 2013, 7:07:23 AM9/2/13
to cppu...@googlegroups.com
Hi Bas,
 
actually, the safest thing to do is declaring the ostringstream inside the test case. It will then go out of scope when the test case finishes, and there will be no leaks.
 
However, I need to do some setup-ish stuff with the ostringstream first, so I prefer declaring it in the test group, or as it were, statically.
 
Arnd

A. Robert S.

unread,
Sep 2, 2013, 7:18:34 AM9/2/13
to cppu...@googlegroups.com
Hi Andreas,
 
You are right. Allocating o dynamically and destroying it in teardown() works great. I dereference o directly tho, as in *o.
 
Thanks,
Robert

A. Robert S.

unread,
Sep 2, 2013, 7:27:57 AM9/2/13
to cppu...@googlegroups.com
Hi Bas,
 
You are right. In the latest version, the problem does not occur. It does occur in version 3.3.
 
Arnd

A. Robert S.

unread,
Sep 2, 2013, 7:36:46 AM9/2/13
to cppu...@googlegroups.com
...or so I thought. For some reason, this approach leads to crashes when I use it for a number of tests. So, I'll stick with my original approach.
 
Robert

Bas Vodde

unread,
Sep 2, 2013, 8:42:15 AM9/2/13
to cppu...@googlegroups.com

I'd be interested in reasons for crashes though :P

Bas

A. Robert S.

unread,
Sep 2, 2013, 9:02:06 AM9/2/13
to cppu...@googlegroups.com
Me, too.
 
Present them to me on a platter, anyone ??
 
Robert

Bas Vodde

unread,
Sep 2, 2013, 9:02:45 AM9/2/13
to cppu...@googlegroups.com
Could you send me the code that crashes :)

Bas

A. Robert S.

unread,
Sep 2, 2013, 9:04:06 AM9/2/13
to cppu...@googlegroups.com
Well, actually, errrr, it doesn't crash anymore. I strongly suspect programmer error :-(.
 
Fact is, dynamic allocation works just fine after all.
 
Robert

On Monday, September 2, 2013 2:42:15 PM UTC+2, Bas Vodde wrote:

Bas Vodde

unread,
Sep 2, 2013, 9:07:55 AM9/2/13
to cppu...@googlegroups.com

Sure, but the static allocation usually leads to cleaner tests :) Thats why we made that possible in the later CppUTest version :)

(No need for setup and teardown anymore then)

Bas

A. Robert S.

unread,
Sep 2, 2013, 9:23:15 AM9/2/13
to cppu...@googlegroups.com
Well, the thing is that reusing an ostringstream is kind of.... messy.
 
It so happens that most of my test groups have only one test case to them. One has four; the ostringstream would just keep collecting output and I didn't care too much.
 
At the very least you'd have to do o.str(""); in teardown().
 
Robert

Bas Vodde

unread,
Sep 2, 2013, 9:26:36 AM9/2/13
to cppu...@googlegroups.com

Nope, because each test is created in a separate object.

So, if you have 4 tests, then it will actually create 4 ostringstreams… thus they aren't shared.

Thus, no need for the o.str("") in the teardown.

Bas

A. Robert S.

unread,
Sep 2, 2013, 9:38:50 AM9/2/13
to cppu...@googlegroups.com
Ooookay. So the only snag is that it won't be compatible with older versions of CppUTest (like v3.3).
 
Robert

Bas Vodde

unread,
Sep 2, 2013, 9:55:59 AM9/2/13
to cppu...@googlegroups.com

It is :)

Except that your earlier memory leaks are fixed :P

Bas

A. Robert S.

unread,
Sep 2, 2013, 10:07:55 AM9/2/13
to cppu...@googlegroups.com
Is the fix present in CppUTest v3.4?

Bas Vodde

unread,
Sep 2, 2013, 7:15:03 PM9/2/13
to cppu...@googlegroups.com

Yes, it should be…

Bas

A. Robert S.

unread,
Sep 3, 2013, 2:53:25 AM9/3/13
to cppu...@googlegroups.com
Oki. Will try. Robert
Message has been deleted

Bas Vodde

unread,
Sep 3, 2013, 9:44:31 PM9/3/13
to cppu...@googlegroups.com

Yeah, I ought to be doing an official release, you are right.

But 3.4 shouldn't have the leak. Could you send me the test so I can look into it?

Thanks!

Bas

On 3 Sep, 2013, at 3:22 PM, A. Robert S. <arst...@googlemail.com> wrote:

> Sorry to report that with v3.4 it leaks. Are you considering another "official" release anytime soon?
>
> Arnd
>
> On Tuesday, September 3, 2013 1:15:03 AM UTC+2, Bas Vodde wrote:
>

A. Robert S.

unread,
Sep 4, 2013, 12:13:44 PM9/4/13
to cppu...@googlegroups.com
I deleted the post above, because with 3.4 it doesn't leak, after all. I had some issues with my project not linking the correct libraries.
 
So, I am using 3.4 from now on. The test? Yes, will do. But I'll have to dig into git, what with all the refactoring that happened since.... :P
 
I will also post a later incarnation of the test here for discussion. std::ostringstream is a rather convenient option for testing C++ code that is supposed to write text files (if you design your code right, which means passing the more generic std::istream& and std::ostream& via constructors, rather than instantiating streams inside a class, or using one of the more specialized stream classes). You can then pass an ifstream, ofstream, ostringstream and even a stringstream, which can be examined by tests.
 
Arnd

A. Robert S.

unread,
Sep 5, 2013, 2:56:08 PM9/5/13
to cppu...@googlegroups.com
Where should I send the test to? I prefer not to post it publicly.

Bas Vodde

unread,
Sep 6, 2013, 12:22:15 AM9/6/13
to cppu...@googlegroups.com

Just my (this) email directly.

But you mentioned it didn't leak anymore?

Bas

A. Robert S.

unread,
Sep 6, 2013, 1:53:23 AM9/6/13
to cppu...@googlegroups.com
I cant't see your (this) email :-P

With CppUTest 3.4 it runs like an ace. With 3.3 my latest tests report thousands of leaks...

I do see my own email. Do you see mine? There didn't seem to be a way to stop it from showing, but yours doesn't show after all.

Arnd

Bas Vodde

unread,
Sep 6, 2013, 5:49:34 AM9/6/13
to cppu...@googlegroups.com

I'll drop you a mail.

I think with 3.3 it should leak. With 3.4 we changed how tests are allocated causing the leaks to disappear.

So, if the problem is only with 3.3, then it won't be useful as that won't be fixed :P

Bas

A. Robert S.

unread,
Sep 6, 2013, 8:59:29 AM9/6/13
to cppu...@googlegroups.com
Just drop me a mail if you still want it, even though of little use. ;-)
Reply all
Reply to author
Forward
0 new messages