1

I have a class "stampstream" that is essentially intended to work like 'cout'. The two functions that follow are outside the class. This is the part that works.

class stampstream: public std::ostream
{
    //code
    stampstream& operator<<(stampstream& (*x)(void))
    {
        //code
    }
    //code
};

stampstream& endr()
{
    //nocode
}

stampstream& (*endrow)(void)=endr;

In main:

stampstream s;
s << "teststring1" << endrow;

Note that "endrow" is essentially 'endl'. This part works perfectly fine, compiles and executes with the right output. Now, I'm trying to overload << with another possible row(int) function. This is the part that doesn't work. Again, it's part of the same class as above and the following 2 functions are outside the class.

class stampstream: public std::ostream
{
    //code
    stampstream& operator<<(stampstream& (*r)(int y))
    {
        //code
    }
    //code
};

stampstream& ro(int y)
{
    //nocode
}

stampstream& (*row)(int)=ro;

In main:

s << "teststring2" << row(5);

This is the error I get:

error: invalid user-defined conversion from ‘stampstream’ to ‘stampstream& (*)(int)’ [-fpermissive]

What am I doing wrong with row ?

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
Prasad
  • 67
  • 2
  • 12
  • Hint: What is the type of `row(5)`? – cHao Oct 29 '14 at 02:10
  • row is of type stampstream with int parameter – Prasad Oct 29 '14 at 02:12
  • And *when you call it*, as you do when you say `row(5)`, what does it return? A `stampstream`, not a pointer to a function. – cHao Oct 29 '14 at 02:13
  • It doesn't return anything, but neither does endrow and that works perfectly fine. Yeah, it's supposed to be a stampstream type return. – Prasad Oct 29 '14 at 02:15
  • @Prasad: Actually, `s << endrow();` doesn't work. See what's wrong yet? – Ben Voigt Oct 29 '14 at 02:16
  • You're not *calling* `endrow`. That's why it works. – cHao Oct 29 '14 at 02:16
  • I have no operations to perform in the endrow (endr) function itself. Hence the //nocode. Operations are performed inside the class under the overloading. The reason endrow is there is because without it the compiler tells me endrow was not declared in this scope – Prasad Oct 29 '14 at 02:18
  • `s << endrow` works, `s << endrow()` fails, `s << row(5)` fails. Do you see any pattern? Have you tried `s << row` ? – Ben Voigt Oct 29 '14 at 02:19
  • @BenVoigt, yeah I do. I'm just not clear on where the format conversion is happening and faulty. – Prasad Oct 29 '14 at 02:20
  • Do you know what the `()` means? – Ben Voigt Oct 29 '14 at 02:21
  • It's a parameter list for a function... – Prasad Oct 29 '14 at 02:21
  • 1
    It's the "function call operator". It appears to the right of a function or pointer-to-function, **calls it immediately**, and evaluates to the result (return) of the function call. If you want `s << row(5)` to work, then `row(5)` needs to return something that `operator<<` can accept. – Ben Voigt Oct 29 '14 at 02:22
  • There's a certain computation that needs to happen inside the class where the row function is accepted by overloading <<. If the row function (outside the class) is of type stampstream, what should I be returning, since I can't return *this ? – Prasad Oct 29 '14 at 02:28
  • @Prasad: You're starting to stumble upon the reason function objects exist. :P – cHao Oct 29 '14 at 02:30
  • @cHao I'm unsure how to proceed at this point. This is the last obstacle I'm facing in this project. – Prasad Oct 29 '14 at 02:43

2 Answers2

3

The problem is that in

s << "teststring2" << row(5);

function call has higher precedence than <<, so this calls the function row points at and then tries to pass its return value (a stampstream &) to operator<<.

What you want is

s << "teststring2" << row;

This doesn't call the function and instead passes the pointer to operator<<. Inside that function, when you want to call x, you'll need to provide the argument.

It sounds like what you are trying to do is to have a function row that creates a functor object that you can then pass to operator<<. You can do that in C++11 with a lambda:

class stampstream: public std::ostream
{
    //code
    stampstream& operator<<(std::function<stampstream&(void)> r)
    {
        //code
    }
    //code
};

std::function<stampstream&(void)> row(int x)
{
    return [x]()->stampstream & {
        //code
    }
}

Which you can call as:

s << "teststring2" << row(5);

The call to row happens first, which constructs a lambda and returns it (but doesn't yet run the code in it). The functor is passed to operator<< and when that function calls r, it will call the code in the lambda (which can refer to the x param, which was bound by value, so copied into the lambda.)

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • I got a bunch of errors, but mainly this brings me back to: error: ‘row’ was not declared in this scope... since I'd assume it doesn't have the stampstream type. – Prasad Oct 29 '14 at 02:37
  • 'a bunch of errors' probably indicates your compiler doesn't support C++11 – Chris Dodd Oct 29 '14 at 02:43
  • I'm using the g++ compiler. I tried g++ -std=c++11... This is what I'm getting: error: declaration of ‘operator<<’ as non-function stampstream& operator<<(std::function r). error: expected ‘;’ at end of member declaration. error: expected ‘)’ before ‘<’ token. error: ‘function’ in namespace ‘std’ does not name a template type std::function row(int x). stampstream.cpp: In function ‘int main()’: error: ‘row’ was not declared in this scope s << "teststring2" << row(5). – Prasad Oct 29 '14 at 02:48
  • @Prasad You also need to `#include `. – David G Oct 29 '14 at 03:03
  • Thank you! It compiles.. Now I'm wondering what I'm supposed to have after return [x]()->stampstream & {... what I'm trying to do requires a private class member data. Also how do I use the parameter 5 passed into row 5 inside the class under the overloaded << ? Wouldn't the parameter need to be passed all the way to the operator overloader? – Prasad Oct 29 '14 at 03:08
  • @Prasad Because you want to access data members from the `row` function, this is not how it should be done. Instead follow the code in my answer but make this change: Change all `std::ostream&` to `stampstream&`. Now in the `<<` function your will be able to access data members through the `os` parameter. – David G Oct 29 '14 at 03:22
  • @0x499602D2 I managed to use this solution by keeping a global variable that helps me exchange the number I need to pass from function to class. – Prasad Oct 29 '14 at 04:18
2

Since Chris has already answered your question, I wanted to advise you of alternatives to your current method:

  1. Your stampstream class should use a specialized std::streambuf that writes out the stamp inside overflow(). There's no need to extend an std::ostream and reimplement the input operators. For an example of how to do this, see this thread.

  2. You seem to be using endrow and row like manipulators. Operator overloads already exist for manipulators that do not take arguments at the call site. You can have endrow take and return an object of type std::ostream&. For row you can have it return a class that overloads the inserter to allow the syntax you want:

    struct row_impl
    {
        row_impl(int row) : row(row) { }
        // Write out the row and return the stream
        friend std::ostream& operator<<(std::ostream& os, row_impl const& rimpl);
    private:
        int row;
    };
    
    std::ostream& operator<<(std::ostream& os, row_impl const& rimpl)
    {
        // This is where the code for your original row() function goes
    }
    
    // write out to and return the stampstream instance
    std::ostream& endrow(std::ostream& os);
    
    row_impl row(int row)
    {
        return row_impl(row);
    }
    

    Now s << endrow and s << row(5) will work without having to add anything to your stampstream class.

Community
  • 1
  • 1
David G
  • 94,763
  • 41
  • 167
  • 253