65

Just a quick question here guys. I've been searching to no avail so far.

A bit more info here:

stringstream report_string;

report_string << "some string here...";

In my code itself are various conditions for assigning values to the report_string variable.

I'd like to check whether it was assigned a value or not.

4
  • Well, there are [potentially] two different "things" being asked -- what have you tried? Commented Nov 8, 2011 at 5:34
  • Define your terms. An example program where you would use this ability would help a lot (with a placeholder where you do the "check for null"). Commented Nov 8, 2011 at 5:35
  • Can you clarify what you mean by "variable is empty/null"? Are you saying you want to figure out if the stream contains no data? Commented Nov 8, 2011 at 5:35
  • Sorry guys. I a just added a bit more info. Commented Nov 8, 2011 at 5:43

8 Answers 8

72

myStream.rdbuf()->in_avail() can be used to get the count of available characters ready to be read in from a stringstream, you can use that to check if your stringstream is "empty." I'm assuming you're not actually trying to check for the value null.

For example if you want to extract an int from a stringstream and then see if there were any left over characters (ie. non-numeric) you could check if myStream.rdbuf()->in_avail() == 0.

Is that something similar to what you're trying to do? I'm not sure if there's better ways but I've done this in the past and it's worked fine for me.

https://en.cppreference.com/w/cpp/io/basic_streambuf/in_avail

EDIT: I see you just updated your question as I posted.

Sign up to request clarification or add additional context in comments.

4 Comments

Just what I needed. This would work fine for me since I clear the stream on every loop iteration and I'd just like to see if a value was assigned to it. Thanks! :)
@Matt: rather than clearing the stream at each iteration, you could perfectly declare it within the loop and save yourself some work... and potential bugs (like using continue and forget about clearing it...)
I find that rdbug()->in_avail() always returns 0 as seems to be described in various places on the web for GNU g++. I found Cubbi's comments at cpp.re/forum/general/233925 informative: "it reports the size of the get area (meaning, how many bytes can be read without making a virtual call)... Without a virtual function call, there is no way for a write to touch the read-side of a stream." My use case is simple/limited and I don't mind throwing away any characters in the stringbuffer, so I can simply test my_stream.readsome(&test_char, 1) == 0 to confirm it is empty.
Also looking at my_stream.tellp() == 0, what about my_stream.tellg()>=mystream.tellp()? I don't really see the get streampointer being larger than the put streampointer, but anyway. This also avoids any 0 conversion uncertainties and it is fairly clear and causes no construction.
18

This method is efficient and should work with output strings as well:

ostringstream report_string;

if (report_string.tellp() == 0) {
    // do something
}

6 Comments

Should be if (report_string.tellp() == std::streampos(0)).
I like this one, of course it depends on the code not trying to do a seekp() on you. Also I think OP's question was about doing something if tellp() != 0.
@OzSolomon Could you please explain why std::streampos(0) is better than simply 0? I'm genuinely curious.
@Yurim I don't remember now exactly why I posted that comment, but likely this is because std::fpos (the implementation class for std::streampos) has no standards-formal requirement to have a constructor that takes an int. That means that 0 may, or may not be implicitly convertible to streampos, depending on your particular standard library implementation.
This doesn't always work - for example, whether or not the string stream is constructed with content - i.e. for ostringstream report_string; and ostringstream report_string{"initial text"}; - (tellp() == 0) will be true.
|
12

An easy check would be to see if the string content of the stream is empty or not:

#include<assert.h>
#include<sstream>

int main(){
std::stringstream report_string;
report_string << ""; // an empty strin g

//emptiness check of stringstream
assert(report_string.str().empty());
}

1 Comment

The problem with the str() is that it potentially creates a huge block of memory just to check whether it's empty or not.
8

It's normally reasonable and readable to use...

report_string.str().empty()

...but that may involve dynamic allocation and copying the entire string to a temporary, only to be thrown away.

If performance is important, another option is...

report_string.peek() == decltype(report_string)::traits_type::eof()
  • this looks for a character not yet extracted from the stream, ignoring input that's already been successfully parsed/extracted

    • that's different from testing report_string.str().empty(), which still "sees" already-extracted input
  • if earlier parsing left the stream in a fail state you haven't clear()ed, this will return eof() regardless of whether there are more unextracted characters

2 Comments

report_string.peek() == std::char_traits<char>::eof() did the right thing for me (i.e. at least for the special case of checking whether a stringstream is unmodified) - I think std::char_traits<char>::eof() is probably the same as decltype(report_string)::traits_type::eof(). If the check fails, I'm interested passing report_string.rdbuf() to std::cout to avoid the extra copy, so calling report_string.str() would have made that optimization pointless. (calling rdbuf without the check has side effects, and may throw).
@Apriori: std::char_traits<char>::eof() will be the same if report_string is instantiated for char - yours is simpler, but an extra point of maintenance if report_string's type changes one day - much of a muchness all up. Interesting insight about rdbuf() potentially throwing - I didn't think that could happen! Cheers
6

One way would be to check the size of the internal string and compare against zero. Note that this is different from myStream.rdbuf()->in_avail() as AusCBlock suggests; in_avail() can return a value different from the actual size of the stream (if, for example, the internal buffer is represented with multiple non-contiguous memory blocks). In particular, in_avail() can, in principle, return zero in non-empty buffers (it's possible that the stringbuf specification restricts this further; I have no checked in that much detail).

1 Comment

This looks interesting. I might need this later on. Thanks! :)
5

