2010-12-28 3 views
4

Ich bin ein stream insertion operator für eine Klasse von mir implementieren. Ich möchte, dass meine Klasse mit schmalen und breiten Streams arbeitet. Ich benutze eine Vorlage, um diese Art von Verhalten zu ermöglichen - und alles ist unabhängig davon, welcher Stream-Typ tatsächlich verwendet wird, mit Ausnahme von Zeichenliteralen. Wenn es eine breite Zeichenfolge ist, müssen die Zeichenliterale L dem Literal vorangestellt werden, andernfalls nicht.Wie schreibt man einen Stream Insertion Operator für den allgemeinen Fall? (Das heißt, für `char` und` wchar_t` Ströme?)

Gibt es eine Möglichkeit, diese Art von Sache auf den Template-Parameter zu tippen, so dass ich nicht so viel Code auf diesem duplizieren muss?

(ich würde es vorziehen, schmal zu breiten Charakter oder breit zu schmal Konvertierungen Zeichen zur Laufzeit, wenn möglich, zu vermeiden, durchgeführt wird.)

Beispiel von dem, was ich habe zur Zeit - es eine Schablone ist, aber es gewann‘ t Arbeit mit schmalen Zeichenströmen wegen der breiten Zeichenliterale:

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; 
} 
+0

Es wird mehr Spaß in C++ 0x, wenn es eine gazillion neue Charaktertypen gibt. –

+0

Nur mit dem gleichen Problem und ich entschied mich für dalle's Lösung vorerst. Jetzt, im Jahr 2015, mit C++ 0x und sogar C++ 11 bin ich neugierig, ob es besser ist, dieses Problem zu lösen. Ich versuche nur in Streams zu gelangen ... –

+0

@ AndreasW.Wylach: Ich habe einfach aufgegeben und mein eigenes Ding, inspiriert von Fastformats API, erstellt: https://github.com/BillyONeal/Instalog/blob/master/LogCommon/LogSink. hpp –

Antwort

8

Wenn Sie nicht über die Laufzeit-Overhead wollen, glaube ich, dass, obwohl hässlich, ein Makro, das Ihnen in diesem Fall helfen kann.

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; 
} 

Sie können sich wahrscheinlich erweitern doselect, mit einem anderen schönen Makro zur weiteren Code-Duplizierung zu reduzieren. d.h. doselect2(" --> ") würde automatisch auf doselect(charT, " --> ") expandieren.

1

Was ich tue, ist nur char einfügen, und sie entweder zu breiten oder schmalen char konvertieren. Die Konvertierung wird während der Kompilierung durchgeführt, und wenn Sie nur Zeichen verwenden, die in der breiten und schmalen Codierung gleich sind, funktioniert es. Das ist ermüdend, aber es funktioniert.

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; 
} 

Wenn Sie viele Strings haben, können Sie eine andere Vorlage verwenden, dass Sie auf dem breiten oder schmalen Zeichentyp spezialisiert werden und verwenden die Saiten zu speichern. Dies zwingt Sie jedoch, Ihre Strings zu duplizieren (und Sie werden das DRY-Prinzip nicht bestehen).

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; 
} 
+0

Oh Gott, meine Augen! –

+0

Ja, das ist hässlich. Ich bevorzuge auch @dalle Lösung. –