Search code examples
c++c++11iostreamiomanip

Can you pass a manipulator to a function?


I'd like to pass a list of manipulators to a function, something like this:

void print(const vector<std::smanip>& manips) {
  // ...
  for (auto m : manips)
    cout << m;
  // ...
}

which would ideally be called by code something like this:

some_object.print({std::fixed, std::setprecision(2)}));

g++ 4.7.0 says:

error: ‘std::smanip’ has not been declared

Apparently, smanip isn't really defined in the standard, and C++11 compilers don't need to provide an explicit name for the type of manipulators. I tried declaring a type by leeching off of a known manipulator, like this:

typedef decltype(std::fixed) manip;

This opened up a host of new error messages, including this one:

error: ‘const _Tp* __gnu_cxx::new_allocator< <template-parameter-1-1> 
>::address(__gnu_cxx::new_allocator< <template-parameter-1-1> >::const_reference)
const [with _Tp = std::ios_base&(std::ios_base&); __gnu_cxx::new_allocator<
<template-parameter-1-1> >::const_pointer = std::ios_base& (*)(std::ios_base&);
__gnu_cxx::new_allocator< <template-parameter-1-1> >::const_reference =
std::ios_base& (&)(std::ios_base&)]’ cannot be overloaded

Should I just give up now, or is there a way to do this?


Solution

  • An output manipulator is simply any type for which os << m is defined for some basic_ostream instantiation. A manipulator can be a function (subject to the operator<< overloads of basic_ostream) but it can also be any type which defines its own operator<<. As such we need to perform type erasure to capture the operator<< for an appropriate basic_ostream instantiation; the simplest way to do this is with std::function and a lambda:

    #include <iostream>
    #include <iomanip>
    #include <functional>
    #include <vector>
    
    template<typename S>
    struct out_manipulator: public std::function<S &(S &)> {
       template<typename T> out_manipulator(T &&t): std::function<S &(S &)>(
          [=](S &i) -> S &{ return i << t; }) {}
       template<typename T> out_manipulator(T *t): std::function<S &(S &)>(
          [=](S &i) -> S &{ return i << t; }) {}    // for g++
       template<typename U> friend U &operator<<(U &u, out_manipulator &a) {
          return static_cast<U &>(a(u));
       }
    };
    
    void print(const std::vector<out_manipulator<std::ostream>> &manips) {
       for (auto m: manips)
          std::cout << m;
    }
    
    int main() {
       print({std::fixed, std::setprecision(2)});
       std::cout << 3.14159;
    }