Now it is obvious that writing a manipulator for more and more parameters, or more manipulators at the same time, is time consuming and an awkward job. Unless you are certain that you will benefit a great deal, you may hesitate to do it. Whenever you get into a situation where you may need to write many manipulators, writing a good template for what to build later on is a good idea.
As I have just shown you how to write a manipulator for a single argument, I will write the template for two arguments. Note that this will only work if you pass on two arguments. For more arguments, extend the template list; for less, just remove it from the template list, and with it any code related to that variable.
On the other hand, you can also create a custom struct and group more data inside of it, but here it goes:
#include <iostream>
#include <string>
using namespace std;
// the template class that will do the job in the background
template<typename A1, typename A2, typename BT>
class TwoArgManip
{
public:
// take the arguments and the pointer to the function
TwoArgManip (basic_ostream<BT>&
(*pFun) (basic_ostream<BT>&, A1, A2), A1 val, A2 val2)
// initialize using the : method
: manipFun_(pFun), val_1(val), val_2(val2) {}
// the operator overload
void operator( )(basic_ostream<BT>& os) const
{// Invoke the function pointer with the
// stream and value
manipFun_(os, val_1,val_2);
}
private:
A1 val_1;
A2 val_2;
// this will hold the reference to the function
basic_ostream<BT>& (*manipFun_)
(basic_ostream<BT>&, A1, A2);
};
// a template for the operator overload
template<typename A1, typename A2, typename BT>
basic_ostream<BT>& operator<<
(basic_ostream<BT>& stream,
const TwoArgManip<A1,A2,BT>& manipulator)
{
manipulator(stream);
return(stream);
}
Here you should note that we also save a function reference during the initialization, so later on we can call the function inside the operator() overload. The compiler will automatically note that, upon run time, the operator<< will be used, so that will be constructed by default.
Additionally, you may say that I promised one with two arguments, but it turned out to be one with three. How is this possible? Well, you see, the third parameter is for the Base Type, or more commonly, whether we are talking about a stream constructed on the wide or narrow character.
Here is how you use the class by declaring two simple functions rather than two functions and a class:
// The function that will be called by the manipulator
// Here you add any modification that you do for the stream
ostream& setWidthAndFillFunc(ostream& stream, int n, char c) {
stream.width(n);
stream.fill(c);
return(stream);
}
// The manipulator used by the coder
TwoArgManip<int, char,char> setWidthAndFill(int n, char a) {
return
(TwoArgManip<int,char,char>(setWidthAndFillFunc, n,a));
}
In addition, using it is just as simple:
cout << setWidthAndFill(10, '@') << right << "Yetin";
With the output:
@@@@@Yeti