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