Function that prints something to std::ostream and returns std::ostream?
Asked Answered
L

9

13

I want to write a function that outputs something to a ostream that's passed in, and return the stream, like this:

std::ostream& MyPrint(int val, std::ostream* out) {
  *out << val;
  return *out;
}

int main(int argc, char** argv){
    std::cout << "Value: " << MyPrint(12, &std::cout) << std::endl;
    return 0;
}

It would be convenient to print the value like this and embed the function call in the output operator chain, like I did in main().

It doesn't work, however, and prints this:

$ ./a.out
12Value: 0x6013a8

The desired output would be this:

Value: 12

How can I fix this? Do I have to define an operator<< instead?

UPDATE: Clarified what the desired output would be.

UPDATE2: Some people didn't understand why I would print a number like that, using a function instead of printing it directly. This is a simplified example, and in reality the function prints a complex object rather than an int.

Leishaleishmania answered 6/7, 2009 at 16:28 Comment(7)
Why are you passing the stream via a pointer rather than via areference?Diaphone
Maybe he's read the Google style guide, and bought into this "don't use non-const reference parameters" fashion that's going around.Zinciferous
Also, what's the point of not doing cout << "Value: " << 12; like @Neil recommends? That seems to be the most straight forward solution.Aristocratic
@Neil and @onebyone: Yes, I started using the google style conventions.Leishaleishmania
@litb: Just printing '12' was just a toy example. In reality, I don't pass an int, but a more complicated data structure that has to be printed to the stream.Leishaleishmania
@dehmann Please stop using the google conventions. If you need reasons, there was a big discussion about this on the comp.lang.c++.moderated usenet newsgroup.Diaphone
Re: UPDATE2 -- To print this complex object of yours, why not just define a new overloaded std::operator<< for that type, just like every other normal class uses?Axiom
Z
15

You can't fix the function. Nothing in the spec requires a compiler to evaluate a function call in an expression in any particular order with respect to some unrelated operator in the same expression. So without changing the calling code, you can't make MyPrint() evaluate after std::cout << "Value: "

Left-to-right order is mandated for expressions consisting of multiple consecutive << operators, so that will work. The point of operator<< returning the stream is that when operators are chained, the LHS of each one is supplied by the evaluation of the operator to its left.

You can't achieve the same thing with free function calls because they don't have a LHS. MyPrint() returns an object equal to std::cout, and so does std::cout << "Value: ", so you're effectively doing std::cout << std::cout, which is printing that hex value.

Since the desired output is:

Value: 12

the "right" thing to do is indeed to override operator<<. This frequently means you need to either make it a friend, or do this:

class WhateverItIsYouReallyWantToPrint {
    public:
    void print(ostream &out) const {
        // do whatever
    }
};

ostream &operator<<(ostream &out, const WhateverItIsYouReallyWantToPrint &obj) {
    obj.print(out);
}

If overriding operator<< for your class isn't appropriate, for example because there are multiple formats that you might want to print, and you want to write a different function for each one, then you should either give up on the idea of operator chaining and just call the function, or else write multiple classes that take your object as a constructor parameter, each with different operator overloads.

Zinciferous answered 6/7, 2009 at 16:48 Comment(3)
I know I always find it easier to break streams down into traditional function calls when I can't get my head around something so I thought I'd add this for the OP in case it helps them too. Hope you don't mind. I think I'm right in saying the std::cout line basically boils down to cout.operator<<( "Value: " ).operator<<( MyPrint(12, &std::cout ) ).operator<<( std::endl ); which might be an easier way of visualising what this answer says.Fayth
I updated the question; it says now that the desired output would be Value: 12.Leishaleishmania
@SteveJessop You've probably forgotten to put return statement at the end of operator<< implementation: return out;.Delao
B
6

You want to make MyPrint a class with friend operator<<:

class MyPrint
{
public:
    MyPrint(int val) : val_(val) {}
    friend std::ostream& operator<<(std::ostream& os, const MyPrint& mp) 
    {
        os << mp.val_;
        return os;
    }
private:
    int val_;
};

int main(int argc, char** argv)
{
    std::cout << "Value: " << MyPrint(12) << std::endl;
    return 0;
}

This method requires you to insert the MyPrint object into the stream of your choice. If you REALLY need the ability to change which stream is active, you can do this:

class MyPrint
{
public:
    MyPrint(int val, std::ostream& os) : val_(val), os_(os) {}
    friend std::ostream& operator<<(std::ostream& dummy, const MyPrint& mp) 
    {
        mp.os_ << mp.val_;
        return os_;
    }
private:
    int val_;
    std::ostream& os_
};

