1

Currently I have a method that works as follows:

std::stringstream ss; ss << "lives:" << this->Lives;
Text->RenderText(ss.str(), font, x, y, scale,color);

Now this seems messy to me and I wanted to reduce it down to one line. But I cant seem to think of a way to do it cleanly.

I thought of using varidic functions, but that restricts me to one type and I have to specify the number of arguments.

Also though of using a std::initializer_list or varidic templates but it doesn't seem any nicer.

In this solution: Here An answer provided by Georg Fritzsche showed a posible solution using:

helper() << a << b << c;

But the actual implementation of it I'm.... not sure of.

Something akin to:

Text->render(font, x, y, scale,color) << "lives:" << this->Lives;

would be nice, but in the method I'm not sure how to define it.

I cant return a stringstream object, because I cant access it after the return.

So how does a method like this with chaining << work?

Steven Venham
  • 600
  • 1
  • 3
  • 18
  • `Text->RenderText("lives:" + std::to_string(this->Lives), font, x, y, scale,color);` ? – Jarod42 Jun 23 '17 at 18:42
  • @Jarod42 you can't `+` onto a `char const *`. You can use the ""s C++11 `operator""` to make it a std::string, though: ` "lives: "s + ... other stuff` – xaxxon Jun 23 '17 at 18:51
  • 1
    @xaxxon: You can if the right hand argument is a `std::string`, as it is in Jarod42's example. – Benjamin Lindley Jun 23 '17 at 23:37
  • @BenjaminLindley I swear I've gotten errors trying to do that in the past, but I just tried it and it, of course, works. Thanks. – xaxxon Jun 24 '17 at 00:18

2 Answers2

3

Return a temporary object that accumulates all the parts of your string, then when it is automatically destroyed at the end of the statement, have it render the content in its destructor.

#include <utility>
#include <string>
#include <sstream>
#include <iostream>
using namespace std;

class Renderer {
    stringstream sstream;
    float x;
    float y;
public:
    Renderer(float x, float y) : x(x), y(y){}
    template<class T>
    Renderer && operator<<(T&& t) {
        sstream << std::forward<T>(t);
        return std::move(*this);
    };
    ~Renderer() {
        // your real code replaces the line below..
        cout << sstream.str() << endl;
    }
};

Renderer render(float x, float y) {
    return Renderer(x, y);
}

int main() {
    int i = 5;
    render() << "foo" << i;
}

live: https://wandbox.org/permlink/UToweQELJ4jt0QYl

xaxxon
  • 19,189
  • 5
  • 50
  • 80
  • 4
    `Renderer::operator<<` might be a bit more useful as a template method: `template Renderer && operator<<(T&& x) { sstream << std::forward(x); return std::move(*this); };` – Daniel Schepler Jun 23 '17 at 18:51
  • Ah, ok so having a sort of intermediary class. Ill try that. – Steven Venham Jun 23 '17 at 18:58
  • 1
    Should probably add `#include ` to ensure `std::forward` is available. – Daniel Schepler Jun 23 '17 at 19:00
  • 1
    This makes me wonder, when does this object loose scope? and when is the deconstructor called? is it after the line is proccessed? or at the end of the used function? – Steven Venham Jun 23 '17 at 19:12
  • @StevenVenham it is at the end of the line (statement) unless it's extended: http://en.cppreference.com/w/cpp/language/lifetime - temporary object lifetime section This is very well defined for C++. – xaxxon Jun 23 '17 at 19:13
  • @xaxxon That overloaded operator is... Interesting. What one is it? It looks like a blend of the && operator and the Shift operator. Happen to have a Bit more information on that? Cause that syntax is funky. – Steven Venham Jul 10 '17 at 20:51
  • @StevenVenham && in this case is an rvalue reference, introduced in C++11 – xaxxon Jul 10 '17 at 21:53
  • @StevenVenham i highly recommend Effective Modern C++ as a great book for catching up on modern C++. – xaxxon Jul 10 '17 at 22:58
-1

How I'd deal with this

Okay, so you have this:

std::stringstream ss; ss << "lives:" << this->Lives;
Text->RenderText(ss.str(), font, x, y, scale,color);

Now if you really want to do it in a single line, why not put the string generation into a single function call, something specific like

std::string life_text(int lifecount){
   std::stringstream ss;
   ss<<"lives:"<<lifecount;
   return ss.str();
}

So you can call render like this:

Text->render(life_text(lives), x, y, scale,color);

What you're looking for

First, before I answer the question that you asked, the << operator does not imply method chaining. At least not in vanilla C++, in fact, I don't think it's used anywhere like that in c++.

The stream objects aren't really chaining a method, but calling something like

template<typename T> std::stringstream& operator<<(std::stringstream& rightside,T leftside){
    rightside.append(leftside);
    return rightside;
}

So what happens at each step of that is something like:

stringstream r;
r<<"lives:";
r<<this->lives;

What you are asking for isn't really all that simple. You would need to change the rendertext function to return a new sort of object that you can pass arguments to. That's hard.

Second of all, that would mean that your evaluation order would make this somewhat more challenging. There are ways around that, but I don't know if this is a situation where a simple convenience function like above wouldn't be better.

If you're dead set on doing this that way, then you might have to do something that is really potentially very problematic. You'd have to make the object call the actual render function(which I assume you probably have from some framework) at its disposal.

Fine, but now you've required adding some scope that might matter if you need this done in a specific order. It would look, potentially, like this:

{
    Text->render(x,y,scale,color)<<"lives:"<<this->Lives;
}

To my eyes, that looks frustrating.

I'll answer any questions you might have relating to my answer, if you'd like, but, at least to my eyes, your original goal seems like you're barking up the wrong tree with how you'd like to do this.

Something approaching a solution in the manner you wanted

template<std::function<typename ReturnType(std::string&,typename Ts...)> Func>
class caller{
std::stringstream r;
Ts... arguments;
Func storedFunction;
public:

caller(Func x,typename Ts...):storedFunction(x){
arguments=Ts;

}
~caller(){
Func(r.str(),...Ts);
}
friend template<typename S,typename P> caller<S>& operator<<(caller<S>&,T value);
};
template<typename S,typename P> caller<S>& operator<<(caller<S>& c, T value){
    c.r<<value;
}

caller a_suitable_name(std::bind(&Text::Render,&Text),_those_other_arguments); caller<<"lives:"<

This isn't likely to work in its present form, but I don't have time to finish the definition.

violet_white
  • 404
  • 2
  • 15
  • I told you that you don't understand what's being done with the `<<` operator. What you want to do isn't easily done, and would likely take a lot of time to make work properly. – violet_white Jun 23 '17 at 18:54
  • it took me about 3 minutes to write my answer. – xaxxon Jun 23 '17 at 18:54
  • It's more work than writing that function, isn't it? I'm just pointing out that a simple, short function like that might be more suited to the specific case here. I pointed out a problem in my answer, or do you not consider the ordering important to rendering? – violet_white Jun 23 '17 at 19:02
  • the "ordering" issue you're talking about.. are you saying that you think the temporary variable might not be destroyed at the right time? The variable will be destroyed at the end of the line unless its lifetime is explicitly extended. http://en.cppreference.com/w/cpp/language/lifetime – xaxxon Jun 23 '17 at 19:07
  • To be perfectly honest, I hadn't considered using a temporary variable. Though I disagree that I didn't answer the question, I did, even before trying that broken template code, I suggested doing it one way. Although I concede your points, would you please refrain from using the scare quotes in that fashion. – violet_white Jun 23 '17 at 19:23