Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::function as a custom stream manipulator

I'm trying to use C++11 features to make custom stream manipulators easier to create. I can use lambda functions as manipulators, but not std::function<ostream&(ostream&)>.

Here's the code, boiled down:

#include <iostream>
#include <functional>
using namespace std;

auto lambdaManip = [] (ostream& stream) -> ostream& {
    stream << "Hello world" << endl;
};
function<ostream& (ostream&)> functionManip = [] (ostream& stream) -> ostream& {
    stream << "Hello world" << endl;
};

int main (int argc, char** argv) {
    cout << lambdaManip;    // OK
    cout << functionManip;  // Compiler error
}

The second cout statement fails with the following:

g++-4 src/Solve.cpp -c -g -std=c++0x -o src/Solve.o -I/home/ekrohne/minisat
src/Solve.cpp: In function 'int main(int, char**)':
src/Solve.cpp:24:11: error: cannot bind 'std::ostream' lvalue to 'std::basic_ostream<char>&&'
/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++/ostream:579:5: error:   initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = std::function<std::basic_ostream<char>&(std::basic_ostream<char>&)>]'

Why does this fail? I'm using cygwin gcc 4.5.3.

While I'm asking, I'm not nuts about using std::function everywhere, due to the efficiency problems. But I do wish to write functions that return lambda functions, and have no idea how to do so without std::function. For example, something like the following would be great

auto getAdditionFunctor();

auto getAdditionFunctor() {
    return [] (int x, int y) { return x + y };
};

...but obviously does not work. Is there an alternate syntax that does work? I can't imagine what it could be, so I may be stuck with std::function.

If I had a solution to the second question, then the first question would be moot.


Thank you.

Defining operator<<(ostream&, std::function<ostream&(ostream&)> helped. I had misread a web page, and was under the impression that ostream was smart enough to treat an arbitrary object that had an operator() as a manipulator. I was wrong about this. Furthermore, the simple lambda I built was probably just getting compiled into a plain old function, just as I was told. Indeed, if I use variable capture to ensure that the lambda is not a simple function, then the compiler fails. Also, objects with operator() defined are not (by default) treated as manipulators:

class Manipulator {
    ostream& operator()(ostream& stream) const {
        return stream << "Hello world" << endl;
    };
} classManip;

function<ostream& (ostream&)> functionManip = [] (ostream& stream) -> ostream& {
    return stream << "Hello world" << endl;
};

int main (int argc, char** argv) {
    const string str = "Hello world"; 
    auto lambdaManip = [&] (ostream& stream) -> ostream& {
        return stream << str << endl;     
    };

    cout << classManip;     // Compiler error
    cout << lambdaManip;    // Compiler error
    cout << functionManip;  // Compiler error
}

Further update: it turns out a slightly more robust solution than the ones below can be accomplished with:

// Tell ostreams to interpret std::function as a
// manipulator, wherever it sees one.
inline ostream& operator<<(
        ostream& stream, 
        const function<ostream& (ostream&)>& manipulator) {
    return manipulator( stream );
}

This code has an extra const. I discovered this trying to actually implement the solution in my project.

like image 687
Ed Krohne Avatar asked Oct 22 '12 00:10

Ed Krohne


People also ask

What is a parameterized stream manipulator?

A parameterized manipulator is implemented in two parts: The manipulator. It takes an extra parameter. In the previous code example, it takes an extra int parameter. You cannot place this manipulator function in a sequence of input or output operations, since there is no shift operator defined for it.

Is Setfill a parameterized stream manipulator?

setfill (near the bottom of the iomanip. h header file) is an example of a parameterized manipulator. You can create your own parameterized manipulators and your own simple manipulators.

What is a stream manipulator in C++?

Stream Manipulators are functions specifically designed to be used in conjunction with the insertion (<<) and extraction (>>) operators on stream objects, for example − std::cout << std::setw(10);


2 Answers

If you look at operator<< for ostream, there's no overload for taking a std::function - and that's basically what you're trying to do here with cout << functionManip. To fix this, either define the overload yourself:

ostream& operator<<(ostream& os, std::function<ostream& (ostream&)>& s)
{
    return s(os);
} 

Or pass the stream as an argument to the function:

functionManip(std::cout);

As for why the lambda is working, given that the return type of a lambda is undefined, and there is an overload for using a function pointer:

ostream& operator<< (ostream& ( *pf )(ostream&));

The lambda is probably wrapping everything utilizing a a struct and defining operator() which in this case will work exactly like a function pointer. This to me is the most likely explanation, hopefully someone can correct me if I'm wrong.

like image 58
Yuushi Avatar answered Oct 09 '22 21:10

Yuushi


This isn't the cause of the error, but since you've defined both lambdaManip and functionManip as having the return type ostream& I believe you've forgotten to add return stream; to both.


The call cout << functionManip fails because there is no operator<<(ostream&, std::function<ostream&(ostream&)> defined. Add one and the call will succeed.

ostream& operator<<(ostream& stream, function<ostream& (ostream&)>& func) {
  return func( stream );
}

Alternatively, you can call functionManip as

functionManip( cout );

This will work without adding the operator<< definition.


As for your question about returning a lambda, since the lambda returned by getAdditionFunctor is a capture-less lambda, it can be implicitly converted to a function pointer.

typedef int(*addition_ptr)(int,int);
addition_ptr getAdditionFunctor()
{
  return [] (int x, int y) -> int { return x + y; };
}

auto adder = getAdditionFunctor();
adder(10,20); // Outputs 30
like image 31
Praetorian Avatar answered Oct 09 '22 21:10

Praetorian