10
\$\begingroup\$

Please take a look at this code:

class error_stream: boost::noncopyable
{
public:
 template<typename T>
 std::ostream& operator<<(T&& arg)
 {
 return strm() << std::forward<T>(arg);
 }
 std::string str() { return strm().str(); }
 bool empty() const { return !holder_.valid(); }
private:
 std::ostringstream& strm() { return holder_; }
 struct alignas(std::ostringstream) holder
 {
 holder(): ptr_{} {}
 ~holder()
 {
 if(valid())
 ptr_->~basic_ostringstream();
 }
 operator std::ostringstream&()
 {
 if(!valid())
 ptr_ = new (memory_) std::ostringstream{};
 return *ptr_;
 }
 bool valid() const { return ptr_; }
 private:
 char memory_[sizeof(std::ostringstream)];
 std::ostringstream* ptr_;
 } holder_;
};

Here I want to avoid creating std::ostringstream if there was no output made into it:

error_stream strm;
//strm << "...";
if(!strm.empty())
{
 //...get strm.str();
}

I use placement new/delete when a client outputs something into the stream. Do I need to use alingas for block of memory that will hold instance of std::ostringstream?

UPDATE: To give more context - this is part of THROW wrapper:

...
#define THROW_EX_WITH_LOCATION(EXCEPTION, LOCATION) \
 for(error_stream strm;; \
 strm.empty()? \
 throw_exception<EXCEPTION>(LOCATION): \
 throw_exception<EXCEPTION>(LOCATION, strm.str())) \
 strm
...

I don't want to create any stream if ther is no error message but solution should work with minumum overhead over version with just std::ostringstream.

Consider the following cases:

THROW();

and

THROW() << "some error";

I agree this looks like nitpicking.

asked Feb 20, 2014 at 2:17
\$\endgroup\$

1 Answer 1

7
\$\begingroup\$

This seems overcomplicated to me. Lazy-loading something doesn't require placement-new trickery and mucking around with alignments. Why not simply store a pointer to the stream which is only initialised when the user performs some kind of operation? This is the general way to do lazy-loading:

struct error_stream
{
private:
 std::unique_ptr<std::ostringstream> stream;
 // This should use make_unique, which you can find 
 // or write yourself without too much hassle.
 void construct()
 {
 stream = std::unique_ptr<std::ostringstream>(
 new std::ostringstream);
 }
public:
 template <typename T>
 std::ostream& operator<<(T&& arg)
 {
 if(!stream) {
 construct();
 }
 (*stream) << std::forward<T>(arg);
 return *stream;
 }
 bool empty() const
 {
 return stream ? false : true;
 }
 std::string str() 
 {
 if(empty()) {
 construct();
 }
 return stream->str();
 }
};

This seems to have the semantics you want (including being non-copyable due to using a std::unique_ptr) with much less hassle. You don't have to worry about placement new or any alignment requirements, which is going to make the code a -lot- harder to maintain.

Edit: If you really what to avoid the heap, I suppose you can use placement new. You allocate the memory for the stream when you create an error_stream, so I'm curious as to what problem you're trying to solve exactly. That being said, what you want to align is the storage for the ostringstream.

char memory_[sizeof(std::stringstream)];

Hence I'd use:

struct holder
{
....
private:
 alignas(std::ostringstream) char memory_[sizeof(std::stringstream)];
 ...
};
answered Feb 20, 2014 at 5:05
\$\endgroup\$
2
  • \$\begingroup\$ Yes your solution is cleaner. I'm trying to avoid going to free store if possible. \$\endgroup\$ Commented Feb 20, 2014 at 5:21
  • \$\begingroup\$ @AlexT See my edit. \$\endgroup\$ Commented Feb 20, 2014 at 5:36

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.