Custom manipulator for class

I am trying to write a stream manipulator with arguments. I have a class with 3 int CDate (Year, Month, Day). Therefore, I need to make the date_format(const char*) manipulator. eg:

 CDate a(2006, 5, 15); cout <<"DATE IS : " << date_format("%Y-hello-%d-world-%m-something-%d%d") << a; 

The output will be:

 DATE IS : 2006-hello-15-world-5-something-1515 

Suppose I need to use this

 ios_base & dummy_date_format_manipulator ( ios_base & x ) { return x; } ios_base & ( * ( date_format ( const char * fmt ) ) )( ios_base & x ) { return dummy_date_format_manipulator; } 

but I donโ€™t know how to do it.

+1
source share
4 answers

You can use the pword array for pword . Each iostream in C ++ has two arrays associated with it.

 ios_base::iword - array of ints ios_base::pword - array of void* pointers 

You can save your data in it. To get the index, this refers to the empty element in all arrays of iword and pword , you should use the function std::ios_base::xalloc() . It returns an int that you can use as a unique index in *word . You should get this index once at startup, and then use it for all *word operations.

Then programming your own manipulations will look like this:

The manipulator function, which receives a reference to the ios_base object and a pointer to a format string, simply stores this pointer in pword

 iosObject.pword(index_from_xalloc) = formatString 

Then the overloaded operator << ( >> ) gets the format string from the iostream object in the same way. After that, you just do the conversion referencing the format.

+2
source

Manipulators with arguments do not work the same as without arguments! These are just classes with a suitable output operator that, instead of outputting a value, controls the state of the stream. To control the state of the stream, you will probably configure the suitabe value stored with iword() or pword() associated with dtream and used by the output statement.

+1
source

As chris suggested, I would say that you should just use tm , not your own date class:

 tm a{0, 0, 0, 15, 5, 2006 - 1900}; cout << put_time(&a, "%Y-hello-%d-world-%m-something-%d%d"); 

If you must implement custom functions that cannot be executed with get_time and put_time , then you probably want to use the tm member as part of your class so that you can simply extend the functionality that already exists:

 class CDate{ tm m_date; public: CDate(int year, int month, int day): m_date{0, 0, 0, day, month, year - 1900}{} const tm& getDate() const{return m_date;} }; ostream& operator<<(ostream& lhs, const CDate& rhs){ auto date = rhs.getDate(); return lhs << put_time(&a, "%Y-hello-%d-world-%m-something-%d%d"); } 

Then you can use CDate as follows:

 CDate a(2006, 5, 15); cout << "DATE IS:" << a; 

EDIT:

If you look at your question again, I think that you have a misconception about how the insert operator works, you cannot pass both the object and the format: https://msdn.microsoft.com/en-us/ library / 1z2f6c2k.aspx

If you want to specify the format, but save your CDate class, I would again suggest using put_time :

 cout << put_time(&a.getDate(), "%Y-hello-%d-world-%m-something-%d%d"); 

If you insist again on writing your own format accept function, you need to create a helper class that can be built in-line and supported by the insert statement:

 class put_CDate{ const CDate* m_pCDate; const char* m_szFormat; public: put_CDate(const CDate* pCDate, const char* szFormat) : m_pCDate(pCDate), m_szFormat(szFormat) {} const CDate* getPCDate() const { return m_pCDate; } const char* getSZFormat() const { return m_szFormat; } }; ostream& operator<<(ostream& lhs, const put_CDate& rhs){ return lhs << put_time(&rhs.getPCDate()->getDate(), rhs.getSZFormat()); } 

You can use it as follows:

 cout << put_CDate(&a, "%Y-hello-%d-world-%m-something-%d%d") << endl; 
+1
source

Like Dietmar said you can push params to iword (), but I find such a solution tedious and annoying.

I prefer to just set lambda functions as iomanips and use them to directly call methods of different classes or to create custom prints in place. To do this, I created a simple 100-line boilerplate installer / helper class for mainpulators, which can add a lambda function as a manipulator of any class.

So, for your CDate, you can determine that you are working as

 std::ostream& dummy_date_format_manipulator (std::ostream& os) { CustomManip<CDate>::install(os, [](std::ostream& oos, const CDate& a) { os << a.year() << "-hello-" << a.day() << "-world-" << a.month() << "-something-" << a.day() << a.day(); }); return os; } 

Then just direct <op to use the installation helpers for printing:

 std::ostream& operator<<(std::ostream& os, const CDate& a) { CustomManip<CDate>::print(os, a); return os; } 

And basically it's done ..

The mainp installation code and a fully working example are in my blog post at: http://code-slim-jim.blogspot.jp/2015/04/creating-iomanip-for-class-easy-way.html

But to be beautiful .. here is the key part that you want to put in .h somewhere less than all the printouts to demonstrate how it works:

 //g++ -g --std=c++11 custom_class_manip.cpp #include <iostream> #include <ios> #include <sstream> #include <functional> template <typename TYPE> class CustomManip { private: typedef std::function<void(std::ostream&, const TYPE&)> ManipFunc; struct CustomManipHandle { ManipFunc func_; }; 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: 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.erase_event: uninstall(os); break; case os.copyfmt_event: case os.imbue_event: 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); } }; 

Of course, if you really need parameters, use the CustomManip :: make_installer (...) function to do this, but for this you will need to visit the blog.

0
source

Source: https://habr.com/ru/post/984410/


All Articles