How to write a stream insert operator for the general case? (That is, for the streams `char` and` wchar_t`?)

I am implementing a thread insert statement for my class. I would like my class to work with both narrow and wide threads. I use a template to resolve this behavior - and it doesn’t depend on what type of stream is actually used, with the exception of character literals. If it is a wide string, character literals need L added to the literal, otherwise they do not.

Is there a way to associate such a thing with a template parameter so that I don't need to duplicate as much code on this?

(I would prefer to avoid using narrow or wide characters or character conversions at run time, if possible.)

An example of what I currently have is a template, but it will not work with narrow character streams due to wide character letters:

 template <typename charT, typename traits> std::basic_ostream<charT, traits>& operator<<( std::basic_ostream<charT, traits>& lhs, const Process& rhs ) { lhs << L"Process (0x" << std::setw(8) << std::hex << std::setfill(L'0') << rhs.GetId() << L") "; lhs << rhs.GetName() << std::endl; lhs << L"Command Line: " << rhs.GetCmdLine() << std::endl; const std::vector<Thread>& threads = rhs.GetThreads(); for (std::vector<Thread>::const_iterator it = threads.begin(); it != threads.end(); ++it) { lhs << L" --> " << *it << std::endl; } const std::map<void *, Module>& modules = rhs.GetModules(); for (std::map<void *, Module>::const_iterator it = modules.begin(); it != modules.end(); ++it) { lhs << L" --> " << it->second << std::endl; } return lhs; } 
+4
source share
2 answers

If you don't need overhead at runtime, I think that although an ugly macro can help you in this case.

 template <typename T> inline const T* select(const char* narrow, const wchar_t* wide); template <> inline const char* select<char>(const char* narrow, const wchar_t* /*wide*/) { return narrow; } template <> inline const wchar_t* select<wchar_t>(const char* /*narrow*/, const wchar_t* wide) { return wide; } #define doselect(T, str) select<T>(str, L ## str) template <typename charT, typename traits> std::basic_ostream<charT, traits>& operator<<( std::basic_ostream<charT, traits>& lhs, const Process& rhs ) { lhs << doselect(charT, "Process (0x") << std::setw(8) << std::hex << std::setfill(charT('0')) << rhs.GetId() << doselect(charT, ") "); lhs << rhs.GetName() << std::endl; lhs << doselect(charT, "Command Line: ") << rhs.GetCmdLine() << std::endl; const std::vector<Thread>& threads = rhs.GetThreads(); for (std::vector<Thread>::const_iterator it = threads.begin(); it != threads.end(); ++it) { lhs << doselect(charT, " --> ") << *it << std::endl; } const std::map<void *, Module>& modules = rhs.GetModules(); for (std::map<void *, Module>::const_iterator it = modules.begin(); it != modules.end(); ++it) { lhs << doselect(charT, " --> ") << it->second << std::endl; } return lhs; } 

Perhaps you can extend the doselect with another nice macro to further reduce code duplication. those. doselect2(" --> ") will automatically expand to doselect(charT, " --> ") .

+8
source

What I'm doing is just to insert char and convert them to wide or narrow char. The conversion is performed at compile time, and if you use only characters that are equal in wide and narrow coding, it works. This is tiring, but it really works.

 template <typename charT, typename traits> std::basic_ostream<charT, traits>& operator<<( std::basic_ostream<charT, traits>& lhs, const Process& rhs ) { lhs << charT('P') << charT('r') << charT('o') << charT('c') << charT('e') << charT('s') << charT('s') << charT(' ') << charT('(') << charT('0') << charT('x') << std::setw(8) << std::hex << std::setfill(charT('0')) << rhs.GetId() << charT(')') << charT(' '); lhs << rhs.GetName() << std::endl; lhs << charT('C') << charT('o') << charT('m') << charT('m') << charT('a') << charT('n') << charT('d') << charT(' ') << charT('L') << charT('i') << charT('n') << charT('e') << charT(':') << charT(' ') << rhs.GetCmdLine() << std::endl; const std::vector<Thread>& threads = rhs.GetThreads(); for (std::vector<Thread>::const_iterator it = threads.begin(); it != threads.end(); ++it) { lhs << charT(' ') << charT('-') << charT('-') << charT('>') << charT(' ') << *it << std::endl; } const std::map<void *, Module>& modules = rhs.GetModules(); for (std::map<void *, Module>::const_iterator it = modules.begin(); it != modules.end(); ++it) { lhs << charT(' ') << charT('-') << charT('-') << charT('>') << charT(' ') << it->second << std::endl; } return lhs; } 

If you have many lines, you can use another template that you will specialize in wide or narrow type of characters and use to store strings. This, however, will force you to duplicate your lines (and you violate the DRY principle).

 template <typename charT> struct ProcessInsertionOperatorHelper { static const charT* const String1; static const charT* const String2; static const charT* const String3; static const charT* const String4; }; template <> const wchar_t* const ProcessInsertionOperatorHelper<wchar_t>::String1 = L"Process (0x"; template <> const wchar_t* const ProcessInsertionOperatorHelper<wchar_t>::String2 = L") "; template <> const wchar_t* const ProcessInsertionOperatorHelper<wchar_t>::String3 = L"Command Line: "; template <> const wchar_t* const ProcessInsertionOperatorHelper<wchar_t>::String4 = L" --> "; template <> struct ProcessInsertionOperatorHelper<char> { }; template <typename charT, typename traits> std::basic_ostream<charT, traits>& operator<<( std::basic_ostream<charT, traits>& lhs, const Process& rhs ) { lhs << ProcessInsertionOperatorHelper<charT>::String1 << std::setw(8) << std::hex << std::setfill(L'0') << rhs.GetId() << ProcessInsertionOperatorHelper<charT>::String2; lhs << rhs.GetName() << std::endl; lhs << ProcessInsertionOperatorHelper<charT>::String3 << rhs.GetCmdLine() << std::endl; const std::vector<Thread>& threads = rhs.GetThreads(); for (std::vector<Thread>::const_iterator it = threads.begin(); it != threads.end(); ++it) { lhs << ProcessInsertionOperatorHelper<charT>::String4 << *it << std::endl; } const std::map<void *, Module>& modules = rhs.GetModules(); for (std::map<void *, Module>::const_iterator it = modules.begin(); it != modules.end(); ++it) { lhs << ProcessInsertionOperatorHelper<charT>::String4 << it->second << std::endl; } return lhs; } 
+1
source

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


All Articles