Skip to main content
gamedev.net
🔒 Locked

Trouble with a macro hack

Started by 255 Feb 26, 2005 at 6:44 AM 11 replies 1.9k views
Original Post
255
255
I have a macro like this:

// usage: makestr(something << "something_else" << 123)
#define makestr(args) (reinterpret_cast<std::stringstream&>(std::stringstream() << args)).str()

//test case:
std::cerr << makestr("A" << 2 << "B") << std::endl;

It compiles but either segfaults or prints some random hex number. I've tried all three types of casts. std::stringstream.str() returns a copy, not a reference, so that is not the problem. The latest crash yielded a backtrace pointing somewhere into std::string's copy constructor:

#0  0xb7fa4e07 in std::string::_Rep::_M_grab(std::allocator<char> const&, std::allocator<char> const&) ()
   from //usr/lib/gcc-lib/i686-pc-linux-gnu/3.3.5/libstdc++.so.5
#1  0xb7fa4ed8 in std::string::string(std::string const&) ()
   from //usr/lib/gcc-lib/i686-pc-linux-gnu/3.3.5/libstdc++.so.5
#2  0x0804b3e1 in std::basic_stringbuf<char, std::char_traits<char>, std::allocator<char> >::str() const (this=0xbfffeee0) at sstream:148
#3  0x0804a986 in main () at sstream:617
For that same test case dynamic and static casts yielded the random number (in my case 0x804bfd32B). Any help greatly appreciated..
IFooBar
IFooBar
Change the reinterpret_cast to a static_cast.

[edit]
Woops, Seems you've tried static/dynamic cast...

Though it should work with a static cast.
[/edit]

[edit2]
Ah, add an std::ends after the stringstream input, ie:

(static_cast(std::stringstream() << args << std::ends)).str()

And do make sure it's a static cast.
[size=2]aliak.net
255
255
Unfortunately that didn't do it. According to the docs, std::ends is only needed with the deprecated strstream class, but I'm using stringstream.
I tried static_cast again just to be sure but it still prints the hex number, and so does dynamic_cast.
Sharlin
Sharlin
Hmm... the following works for me (as it should, it's a legal downcast after all), with gcc-3.3.5 and gcc-3.4.4, debian/x86:

#include <sstream>#include <iostream>#define mkstr(args) (static_cast<std::ostringstream &>(std::ostringstream() << args).str())int main(){  std::cerr << mkstr(1 << "foobar" << 2) << '\n';}


Edit: As do using dynamic_cast or std::stringstream, and different combinations thereof. dynamic_cast shouldn't be needed, though, as the cast should not be able to fail.
Ksero
Ksero
I'm on VC++.net2k3, and somehow, the first string was implicitly cast to a void * when it was inserted into the stream. For some reason, casting the ostringstream to an ostream worked...
#define makestr(args) (static_cast<std::ostringstream&>( static_cast<std::ostream &>(std::ostringstream()) << args)).str()std::cout << makestr( "A" << 4 << "foo");

Hmmm... that's a quite useful macro [smile]... Thanks! ;)
255
255
Quote:
Original post by Ksero
I'm on VC++.net2k3, and somehow, the first string was implicitly cast to a void * when it was inserted into the stream.


That's it! But why does it do that???

Unfortunately your version doesn't work on gcc 3.3.5. It won't convert a std::ostringstream to a std::ostream& (i.e. can't convert an instance to a reference).


Sharlin, try a test case where you send a string constant first instead of an integer. You'll probably face the same issue then.



EDIT: The macro expands to the following
std::cout << (static_cast<std::ostringstream&>(std::ostringstream() << "A" << 4)).str() << std::endl;

It still prints the first string's address. Why??
Sharlin
Sharlin
Quote:
Sharlin, try a test case where you send a string constant first instead of an integer. You'll probably face the same issue then.


Ah, indeed I do. Exceedingly weird.
Ksero
Ksero
Hmmm... this became a bit long... It seems to work if you first cast it to a (const std::ostream &) and then to a (std::ostream &). It works in gcc 3.3.1
#define makestr(args) (static_cast<std::ostringstream&>( const_cast<std::ostream &>(static_cast< const std::ostream & >( std::ostringstream() ))  << args)).str()cout << makestr( "A" << 4 << "foo");
255
255
Thanks! That did it.

I wonder if someone can give a good explanation on why things got converted to void* in the first place... oh well[rolleyes]
lucky_monkey
lucky_monkey
I'm curious about the use of the macro. I've never really looked hard at stringstreams, so I don't know why you'd use
std::cerr << makestr("A" << 2 << "B") << std::endl;
instread of
std::cerr << "A" << 2 << "B" << std::endl;
What's the reasoning behind the macro? (am I missing something obvious?)
Zahlman
Zahlman
Not as pretty to use (and not guaranteed, as it's off the top of my head), but not a macro:
// usage: makestr()(something)("something_else")(123)()// or (makestr() << something << "something_else" << 123)()class makestr : public std::stringstream {  template<typename T> makestr& operator() (const T& arg)(    return *this << arg;  }  const std::string& operator() () {    return this->str();  }}//test case:std::cerr << makestr()("A")(2)("B")() << std::endl;// I basically turned the << into )( :)
255
255
Quote:
Original post by lucky_monkey
What's the reasoning behind the macro? (am I missing something obvious?)

It's useful for instance when you want to convert numbers (int or real) to strings easily. It can be used wherever you would normally use sprintf (or create a stringstream manually), except now it's a one-liner. No setting up buffers for sprinf or creating (and destroying) an instance of stringstream.

I originally wanted it for statements like this:
throw SomeException(mkstr("Error doing something. (errno = " << errno << ")."));

lucky_monkey
lucky_monkey
Quote:
Original post by 255
Quote:
Original post by lucky_monkey
What's the reasoning behind the macro? (am I missing something obvious?)

It's useful for instance when you want to convert numbers (int or real) to strings easily. It can be used wherever you would normally use sprintf (or create a stringstream manually), except now it's a one-liner. No setting up buffers for sprinf or creating (and destroying) an instance of stringstream.

I originally wanted it for statements like this:
throw SomeException(mkstr("Error doing something. (errno = " << errno << ")."));
Ah, yeah I wasn't looking beyond the examples above (sending the stringstream straight to std::cout). My brain is fried from debugging [dead]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.