stringstream as a func param question

closed account (iT0Gz8AR)
Hi everyone!

A have a quick question about passig miltiple variable to a func that has
(stringstream & text). Here is the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
void PrintText(stringstream & txt) {
    cout << txt.str() << endl;
}
 
void main() {
    // Works !!!
    stringstream t;
    t << "Doom " << 3 << " is Awesome" << endl;
    PrintText(t);
 
    // Doesn not work ??
    PrintText("Doom " << 3 << " is Awesome" << endl);
}


Any idea how to make second one work?
Thanks in advance.

Juris.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <utility>
#include <string>

template< typename T >
void print_text( T&& v ) { std::cout << v ; }

template< typename FIRST, typename... REST >
void print_text( FIRST&& first, REST&&... rest )
{
    print_text( std::forward<FIRST>(first) ) ;
    print_text( std::forward<REST>(rest)... ) ;
}

int main()
{
    const std::string program = "Doom" ;
    int version = 3 ;

    print_text( program , ' ', version , " is Awesome\n" );
    print_text( program , ' ', version , " is Awesome" , '\n' );

    auto endl = std::endl< char, std::char_traits<char> > ;
    print_text( program , ' ', version , " is Awesome" , endl ) ;
}

http://coliru.stacked-crooked.com/a/efe36a03c43a1ec0
closed account (iT0Gz8AR)
Thanks a lot.

Got it working with Templates. Any suggestions on implementing similar functionality without using Templates though?

Thanks a lot.
Juris.
Why do you need this print_text() function?

Can't you just write: std::cout << program << ' ' << version << " is Awesome\n" ; instead?
closed account (iT0Gz8AR)
I am coding BitMap Font rendering for my little Engine.

I need a func, that will take text + variables and convert it to a OpenGL Quad.

Something like that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class qbFont ...
void SetText(stringstream & text) {
    string tempText = text.str();

    /* 
        go through every symbol in this string
        and pick a texture of a letter/number and add to a quad
    */
}

qbFont * PlayerScore = new qbFont();

...

// Need to pass like that (this does not work)
PlayerScore->SetText("Score: " << Player->Score << " Points!");

...

// Rendering a Quad with text generated in SetText()
qbRenderer->DrawSprite(PlayerScore);

...

All of the code is done, just need to pass multiple strings and variables to my func SetText() to pick textures from.

I have just tested Template C++11 code on Windows, Linux and Android and it runs just fine. As I have never used Templates, I am concerned about performance on mobiles. So I was looking for a simpler solution for now.

Juris.
Last edited on
> As I have never used Templates, I am concerned about performance on mobiles.
> So I was looking for a simpler solution for now.

Templates are a pure compile-time mechanism; the recursion is done at compile-time. There is no run-time overhead for using templates.


This would probably give you the best set_text() performance:

a. Append std::string, c-style strings and characters directly.
b. Stringify numbers with std::to_string()
c. Use string stream for stringification only if a. or b. does not apply.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>
#include <string>
#include <type_traits>
#include <sstream>
#include <complex>

namespace detail
{
    // if it is a std::string, a c-style string or a character, append directly
    void append_text( std::string& str, const std::string& v ) { str += v ; }
    void append_text( std::string& str, const char* v ) { str += v ; }
    void append_text( std::string& str, char v ) { str += v ; }
    void append_text( std::string& str, signed char v ) { str += char(v) ; }
    void append_text( std::string& str, unsigned char v ) { str += char(v) ; }

    template < typename T > struct use_std_to_string : std::false_type {};
    template <> struct use_std_to_string<short> : std::true_type {};
    template <> struct use_std_to_string<unsigned short> : std::true_type {};
    template <> struct use_std_to_string<int> : std::true_type {};
    template <> struct use_std_to_string<unsigned int> : std::true_type {};
    template <> struct use_std_to_string<long> : std::true_type {};
    template <> struct use_std_to_string<unsigned long> : std::true_type {};
    template <> struct use_std_to_string<long long> : std::true_type {};
    template <> struct use_std_to_string<unsigned long long> : std::true_type {};
    template <> struct use_std_to_string<float> : std::true_type {};
    template <> struct use_std_to_string<double> : std::true_type {};
    template <> struct use_std_to_string<long double> : std::true_type {};

    // if std::to_string() can be used, use it for stringification, append
    template< typename T >
    typename std::enable_if< use_std_to_string< typename std::decay<T>::type >::value, void >::type
    append_text( std::string& str, const T& v ) { str += std::to_string(v) ; }

    // if std::to_string() can't be used, append via a stringstream
    template< typename T >
    typename std::enable_if< !use_std_to_string< typename std::decay<T>::type >::value, void >::type
    append_text( std::string& str, const T& v )
    { std::ostringstream stm( str, std::ios::app ) ; stm << v ; str = stm.str() ; }
    
    template< typename FIRST, typename... REST >
    void append_text( std::string& str, const FIRST& first, const REST&... rest )
    {
        append_text( str, first ) ;
        append_text( str, rest...) ;
    }
}

