问题
I am trying to figure out how to reuse a stringstream object without the need to re-allocate the underlying string every time I put something in the stream. I have found this answer which led me to do this:
int main() {
stringstream ss;
int x;
ss << "423";
ss >> x; // x is now 423
ss.clear();
ss.seekg(0);
ss.seekp(0);
ss << "1";
ss >> x; // x is now 123. Instead I want x to be 1.
std::cout << x << std::endl;
}
Unfortunately, this doesn't work since the contents of the string from the first pass are still there (after the first pass the string is "423"
and after the second pass they are "123"
). However, if I add a space right after the second put, things seem to work, like this:
int main() {
stringstream ss;
int x;
ss << "423";
ss >> x; // x is now 423
ss.clear();
ss.seekg(0);
ss.seekp(0);
ss << "1";
ss << " "; // add a space right after the desired value
ss >> x; // x is now 1
std::cout << x << std::endl;
}
After the second pass the string is "1 3"
. I am not very familiar with the I/O library and I would like to know if the above approach is safe, or if it just happens to work in this trivial example, or if there are better solutions out there. Live code here. Thank you!
回答1:
I did some investigations and experiments using clang with this code:
Test code
class LogHelper {
public:
~LogHelper() {
std::cout << out.str() << '\n';
}
std::ostream &stream() {
return out;
}
private:
std::ostringstream out;
};
#define LOG() LogHelper().stream() << __FUNCTION__ << '(' << __LINE__ << ")"
#define VAR(x) ", " #x "[" << x << ']'
class MyAllocator : public std::allocator<char> {
public:
using base = allocator<value_type>;
using base::allocator;
value_type* allocate( std::size_t n, const void * hint) {
LOG() << VAR(n);
return base::allocate(n, hint);
}
value_type* allocate( std::size_t n ) {
LOG() << VAR(n);
return base::allocate(n);
}
void deallocate( value_type* p, std::size_t n ) {
LOG() << VAR(n);
base::deallocate(p, n);
}
};
using MySStream = std::basic_stringstream<char, std::char_traits<char>, MyAllocator>;
using MyString = std::basic_string<char, std::char_traits<char>, MyAllocator>;
int main() {
MySStream ss; // (MyString(255, '\0'));
ss.clear();
int x;
ss << "423";
ss << " 423";
LOG();
ss << " 423jlfskdfjl jfsd sdfdsfkdf dsfg dsfg dfg dfg dsfg df gdf gdfg dsfg dsfgdsfgds";
LOG();
ss >> x;
ss.clear();
ss.str({});
ss.seekg(0);
ss.seekp(0);
ss << "1";
ss >> x;
std::cout << x << std::endl;
LOG();
return 0;
}
- your example with longing allocations clang, visual studio, gcc 7.3 ignores custom allocator, gcc 8.x do not compile
main(55)
allocate(34), n[48]
allocate(34), n[96]
deallocate(39), n[48]
main(57)
1
main(70)
deallocate(39), n[96]
- preset long string on stream clang, visual studio
allocate(34), n[256]
allocate(34), n[256]
deallocate(39), n[256]
main(55)
main(57)
1
main(70)
deallocate(39), n[256]
And I have couple findings
- clang and visual behave in same meaner, gcc has some stang issues with this code.
std::basic_stringstream
string buffers always grows, never shrinksstd::basic_stringstream
sucks. You can't reserve string size or buffer size, like in casestd::string
. Custom allocator can be passed only by type, you can't provide allocator by object.- To reduce allocations you have to set large string at the begging, then until you you will not succeed its capacity re-allocation will not happen (second example).
- providing a custom allocator doesn't help a lot and it adds boiler plate code when fetching result string. In my examples it is used mainly to log allocations and deallocations.
ss.str({});
do not cause allocation. Here small string optimization helps
Conclusions:
- you can safely do
ss.str({});
as recommended in linked by you SO answer and it will not cause allocation. Here small string optimization helps and the fact that - custom allocator is not very helpful
- setting large dummy string at begging is quite effective evil hack
- finding alternative should be better approach (maybe boost - I didn't test it)
- Point
1
and your question shows that you didn't do any measurements and your question is based on personal assumptions.
来源:https://stackoverflow.com/questions/52007781/reusing-a-stringstream-without-re-allocation