Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Odd behavior with ostringstream

I was trying to think of a clever way to concatenate various things into a single string argument for a function without having to use an ostringstream explicitly. I thought of:

#define OSS(...) \
  dynamic_cast<std::ostringstream const&>(std::ostringstream() << __VA_ARGS__).str()

However, given:

void f( string const &s ) {
  cout << s << endl;
}

int main() {
  char const *const s = "hello";

  f( OSS( '{' << s << '}' ) );

  ostringstream oss;
  oss << '{' << s << '}';
  cout << oss.str() << endl;
}

it prints when run:

123hello}
{hello}

where 123 is the ASCII code for }. Why does using the macro get it wrong?

FYI: I'm currently using g++ 4.2.1 on Mac OS X as part of Xcode 3.x.


Solution I'm now using

class string_builder {
public:
  template<typename T>
  string_builder& operator,( T const &t ) {
    oss_ << t;
    return *this;
  }

  operator std::string() const {
    return oss_.str();
  }

private:
  std::ostringstream oss_;
};

#define BUILD_STRING(...) (string_builder(), __VA_ARGS__)

using namespace std;

void f( string const &s ) {
  cout << s << endl;
}

int main() {
  char const *const s = "hello";

  f( BUILD_STRING( '{', s, '}' ) );
}
like image 575
Paul J. Lucas Avatar asked Feb 24 '23 12:02

Paul J. Lucas


1 Answers

std::ostringstream() is temporary which thus can be bound only to const references. Standalone operator<< (which take non const references as first argument) aren't considered and only the member one are. The best match in these for a char is converting the char to int.

This problems occurs often with string literals whose address is then displayed.

To solve the problem, the trick is to find a way to transform the temporary in a reference. The member operator<<s do that, but only the one for manipulator does it without side effect and only if the manipulator is a noop -- flush could be used. The members flush and write are also candidates. So for instance

#define OSS(...) \
    dynamic_cast<std::ostringstream const&>(std::ostringstream().flush() << __VA_ARGS__).str()
like image 62
AProgrammer Avatar answered Mar 08 '23 18:03

AProgrammer