Printing a string to a temporary stream object in

2019-01-28 05:54发布

I have a special type of ostringstream that I am trying to output text to as a temporary object but I'm having some trouble. To be clear, this is essentially what I want to do:

ostringstream() << "PARTY DOWN!" << endl;

Now before you say: "But Zack, that code is totally worthless! The object is destroyed at the end of the line, how would you even know if it did anything?", hear me out. I don't try to do this with plain ostringstreams, but rather a derived class in which the destructor actually provides a path for the data to exit the object. So in reality, it looks a lot more like this:

specialstringstream() << "PARTY DOWN!" << endl;

Where specialstringstream has a destructor which dumps the text elsewhere.

I won't go into too much detail as to why I do this. You'll have to trust me that it makes sense for what I need to do and it fits nicely into an existing, gigantic codebase.

Here's the problem: when I do this, everything compiles and runs, but I get a pointer address printed to my output instead of the "PARTY DOWN!" string. I determined that this is happening because the operator chosen by the compiler to perform the stream output is ostream& operator<< (const void* val), not ostream& operator<< (ostream& out, const char* s ).

I have a vague idea of why but I'm at a loss as to how to work around it. What can I do to get char*s to print into a temporary instance of a stringstream?

Here's a short version of the SpecialStringStream object that exhibits the behavior:

class SpecialStringStream : public ostringstream
{
  public:
    SpecialStringStream(ostream* regularStream)
    {
      regularStream_ = regularStream;
    }

    ~SpecialStringStream()
    {
      if (regularStream_ != NULL)
        (*regularStream_) << str(); 
    }

  private:
    ostream* regularStream_;
};

When I do something like: SpecialStringStream(someStreamPtr) << "PARTY DOWN!" << endl;, I get a pointer address like "00444D60" in my output instead of the message.

EDIT: Since I am too new of a user to answer my own question, here is what I've settled on thanks to all the responses.

I came up with the following solution, which works under Visual C++ 8 and all of the other compilers I needed it to. I created a template operator which basically strips a const SpecialStringStream of its constness, casts it as an ostream, and lets the ostream operators do their thing. Feel free to tear it to shreds in the comments and warn me of all the horrible potential bugs I've introduced!

template <class T>
std::ostream& operator<<(const SpecialStringStream &o, T msg)
{
    return static_cast<std::ostream&>(const_cast<SpecialStringStream&>(o)) << msg;
}

4条回答
啃猪蹄的小仙女
2楼-- · 2019-01-28 06:39

The overload ostream& operator<< (ostream& out, const char*) is not viable because your temporary won't bind to the non-const reference ostream&. There isn't really much you can do about that (since you can't cast an rvalue to an lvalue) other than declaring a local variable and using that:

{
  specialstringstream ss;
  ss << "Hello world" << std::endl; // OK, can bind to lvalue
}

Possible solution: You could declare another overload that accepts an rvalue reference:

std::ostream & operator<<(specialstringstream && o, const char * s)
{
  return o << s; // note: *not* "std::move(o)"
}
查看更多
贼婆χ
3楼-- · 2019-01-28 06:44

here is a workaround I'm using:

#define STRM2STR(x) (dynamic_cast<std::ostringstream &>(std::ostringstream() << std::dec << x).str())

insertion of std::dec will result in calling ostream::operator<< (ios_base& (*pf)(ios_base&)) which returns usable ostream &

查看更多
Viruses.
4楼-- · 2019-01-28 06:50

Maybe there is some better way but i figured an other solution:

#include <iostream>
#include <sstream>

class LogClass
{ 
    template <typename T>
    friend const LogClass& operator << (const LogClass& lc, const T& v);
public:
    LogClass()
        : str(new std::ostringstream())
        , refCount(new int(1))
    {}

    LogClass(const LogClass& other)
        : str(other.str)
    {
        ++(*refCount);
    }

    ~LogClass()
    {
        --(*refCount);
        if (!*refCount)
        {
            delete refCount;
            std::cout << str->str() << std::endl;
            delete str;
        }
    }
private:
    mutable std::ostringstream *str;
    int *refCount;

    LogClass& operator = (const LogClass&);
};

template <typename T>
const LogClass& operator << (const LogClass& lc, const T& v)
{
    (*lc.str) << v;
    return lc;
}

int main(int , char**)
{
    for (size_t i = 0; i < 10 ; ++i)
    {
        LogClass() << "PARTY DOWN! " << i;
    }
}

Running it with valgrind:

valgrind --tool=memcheck --leak-check=full ./LogClass

==16197== Memcheck, a memory error detector

==16197== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.

==16197== Using Valgrind-3.7.0.SVN and LibVEX; rerun with -h for copyright info

==16197== Command: ./LogClass

==16197==

PARTY DOWN! 0

PARTY DOWN! 1

PARTY DOWN! 2

PARTY DOWN! 3

PARTY DOWN! 4

PARTY DOWN! 5

PARTY DOWN! 6

PARTY DOWN! 7

PARTY DOWN! 8

PARTY DOWN! 9

==16197==

==16197== HEAP SUMMARY:

==16197== in use at exit: 0 bytes in 0 blocks

==16197== total heap usage: 40 allocs, 40 frees, 7,350 bytes allocated

==16197==

==16197== All heap blocks were freed -- no leaks are possible

==16197==

==16197== For counts of detected and suppressed errors, rerun with: -v

==16197== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 8)

Thats what i wanted, but it is not thread safe. Use shared_ptr from boost to make it so.

查看更多
叼着烟拽天下
5楼-- · 2019-01-28 06:51

You don't want to implement stringstream. You want to implement a basic_streambuf that writes into your special string.

The streams themselves are responsible for formatting and similar functions; the streambufs are responsible for what ultimately becomes the sink for the data.

After all, all a stringstream is is a iostream, with a basic_stringbuf attached.

查看更多
登录 后发表回答