Custom Stream Manipulation in C++ - Templates on the Fly
(Page 4 of 4 )
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
Press any key to continue . . .
That is all you need to know, folks! By now, you have mastered the manipulators for streams at a level close to perfection. Thank you for reading through my article. As usual, I invite you to offer your questions and comments related to this article here at the blog or over at DevHardware Forums. Live with Passion!
| DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware. |