Use eof() instead.

Sample Code:

stringstream report_string;
if ( !(report_string.eof()) ) 
    cout << "report_string EMPTY! \n";

2 Comments

From what I see, eof() is always false for std::stringstream. Haven't seen anything in the documentation. But that's how it behaves even if it's not empty.
The code in this answer is broken for the same reason while (in.eof()) is often broken: eof() is only set if an earlier input attempt hit eof() before succeeding. So, if say report_string >> my_int had read a number from the input and the last digit was the very last thing in the stream, eof() would be true. But, if you used char c = report_string.get(); to get the final character, it would succeed without trying to do anything at the end of file, and eof() would not be true. Illustration: here
3

I know that this question is very old and already answered, but depending on the situation there might also be yet another approach worth considering:

When you are testing if a stringstream is empty, you properly intend to do something with either the individual strings or each line in the stringstream; thus you will most likely be using either the >> operator or std::getline on the stringstream... and if the stream is empty these simply have the value false, thus you could write:

stringstream report_string;

foo(report_string)// some functions which may or may not write to report_string

string single_report;//string to read to

bool empty=true;//First assume it was empty
while(getline(report_string,single_report))//Alternatively use report_string>>single_report if you don't want entire lines
{
    empty=false;//...it wasn't empty
    bar(single_report);//Do whatever you want to do with each individual appended line 
}

if (empty)
{
    //... whatever you want to do if the stream was empty goes here
}

It should be noted that this approach assumes that you were planning on cycling through the stringstream; if you were not, then this approach can't be used.

Comments

0

How about another approach?

If you make the ostringstream an optional type you can check that its been assigned to before using it.

Imagine a class called lazy<> which lazily constructs an object when needed, then we could do this:

int main()
{
    using namespace std;

    auto oss1 = lazy<std::ostringstream>();
    auto oss2 = lazy<std::ostringstream>();

    use(oss1) << "Hello";

    if (oss1) cout << use(oss1).str() << endl;
    if (oss2) cout << use(oss2).str() << endl;

    if_used(oss1, [](auto& ss) { cout << ss.str() << endl; });
    if_used(oss2,
            [](auto& ss) { cout << ss.str() << endl; },
            [](auto& oss) { cout << "oss2 is not used" << endl; });

    use(oss2) << "Goodbye";
    if_used(oss2, [](auto& ss) { cout << ss.str() << endl; });

    return 0;
}

yielding this output:

Hello
Hello
oss2 is not used
Goodbye

Advantages:

  • no redundant construction of the stringstream when not used.

  • optional provides an exception if the unused stringstream is subsequently used (via const reference)

Full example below with customisable constructor:

I've used std::experimental for the optional, but you could just as easily use boost::optional.

#include <iostream>
#include <experimental/optional>
#include <utility>
#include <type_traits>
#include <sstream>

using std::experimental::optional;

namespace detail {
    template<class T, class Constructor>
    struct lazy final
    {
        template<class Con , std::enable_if_t< not std::is_same<std::decay_t<Con>, lazy>::value > * = nullptr>
        lazy(Con&& con)
        : _constructor(std::forward<Con>(con))
        {}

        T& get() {
            if (not bool(_opt)) {
                _opt = _constructor();
            }
            return *_opt;
        }

        const T& get() const {
            return *_opt;
        }

        bool used() const {
            return bool(_opt);
        }

        operator bool() const {
            return used();
        }

    private:
        Constructor _constructor;
        optional<T> _opt;
    };

    template<class T>
    struct default_construct {
        T operator()() const { return T(); }
    };

    struct no_action {
        template<class T>
        void operator()(T&) const { }
    };
}


template<class T, class Constructor = detail::default_construct<T> >
auto lazy(Constructor&& con = detail::default_construct<T>())
{
    return detail::lazy<T, std::decay_t<Constructor>>(std::forward<Constructor>(con));
}

template<class T, class Constructor>
auto& use(detail::lazy<T, Constructor>& l)
{
    return l.get();
}

template<class T, class Constructor>
auto& use(const detail::lazy<T, Constructor>& l)
{
    return l.get();
}

template<class T, class Constructor, class F, class Else = detail::no_action>
void if_used(detail::lazy<T, Constructor>& l, F&& f, Else&& e = detail::no_action())
{
    if (l.used())
        f(l.get());
    else
        e(l);
}

template<class T, class Constructor, class F, class Else = detail::no_action>
void if_used(const detail::lazy<T, Constructor>& l, F&& f, Else&& e)
{
    if (l.used())
        f(l.get());
    else
        e(l);
}

int main()
{
    using namespace std;

    auto oss1 = lazy<std::ostringstream>();
    auto oss2 = lazy<std::ostringstream>();

    use(oss1) << "Hello";

    if (oss1) cout << use(oss1).str() << endl;
    if (oss2) cout << use(oss2).str() << endl;

    if_used(oss1, [](auto& ss) { cout << ss.str() << endl; });
    if_used(oss2,
            [](auto& ss) { cout << ss.str() << endl; },
            [](auto& oss) { cout << "oss2 is not used" << endl; });

    use(oss2) << "Goodbye";
    if_used(oss2, [](auto& ss) { cout << ss.str() << endl; });

    return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.