struct qbFont
{
    template < typename... ARGS > void set_text( const ARGS&... args )
    {
        std::string tempText ;
        detail::append_text( tempText, args... ) ;
        std::cout << "qbFont::set_text::tempText is: '" << tempText << "'\n" ;
        /*
            go through every symbol in this string
            and pick a texture of a letter/number and add to a quad
        */
    }
};

int main()
{
    const int score = 762 ;
    const std::complex<double> cmplx( 1.2, 3.4 ) ;

    qbFont font ;
    font.set_text( "Score: " , score , " Points!" ) ;
    font.set_text( "test: " , cmplx , " (no std::to_string)", " done", '!' ) ;
}

http://coliru.stacked-crooked.com/a/86c7d4eff2161062
Remember that you can only use operators on the object.

stringstream.operator<<() is a member (or external definition from what I'm learning) that applies to a stringstream. This means that you need an object to perform this operation on, and that if you don't provide one it can't work.
closed account (iT0Gz8AR)
Yes. I can see why Templates work and why:

PrintText("Doom " << 3 << " is Awesome");

does not.

I know that I have to do 3 lines of cde to avoid using Templates:

1
2
3
stringstream Text;
Text << "Doom " << 3 << " is Awesome";
PrintText(Text);


I wonder how
cout << "Doom " << 3 << " is Awesome";
works.

Looks like a variable but it is a function too
as it accepts stuff and prints it out at the same time. You think COUT
is based on templates? Not sure if templates were implemented in very first
C++ standard when COUT was already there.

Juris.
If a function returns a reference to an object, we can use the result to call another function on the object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <cstdio>

struct A
{
    A& foo( char c ) { std::printf( "%c", c ) ; return *this ; }
    A& foo( int v ) { std::printf( "%d", v ) ; return *this ; }
    A& foo( double d ) { std::printf( "%.2f", d ) ; return *this ; }
    A& foo( const char* cstr ) { std::printf( "%s", cstr ) ; return *this ; }
};

int main()
{
    A cout ;

    A& r1 = cout.foo( "the values are: " ) ; // A::foo returns reference to cout
    A& r2 = r1.foo(55) ; // on the result r1 (reference to cout), call foo once again
    A& r3 = r2.foo( " and " ) ;
    A& r4 = r3.foo(34.56) ;
    r4.foo('\n') ;

    // use the references returned anonymously
    cout.foo( "the values are: " ).foo(55).foo( " and " ).foo(34.56).foo('\n') ;
}

http://coliru.stacked-crooked.com/a/a1bbdb3819c975ab

Now replace the function foo with an overloaded << operator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <cstdio>

struct A
{
    A& operator<< ( char c ) { std::printf( "%c", c ) ; return *this ; }
    A& operator<< ( int v ) { std::printf( "%d", v ) ; return *this ; }
    A& operator<< ( double d ) { std::printf( "%.2f", d ) ; return *this ; }
    A& operator<< ( const char* cstr ) { std::printf( "%s", cstr ) ; return *this ; }
};

int main()
{
    A cout ;

    A& r1 = cout.operator<< ( "the values are: " ) ;
    A& r2 = r1.operator<< (55) ;
    A& r3 = r2.operator<< ( " and " ) ;
    A& r4 = r3.operator<< (34.56) ;
    r4.operator<< ('\n') ;

    // using the references returned anonymously
    cout.operator<< ( "the values are: " ).operator<< (55).operator<< ( " and " ).
                       operator<< (34.56).operator<< ('\n') ;

    // or equvalently, using the operator:
    A& r5 = cout << "the values are: " ;
    A& r6 = r5 << 55 ;
    A& r7 = r6 << " and " ;
    A& r8 = r7 << 34.56  ;
    r8 << '\n' ;

    // using the references returned anonymously
    cout << "the values are: " << 55 << " and " << 34.56 << '\n' ;
}

http://coliru.stacked-crooked.com/a/1bd68141570b7afd
Qb coder:

What borges posted is called overloading functions and operators. Cout is a class, but you can overload functions and operators in a class just like you can outside a class.

I feel like you might not understand what overloading is, so I will explain:

Overloading a function or operator simply means that you can write two functions with the same alias, but different arguments. When the compiler comes accross a call to that function, it will choose the best one.

Example:

1
2
char foo(const char&); //prototype1
int foo(const int&); //prototype2 


1
2
3
int i = foo(10); //compiler will choose the later of the two

char ch = foo('a'); //the compiler wil choose the former. 


My personal preference for passing arguments to functions, for the sake of consistency, involves passing the address of each variable (const if it wont be modified), and overloading those functions if I want them to be able to take different arguments. For example, I might want to pass no arguments to a function, so I would overload that function with no arguments, and the overloaded function would call the base function with whatever I want the defaults to be.

Hope this helps. Post any questions you may have!! :)
Last edited on
closed account (iT0Gz8AR)
Thanks to all of you. You are an amazing help here guys! Everything you posted makes clear sense.

Thanks.
Juris - qbCoder
> My personal preference for passing arguments to functions, for the sake of consistency,
> involves passing the address of each variable (const if it wont be modified)

My personal preference is for referential transparency. Always pass by value, and return by value, unless there is a compelling reason not to.
Topic archived. No new replies allowed.