C++ iostream Corruption using stringstream

微笑、不失礼 提交于 2019-11-28 02:05:45

The problem isn't with the logger, rather it's with your use of stringstream. When std::stringstream is initialized, the stream's position indicator is positioned at the beginning of the stream.

Now when you start writing to the string using '<<', you start writing at the position indicator, replacing whatever was there before.

To work around this, you can initialize the stringstream with std::stringstream("Another ", stringstream::in | stringstream::out | std::stringstream::ate)

(as per http://www.cplusplus.com/reference/iostream/stringstream/stringstream/ )

Using variadic templates:

void Log_impl(std::ostream &os) {} // recursion base case

template<typename T,typename... Us>
void Log_impl(std::ostream &os,T &&t,Us &&... us) {
    os << std::forward<T>(t);
    Log_impl(os,std::forward<Us>(us)...);
}

template<typename... Ts> void Log(Ts &&... ts) {
    std::stringstream ss;
    Log_impl(ss,std::forward<Ts>(ts)...);
    fprintf(stdout,"%s\n",ss.str().c_str()); // thread safe output
}

usage:

Log("Another"," test ",100);

I haven't actually tested this code...

Jerry Coffin

At least in my opinion, this approach to the problem is at least somewhat clumsy to use, since it requires you to create an auxiliary ostream object of some sort, stream data into it, and then pass that to your log. That doesn't seem to fit very closely with what you've said you'd really prefer.

I'm also a bit less than excited about how you've done your thread-locking code. Just for example, if you get an exception during your std::cout << os.rdbuf();, you can exit the scope without leaving the critical section.

I think I'd start with a thin wrapper around a critical section, add an RAII-style class to lock the critical section (and automatically unlock it when it goes out of scope), then use those in implementing the Log class. Further, I'd cheat and have the Log class use a template member function to accept almost any type of output in one fell swoop:

Edit: After some more thought, I've decided to embrace the notion that every problem can be solved with another level of indirection. To do that, I've added an intermediate transaction that encapsulates chaining the output of a number of items into a stringstream, then writing out that result as a thread-safe transaction.

#include <windows.h>
#include <iostream>
#include <sstream>

class crit_sect {
    CRITICAL_SECTION cs;

    void lock() { EnterCriticalSection(&cs); }
    void unlock() { LeaveCriticalSection(&cs); }
    friend class lock;

    crit_sect(crit_sect const &); /* = delete; */
    crit_sect &operator=(crit_sect const &other); /* = delete; */
public:
    crit_sect() { InitializeCriticalSection(&cs); }
    ~crit_sect() { DeleteCriticalSection(&cs); }
};

class lock {
    crit_sect &cs;
public:
    lock(crit_sect &c) : cs(c) { cs.lock(); }
    ~lock() { cs.unlock(); }
};

class transaction {
    std::ostringstream buffer;
public:
    transaction(std::string const &s="") : buffer(s, std::ios::out | std::ios::ate) {}

    template <class T>
    transaction &operator<<(T const &t) {
        buffer << t;
        return *this;
    }

    friend std::ostream &operator<<(std::ostream &os, transaction const &t) {
        return os << t.buffer.str();
    }
};

class Log {
    std::ostream &out;
    crit_sect mutex;
public:
    Log(std::ostream &sink) : out(sink) { }

    template <class T>
    void operator<<(T const &t) {
        lock l(mutex);
        out << t;
    }    
};

int main() {
    Log l(std::cout);

    l << "This is a string\n";

    l << (transaction("Another ") << "Test");
    return 0;
}

Since the log type doesn't support chaining, any attempt at chaining the output without using a transaction will fail (won't compile). Compared to the original, usage is still a bit cleaner though -- the extra parameters needed for the ostringstream ctor are hidden, and the name transaction clarifies what is being done, or rather, accomplished.

<< doesn't send the null byte at the end of "test" into the temporary stringstream("Another ") you created, which is why you see "testher".

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!