How do you create and use an ostringstream in an initialisation list?

A

Adam Nielsen

Hi everyone,

I'm trying to work out how to construct a temporary object in a class'
initialisation list and then call a function on it, in order to pass the
result to a base class' constructor.

I'm not sure whether the object is being constructed correctly, but as I
can't call the str() function I'm guessing not.

Could someone please have a look at the example code below and let me
know what I'm doing wrong?

Thanks,
Adam.


#include <iostream>
#include <sstream>

struct PrintString
{
PrintString(std::string s)
{
std::cout << s << std::endl;
}
};

struct PrintNumber: public PrintString
{
PrintNumber(int iNumber)
: PrintString(
// How should this be done?
(std::eek:stringstream() << "The number is " << iNumber).str()
)
{
}

};

int main(void)
{
PrintNumber(5);
return 0;
}
 
S

svobodamo

I'm not sure whether the object is being constructed correctly, but as I
can't call the str() function I'm guessing not.

it seems, that the reason is that std library's operator<< (..., int)
does return some basic_ostream, instead of the type you supplied.
Could someone please have a look at the example code below and let me
know what I'm doing wrong?

well the easiest way is to not to do it at all :)
consider for example this:

template<typename T>
std::string to_string(T t)
{
std::stringstream s;
s << t;
return s.str();
}

struct PrintNumber: public PrintString
{
PrintNumber(int iNumber) : PrintString(std::string("The number is")
+ to_string(iNumber)) { }
};

i think it's even more readable

m.
 
T

tony_in_da_uk

// How should this be done?
(std::eek:stringstream() << "The number is " << iNumber).str()

The issue is that operator<< returns a std::eek:stream&, which lacks the
str() member function. Just cast back to ostringstream before calling
str().

Cheers,

Tony
 
K

Kai-Uwe Bux

Adam said:
Hi everyone,

I'm trying to work out how to construct a temporary object in a class'
initialisation list and then call a function on it, in order to pass the
result to a base class' constructor.

I'm not sure whether the object is being constructed correctly, but as I
can't call the str() function I'm guessing not.

Could someone please have a look at the example code below and let me
know what I'm doing wrong?

Two things.

Thanks,
Adam.


#include <iostream>
#include <sstream>

struct PrintString
{
PrintString(std::string s)
{
std::cout << s << std::endl;
}
};

struct PrintNumber: public PrintString
{
PrintNumber(int iNumber)
: PrintString(
// How should this be done?
(std::eek:stringstream() << "The number is " << iNumber).str()

a) The streams are designed using polymorphism. Therefore, operator<< takes
and returns references to basic_ostream. Thus, the static type of the
expression

(std::eek:stringstream() << "The number is " << iNumber)

is not std::eek:stringstream& but std::basic_ostream<char>&. However, a
basic_ostream has no str() member.

Now, you really did create an ostringstream temporary. So there is no
probleam casting this thing back

static_cast<std::eek:stringstream&>
(std::eek:stringstream() << "The number is " << iNumber)
.str()

b) But this will _not work_ properly because of the second problem. The
compiler cannot treat the expression

std::eek:stringstream() << "The number is "

as

operator<< ( std::eek:stringstream(), "The number is " )

because that would require binding a temporary to a non-const reference.
Instead, the compiler chooses the interpretation

std::eek:stringstream().operator<< ( "The number is " );

which, in turn, will match the

operator<< ( void* );

member since there is no

operator<< ( char* );

member.

Thus, you would need to trick the compiler a little bit:


static_cast<std::eek:stringstream&>
(std::eek:stringstream() << std::dec << "The number is " << iNumber)
.str()



c) Clearly, this is way too tricky to put it into your code. You'd end up
writing a page of comments for the maintenance programmer if you did this.

You may want to have a look at boost::lexical_cast, which will convert just
about anything into a string.
)
{
}

};

int main(void)
{
PrintNumber(5);
return 0;
}


BTW: what problem is this strange hierarchy of Print-classes supposed to
solve? It looks as though you chose a very very roundabout way to print
some numbers. Why?


Best

Kai-Uwe Bux
 
J

James Kanze

Adam Nielsen wrote:

[...]
[...]
Thus, you would need to trick the compiler a little bit:

static_cast<std::eek:stringstream&>
(std::eek:stringstream() << std::dec << "The number is " << iNumber)
.str()
c) Clearly, this is way too tricky to put it into your code. You'd end up
writing a page of comments for the maintenance programmer if you did this.

On the other hand, if you wrap it into a function (a static
member function, if you prefer), it can be made readable:

class PrintNumber : public PrintString
{
private:
static std::string initBase( int i )
{
std::eek:stringstream s ;
s << i ;
return s.str() ;
}

public:
explicit PrintNumber( int i )
: PrintString( initBase( i ) )
{
}
} ;

