PS: Also there is one little bug with the code.. it does install the streamEvent responder several times.. but what a blog post without a bug or 2..
Anyway heres the code:
//g++ -g --std=c++11 custom_class_manip.cpp
#include <iostream>
#include <ios>
#include <sstream>
#include <functional>
template <typename TYPE>
class CustomManip
{
public:
typedef std::function<void(std::ostream&, const TYPE&)> ManipFunc;
// installation helper for a manipulator with params
class Installer
{
ManipFunc func_;
public:
Installer(ManipFunc func) :
func_(func)
{}
inline friend
std::ostream& operator<<(std::ostream& os,
const Installer& installer )
{
CustomManip<TYPE>::install(os,
installer.func_);
return os;
}
};
private:
struct CustomManipHandle
{
ManipFunc func_;
CustomManipHandle() { std::cout << "new handle at " << this << "\n"; }
~CustomManipHandle() { std::cout << "del handle at " << this << "\n"; }
};
static int handleIndex()
{
// the id for this Custommaniputors params
// in the os_base parameter maps
static int index = std::ios_base::xalloc();
return index;
}
public:
// for parameterized manipulator installations
static Installer make_installer(ManipFunc func)
{
return Installer(func);
}
// for unparameterized manipulator installations
static void install(std::ostream& os, ManipFunc func)
{
CustomManipHandle* handle =
static_cast<CustomManipHandle*>(os.pword(handleIndex()));
// check if its installed on this ostream
if (handle == NULL)
{
// install it
handle = new CustomManipHandle();
os.pword(handleIndex()) = handle;
// install the callback so we can destroy it
os.register_callback (CustomManip<TYPE>::streamEvent,0);
}
handle->func_ = func;
}
static void uninstall(std::ios_base& os)
{
CustomManipHandle* handle =
static_cast<CustomManipHandle*>(os.pword(handleIndex()));
//delete the installed Custommanipulator handle
if (handle != NULL)
{
os.pword(handleIndex()) = NULL;
delete handle;
}
}
static void streamEvent (std::ios::event ev,
std::ios_base& os,
int id)
{
switch (ev)
{
case os.copyfmt_event:
std::cout << "copyfmt_event\n"; break;
// does this mean i need to copy my manip as well??
case os.imbue_event:
std::cout << "imbue_event\n"; break;
case os.erase_event:
std::cout << "erase_event\n";
uninstall(os);
break;
}
}
static void print(std::ostream& os, const TYPE& data)
{
CustomManipHandle* handle
= static_cast<CustomManipHandle*>(os.pword(handleIndex()));
if (handle != NULL)
{
handle->func_(os, data);
return;
}
data.printDefault(os);
}
};
/**********************************
****************TEST**************
**********************************/
class A
{
int v_;
public:
A(int v) :
v_(v)
{}
void printDefault(std::ostream& os) const { os << " " << v_ << " default\n"; }
void printVer1 (std::ostream& os) const { os << " " << v_ << " abc\n"; }
void printVer2 (std::ostream& os) const { os << " " << v_ << " xyz\n"; }
void printParam (std::ostream& os,
const std::string paramA,
const int paramB) const
{
os << " " << v_ << " param: " << paramA << " " << paramB << "\n";
}
};
std::ostream& operator<<(std::ostream& os, const A& a)
{
CustomManip<A>::print(os, a);
return os;
}
class B
{
public:
void printDefault(std::ostream& os) const { os << " default\n"; }
void printVer3 (std::ostream& os) const { os << " mno\n"; }
};
std::ostream& operator<<(std::ostream& os, const B& b)
{
CustomManip<B>::print(os, b);
return os;
}
std::ostream& manip_default (std::ostream& os)
{
CustomManip<A>::uninstall(os);
CustomManip<B>::uninstall(os);
return os;
}
std::ostream& manip_ver1 (std::ostream& os)
{
CustomManip<A>::install(os,
[](std::ostream& oos, const A& a)
{
a.printVer1(oos);
});
return os;
}
std::ostream& manip_ver2(std::ostream& os)
{
CustomManip<A>::install(os,
[](std::ostream& oos, const A& a)
{
a.printVer2(oos);
});
return os;
}
std::ostream& manip_ver3(std::ostream& os)
{
CustomManip<B>::install(os,
[](std::ostream& oos, const B& b)
{
b.printVer3(oos);
});
return os;
}
CustomManip<A>::Installer manip_param(const std::string str,
const int other)
{
return CustomManip<A>::make_installer(
[str, other](std::ostream& oos, const A& a)
{
a.printParam(oos, str, other);
});
}
int main()
{
A a(1);
B b;
std::cout << a
<< b
<< "\n";
std::cout << manip_ver1 << a
<< manip_ver2 << a
<< manip_ver3 << b
<< manip_default << a
<< manip_ver1 << a
<< manip_param("testing", 4) << a
<< b
<< "\n";
std::stringstream oss;
oss << manip_ver1 << a
<< manip_ver2 << a
<< manip_ver3 << b
<< manip_default << a
<< manip_ver1 << a
<< manip_param("testing", 4) << a
<< b
<< "\n";
std::cout << oss.str();
}
The resulting output looks like
1 default default new handle at 0x307de0 1 abc 1 xyz new handle at 0x307e50 mno del handle at 0x307de0 del handle at 0x307e50 1 default new handle at 0x307de0 1 abc 1 param: testing 4 default new handle at 0x307ef0 new handle at 0x307e90 del handle at 0x307ef0 del handle at 0x307e90 new handle at 0x307e90 1 abc 1 xyz mno 1 default 1 abc 1 param: testing 4 default erase_event del handle at 0x307e90 erase_event erase_event