c++manipulators

Custom I/O manipulator in C++ for padding


In a question I found something like this: Write a custom I/O manipulator that prints

123.456%%%

against the following code cout<<setup<<123.456789;

How can this be achieved? I tried this:

#include <iostream>
#include <iomanip>
using namespace std;
std::ostream& setup(std::ostream& os, double num) {
    os << std::setprecision(3) << std::fixed << std::showpoint << std::setw(10) << std::left << num << "%%%";
    return os;
}

int main() {
    //cout << setfill('?') << setw(10) << 2343.0;
    cout<<setup<<123.45678;
    return 0;
}

Output: 1123.457


Solution

  • Building on @IgorTandetnik's comments above:

    The problem:

    Your setup function does not match the required signature of I/O manipulators as specified here for func - see overloads (18)-(20) of basic_ostream& operator<< :

    (18): basic_ostream& operator<<(std::ios_base& (*func)(std::ios_base&) );
    
    (19): basic_ostream& operator<<(std::basic_ios<CharT, Traits>& (*func)std::basic_ios<CharT, Traits>&) );
    
    (20): basic_ostream& operator<<(std::basic_ostream<CharT, Traits>& (*func)(std::basic_ostream<CharT, Traits>&) );
    

    Therefore when you use cout << setup, the setup function is not called (as it was done if it was a proper manipulator).
    Instead the address of it is converted to bool and displayed as 1 (this is the initial 1 that you see).
    clang even gives a meaniniful warning:

    warning: address of function 'setup' will always evaluate to 'true'
    

    A solution:

    You can achieve what you want by using one of the I/O manipulators signatures.

    Using the signature of func from overload (18) is shown below:

    #include <iostream>
    #include <iomanip>
    
    std::ostream& setup(std::ostream& os) {
        return os << std::setprecision(3) << std::fixed << std::showpoint << std::setw(10) << std::left << std::setfill('%');
    }
    
    int main() {
        std::cout<<setup<<123.45678;
        return 0;
    }
    

    Output:

    123.457%%%
    

    Live demo - Godbolt

    A side note: Why is "using namespace std;" considered bad practice?.


    Update:

    If you want to truncate the value and avoid rounding, you can multiply the value by 1000 (for 3 digits precision), floor it, and then divide by 1000:

    #include <iostream>
    #include <iomanip>
    #include <cmath>
    
    std::ostream& setup(std::ostream& os) {
        return os << std::setprecision(3) << std::fixed << std::showpoint << std::setw(10) << std::left << std::setfill('%');
    }
    
    int main() {
        double val = 123.45678;
        double val_truncated = std::floor(val*1000) / 1000;
        std::cout<<setup<<val_truncated;
        return 0;
    }
    

    Output:

    123.456%%%
    

    Live demo - Godbolt