I use similar functions (either in anonymous namespace, or as
static members) in initialization lists quite often.

[...]
BTW: what problem is this strange hierarchy of Print-classes supposed to
solve? It looks as though you chose a very very roundabout way to print
some numbers. Why?

I was wondering about that myself.
 
A

Adam Nielsen

Thanks everyone for all your replies - that really explained what was
going on (I was wondering where those basic_ostream error messages were
coming from!)

Agreed. I think I'll stick with the static/global function to do the
processing.
I was wondering about that myself.

It was just an example to demonstrate the problem. It has cropped up
twice in my code - once when I have a base exception class that takes an
error message, and then that's inherited by another class that handles
database errors - but since I want to include various additional info in
the error message that gets logged (e.g. database IDs) I have to cram
all these into a string to pass it on to the base class.

The second time it came up someone may have some advice (even if it is a
bit off topic, as it's probably compiler specific.) To assist with
debugging I want to pass the source filename and the line number to the
object constructor when throwing an exception, as these particular
exceptions should only be thrown when there's a coding error (e.g.
trying to read an integer out of the database into a string.) Knowing
where the exception was thrown from without having to run through a
debugger will make tracking down the problem that much easier
(especially in the hopefully unlikely event that it gets thrown when the
system is in production.)

I can use __FILE__ to pass the current filename in as a const char *,
but using __LINE__ passes the line number as an integer (hence the need
for an ostringstream to convert it to a string.) Using
__STRING(__LINE__) doesn't work (it expands to "__LINE__"), so using
string streams was the only way of doing this that came to mind.

Anyway, thanks again for all your help!

Cheers,
Adam.
 
J

James Kanze

On Oct 12, 11:52 am, Adam Nielsen <[email protected]>
wrote:

[...]
It was just an example to demonstrate the problem. It has cropped up
twice in my code - once when I have a base exception class that takes an
error message, and then that's inherited by another class that handles
database errors - but since I want to include various additional info in
the error message that gets logged (e.g. database IDs) I have to cram
all these into a string to pass it on to the base class.

Great minds think alike---that's the exact case where I use the
static function to generate the string. (My exceptions aren't
from a database, but the exception constructor does need to
format a string from various other information, and use it to
initialize the base std::runtime_error class.)
The second time it came up someone may have some advice (even
if it is a bit off topic, as it's probably compiler specific.)
To assist with debugging I want to pass the source filename
and the line number to the object constructor when throwing an
exception, as these particular exceptions should only be
thrown when there's a coding error (e.g. trying to read an
integer out of the database into a string.)

That's an interesting example. Traditionally, I'd say that
coding errors should be handled via assert, rather than an
exception. But I think you've found an exception to that
rule---the coding error could also be in the data base schema,
and you probably don't want to bring the application down
because of that.
Knowing
where the exception was thrown from without having to run through a
debugger will make tracking down the problem that much easier
(especially in the hopefully unlikely event that it gets thrown when the
system is in production.)
I can use __FILE__ to pass the current filename in as a const char *,
but using __LINE__ passes the line number as an integer (hence the need
for an ostringstream to convert it to a string.) Using
__STRING(__LINE__) doesn't work (it expands to "__LINE__"), so using
string streams was the only way of doing this that came to mind.

You can use a double indirection in the macros:

#define S1( s ) # s
#define S( s ) S1( s )
#define HERE __FILE__ ":" S( __LINE__ )

// ...
std::cout << HERE << std::endl ;

But I still find the ostringstream more flexible with regards to
its formatting possibilities.
 
O

Old Wolf

I'm trying to work out how to construct a temporary object in a class'
initialisation list and then call a function on it, in order to pass the
result to a base class' constructor.
struct PrintNumber: public PrintString
{
PrintNumber(int iNumber)
: PrintString(
// How should this be done?
(std::eek:stringstream() << "The number is " << iNumber).str()
)
{
}
};

I would write:

struct PrintNumber: public PrintString
{
PrintNumber(int iNumber) : PrintString( convert_number(iNumber) ) {}

private:
std::string convert_number(int n)
{
std::eek:stringstream oss;
oss << "The number is " << n;
return oss.str();
}
};
 
J

James Kanze

On Oct 12, 3:22 pm, Adam Nielsen <[email protected]>
wrote:
I would write:
struct PrintNumber: public PrintString
{
PrintNumber(int iNumber) : PrintString( convert_number(iNumber) ) {}
private:
std::string convert_number(int n)

I'm not sure that it's absolutely necessary, but I'd make this
function static. If the constructors that use it are all in a
single file, I'd put it in an anonymous namespace in the file,
rather than making it a member. And in the very particular case
here, there is some argument for making it a static protected
template member of the base class (but that's not usually
applicable).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top