int main(int argc, char** argv)
{
    std::cout << "Value: " << MyPrint(12, std::cout) << std::endl;
    return 0;
}
Bolter answered 6/7, 2009 at 16:35 Comment(11)
Except that this does not do what was asked for.Diaphone
Well, to start with, it doesn't compile.Diaphone
Oops. Bad copy/paste. Thanks for catching.Bolter
It doesn't have to be a friend; it only uses public methods of the std::ostream.Alvy
Make it a template class and add a template function to create it and its perfect.Hu
@Max Lybbert: It does access a private member variable?Leishaleishmania
This is exactly how I would have answered this questionFlannelette
Sorry, "it only uses public methods of the std::ostream, so it doesn't need to be a friend of the std::ostream; since it's a method of MyPrint it already has access to private methods and variables of MyPrint and doesn't need to be a friend of MyPrint either (plus a class can't be a friend of itself)."Alvy
@Max Lybbert. 1) What do you mean by "method"? 2) The function operator<< in his code is not a member of the class. Removing the friend keyword would make it a member, but would make the code invalid, because a member operator<< must have one parameter only. It's called a "friend definition".Aristocratic
I've put some explanation here: #992018Aristocratic
Thanks for the correction. As you can see from my answer, I've generally defined these as non-member functions.Alvy
A
4

You have two options. The first, using what you already have is:

std::cout << "Value: ";
MyPrint(12, &std::cout);
std::cout << std::endl;

The other, which is more C++-like, is to replace MyPrint() with the appropriate std::ostream& operator<<. There's already one for int, so I'll do one just a tad more complex:

#include <iostream>

struct X {
    int y;
};

// I'm not bothering passing X as a reference, because it's a
// small object
std::ostream& operator<<(std::ostream& os, const X x)
{
    return os << x.y;
}

int main()
{
    X x;
    x.y = 5;
    std::cout << x << std::endl;
}
Alvy answered 6/7, 2009 at 17:43 Comment(0)
W
1

There's no way to do what you're expecting there because of the order the functions are evaluated in.

Is there any particular reason you need to write directly to the ostream like that? If not, just have MyPrint return a string. If you want to use a stream inside MyPrint to generate the output, just use a strstream and return the result.

Williamson answered 6/7, 2009 at 17:13 Comment(0)
A
0

First, there is no reason not to pass in the ostream by reference rather than by a pointer:

std::ostream& MyPrint(int val, std::ostream& out) {
  out << val;
  return out;
}

If you really don't want to use std::ostream& operator<<(std::ostream& os, TYPE), you can do this:

int main(int argc, char** argv){
    std::cout << "Value: ";
    MyPrint(12, std::cout) << std::endl;
    return 0;
}
Axiom answered 6/7, 2009 at 16:38 Comment(5)
"there is no reason not to pass in the ostream by reference". Some folks don't like non-const reference parameters. I disagree with them, but I wouldn't say there is "no reason" for their preference.Zinciferous
I don't see how a non-const pointer would be preferable to a non-const reference in any sane coding standard.Soilasoilage
Google says "References can be confusing, as they have value syntax but pointer semantics" (google-styleguide.googlecode.com/svn/trunk/…). I think they mean that pass-by-value and pass-by-reference look the same at the call site, so they think code is more readable if readers can assume that anything which looks like a pass-by-value, leaves the value unmodified. And as the saying goes, Google is insane like a fox. It does have some slightly old-fashioned attitudes to C++, but I think not irrational.Zinciferous
The standard operator<< notation takes the std::ostream by reference as an input; since this guy is attempting to emulate ostream's functionality, it would be better to match the syntax rather than passing a reference. Plus, he's returning a reference to the dereferenced pointer! If you used a pointer, you would have to call something like MyPrint(12, &(std::cout << "Value:") ); which is just awkward. You're passing a pointer to a temporary object.Axiom
"The standard operator<< notation takes the std::ostream by reference as an input". Yes, Google notes "necessary for some applications" as a "Con" of their "no non-const reference" rule. This is among the reasons I disagree with the rule: it's a pretty killer "Con" :-)Zinciferous
A
0

After changing the pointer to a reference, you can do this:

#include <iostream>

std::ostream& MyPrint(int val, std::ostream& out) {
    out << val;
    return out;
}

int main(int, char**) {
    MyPrint(11, std::cout << "Value: ") << std::endl; 

    return 0;
}

The syntax for MyPrint is essentially that of an unrolled operator<< but with an extra argument.

Axiom answered 6/7, 2009 at 17:55 Comment(0)
W
0

The problem can be solved this way:

my_answer

Wretched answered 28/7, 2023 at 13:14 Comment(2)
meta.https://mcmap.net/q/12237/-windows-cmd-pipe-not-unicode-even-with-u-switchHamford
While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From ReviewTweedsmuir
Q
0

Since C++11, chaining a function returning a reference to an ostream with another ostream is ill-formed. For e.g., see this answer.

The reason is because since C++11, there is no implicit conversion of an ostream to a void *.

Quartan answered 18/9, 2023 at 10:26 Comment(0)
D
-1

In your case the answer is obviously:

 std::cout << "Value: " << 12 << std::endl;

If that isn't good enough, please explain what output you want to see.

Diaphone answered 6/7, 2009 at 16:44 Comment(4)
The example is obviously simplified, and shows the user wants to understand how to chain stream operations.Hu
Unlike many people round here, I don't have the happy facility of understanding the "obvious" unasked question - I like to have things spelled out.Diaphone
I think you should chage your icon to a grumpy faced starfish :-(Hu
If you can find me one, I'll happily use it.Diaphone

© 2022 - 2024 — McMap. All rights reserved.