2010-11-23 5 views
4

NEU: Vielen Dank an alle, die mir dabei geholfen haben! Die Antwort ist unten markiert, und ich habe mit einer funktionierenden Version in meiner Frage auf der Antwort erweitert, unter (siehe dort):Vorlagen und String-Literale und UNICODE


Ich scheine viel in diese Situation zu laufen (während unseres String-Dienstprogramme Aktualisierung Bibliothek):

Ich brauche eine Möglichkeit, eine Vorlage, die für beide char und wchar_t, die verschiedene String-Literale verwendet funktioniert. Momentan finde ich das herausfordernd, weil ich nicht weiß, wie ich eine Kompilierzeit haben kann, um String-Literale als schmale oder breite Zeichen zu ändern.

Zur Erörterung, nehmen Sie die folgende TCHAR basierte Funktion:

// quote the given string in-place using the given quote character 
inline void MakeQuoted(CString & str, TCHAR chQuote = _T('"')) 
{ 
    if (str.IsEmpty() || str[0] != chQuote) 
     str.Format(_T("%c%s%c"), chQuote, str, chQuote); 
} 

ich möchte es als Templat statt:

// quote the given string in-place using the given quote character 
template <typename CSTRING_T, typename CHAR_T> 
inline void MakeQuoted(CSTRING_T & str, CHAR_T chQuote = '"') 
{ 
    if (str.IsEmpty() || str[0] != chQuote) 
     str.Format("%c%s%c", chQuote, str, chQuote); 
} 

Ab sofort haben wir ein Problem mit den beiden Zeichenketten (" ', und "% c% s% c").

Wenn das obige für CSTRING_T = CStringA, CHAR_T = char aufgerufen wird, dann sind die obigen Literale in Ordnung. Wenn es für CStringW und wchar_t aufgerufen wird, dann reall y brauchen (L '"' und L"% c% c% c ").

Also muss ich einen Weg, etwas zu tun wie:

template <typename CSTRING_T, typename CHAR_T> 
inline void MakeQuoted(CSTRING_T & str, CHAR_T chQuote = Literal<CHAR_T>('"')) 
{ 
    if (str.IsEmpty() || str[0] != chQuote) 
     str.Format(Literal<CHAR_T>("%c%s%c"), chQuote, str, chQuote); 
} 

Und das ist, wo ich bin verloren: Was in der Welt kann ich Wörtliche tun, um (String-oder-Zeichen-Literal), die tatsächlich Ergebnisse in L "string" oder "string" abhängig von CHAR_T?

Edit: Es gibt über hundert Funktionen, viele von ihnen komplexer mit mehr String-Literalen in ihnen, die für schmale und breite Strings verfügbar sein müssen. Kurz vor dem Kopieren jeder dieser Funktionen und dann jeder Bearbeitung entweder breit oder schmal, sicherlich gibt es eine Technik, die eine einzige Definition erlauben würde, die sich nach CHAR_T unterscheidet?


Ich gebe die Antwort auf den Hybrid-Makro + Vorlage, dass Mark Ransom geliefert, aber ich wollte eine vollständigere Lösung (für alle, die gepflegt) aufzunehmen, so ist es hier:

// we supply a few helper constructs to make templates easier to write 
// this is sort of the dark underbelly of template writing 
// to help make the c++ compiler slightly less obnoxious 

// generates the narrow or wide character literal depending on T 
// usage: LITERAL(charT, "literal text") or LITERAL(charT, 'c') 
#define LITERAL(T,x) template_details::literal_traits<typename T>::choose(x, L##x) 

namespace template_details { 

    // Literal Traits uses template specialization to achieve templated narrow or wide character literals for templates 
    // the idea came from me (Steven S. Wolf), and the implementation from Mark Ransom on stackoverflow (http://stackoverflow.com/questions/4261673/templates-and-string-literals-and-unicode) 
    template<typename T> 
    struct literal_traits 
    { 
     typedef char char_type; 
     static const char * choose(const char * narrow, const wchar_t * wide) { return narrow; } 
     static char choose(const char narrow, const wchar_t wide) { return narrow; } 
    }; 

    template<> 
    struct literal_traits<wchar_t> 
    { 
     typedef wchar_t char_type; 
     static const wchar_t * choose(const char * narrow, const wchar_t * wide) { return wide; } 
     static wchar_t choose(const char narrow, const wchar_t wide) { return wide; } 
    }; 

} // template_details 

Darüber hinaus habe ich einige Helfer schreiben Vorlagen zu machen, die dieses Konzept in Verbindung mit CStringT verwendet <> ein bisschen leichter/schöner & comprehend zu lesen:

// generates the correct CString type based on char_T 
template <typename charT> 
struct cstring_type 
{ 
    // typedef CStringT< charT, ATL::StrTraitATL< charT, ATL::ChTraitsCRT<charT> > > type; 
    // generate a compile time error if we're invoked on a charT that doesn't make sense 
}; 

template <> 
struct cstring_type<char> 
{ 
    typedef CStringA type; 
}; 

template <> 
struct cstring_type<wchar_t> 
{ 
    typedef CStringW type; 
}; 

#define CSTRINGTYPE(T) typename cstring_type<T>::type 

// returns an instance of a CStringA or CStringW based on the given char_T 
template <typename charT> 
inline CSTRINGTYPE(charT) make_cstring(const charT * psz) 
{ 
    return psz; 
} 

// generates the character type of a given CStringT<> 
#define CSTRINGCHAR(T) typename T::XCHAR 

Mit dem oben genannten ist es möglich, Vorlagen zu schreiben, die die richtige CString-Varietät basierend auf CStringT <> oder char/wchar_t-Argumenten generieren. Zum Beispiel:

// quote the given string in-place using the given quote character 
template <typename cstringT> 
inline void MakeQuoted(cstringT & str, CSTRINGCHAR(cstringT) chQuote = LITERAL(CSTRINGCHAR(cstringT), '"')) 
{ 
    if (str.IsEmpty() || str[0] != chQuote) 
     str.Format(LITERAL(cstringT::XCHAR, "%c%s%c"), chQuote, str, chQuote); 
} 

// return a quoted version of the given string 
template <typename cstringT> 
inline cstringT GetQuoted(cstringT str, CSTRINGCHAR(cstringT) chQuote = LITERAL(CSTRINGCHAR(cstringT), '"')) 
{ 
    MakeQuoted(str, chQuote); 
    return str; 
} 
+0

Ich bin mir bewusst, dass ich Funktion Überladung verwenden kann, um zwei Definitionen für MakeQuoted zu generieren, anstatt eine einzige Vorlage zu verwenden, an welcher Stelle ich keine Sorgen über die Literale machen muss (siehe unten für @In Silicos Antwort). Das scheint jedoch albern zu sein, da ich den ganzen Code wortwörtlich wiederhole, nur um zwei verschiedene Sätze von Literalen zu liefern. Sicherlich gibt es eine Möglichkeit, Metaprogrammierung zu verwenden, um das richtige Literal (das typabhängige Literal) im laufenden Betrieb zu erzeugen. – Mordachai

+0

@Mordachai: Keine Metaprogrammierung verwenden. Wenn das so wäre, könnten wir auch die Saiten einschalten. Können Sie ein Beispiel für eine Syntax nennen, die Sie verwenden möchten, um diese Funktion * zu verwenden? –

+0

Ich bin mir nicht sicher, dass dies der Fall ist. Vorlagen generieren Code, der basierend auf den Typparametern variiert, die der Vorlage bereitgestellt werden. Nach was ich hier suche, ist ein Literal, das mit dem Typ variiert, der ihm geliefert wird. Vielleicht Template-Spezialisierung ?! Ich möchte, dass der Compiler die automatische Typ-Promotion für String-Literale durchführt (man denke an ein skalares Argument, die Vorlage würde ohne Aufwand funktionieren, da ein skalares Literal automatisch zum richtigen Typ hochgestuft wird. Nicht so für String-Literale). :( – Mordachai

Antwort

5

Das Konzept ist ein Makro zu verwenden, um beide Formen der wörtlichen, char und wchar_t zu erzeugen, dann lassen Sie eine Template-Funktion wählen, welche für den Kontext geeignet ist.

Denken Sie daran, dass Vorlagenfunktionen keinen Code generieren, bis Sie anderen Code haben, der sie aufruft. Die meiste Zeit ist das egal, aber es wäre für eine Bibliothek.

Dieser Code ist nicht getestet, aber ich glaube, es wird funktionieren.

#define LITERAL(T,x) CString_traits<T>::choose(x, L##x) 

template<typename T> 
struct CString_traits 
{ 
    typedef char char_type; 
    static const char * choose(const char * narrow, const wchar_t * wide) { return narrow; } 
    static char choose(char narrow, wchar_t wide) { return narrow; } 
}; 

template<> 
struct CString_traits<CStringW> 
{ 
    typedef wchar_t char_type; 
    static const wchar_t * choose(const char * narrow, const wchar_t * wide) { return wide; } 
    static wchar_t choose(char narrow, wchar_t wide) { return wide; } 
}; 

template <typename T> 
inline void MakeQuoted(T & str, CString_traits<T>::char_type chQuote = LITERAL(T,'"')) 
{ 
    if (str.IsEmpty() || str[0] != chQuote) 
     str.Format(LITERAL(T,"%c%s%c"), chQuote, str, chQuote); 
} 
+0

Das Hauptproblem damit, das ich sehen kann, ist, wenn der Anrufer chQuote ersetzen wollte - sie würden dem LITCHAR-Makro ausgesetzt werden müssen, ist es von diesem Gesichtspunkt nicht schrecklich sauber. – Puppy

+0

@DeadMG, sehe ich keine Möglichkeit zu vermeiden, den aufrufenden Code dem Makro auszusetzen. Es ist notwendig, gleichzeitig char und wchar_t Formen des Literals zu erstellen, ohne sich zu wiederholen. –

+1

Ich bin kein Sprach-Purist auf alle Fälle. Ich möchte Code, der sehr brauchbar ist, und das sieht vielversprechend aus. Ich werde es versuchen und sehen, ob ich es schaffen kann.Mit einem zusätzlichen Makro in einer Welt von Tausenden (Win32-Programmierung) leben zu müssen, ist kaum ein Nachteil. – Mordachai

-1

Ich glaube, Sie TEXT MFC Makro wollen:

TCHAR* psz = TEXT("Hello, generic string"); 
+0

Wenn ich eine Frage beantwortet haben, die Sie nicht gefragt haben, lassen Sie mich wissen, und ich werde dies löschen –

+0

Dies funktioniert, wenn Was ich brauche, ist eine Zeichenfolge, die mit den Compilereinstellungen variiert, und nicht mit der Art der Zeichenfolge, die verarbeitet wird. Dies funktioniert sehr gut für Client-Code (die Anwendungsquelle), ist aber nicht hilfreich für Bibliothekscode, der innerhalb eines einzelnen Projekts sowohl für schmale als auch für breite Strings verwendet wird, unabhängig vom UNICODE- oder MBCS-Build-Ziel des Compilers. – Mordachai

0

Sie brauchen keine Vorlagen zu verwenden für so etwas, wenn man bedenkt, es gibt nur zwei Möglichkeiten, MakeQuoted() zu verwenden.Sie können Überladen von Funktionen für den gleichen Zweck verwendet werden:

inline void MakeQuoted(CStringA& str, char chQuote = '"') 
{ 
    if (str.IsEmpty() || str[0] != chQuote) 
     str.Format("%c%s%c", chQuote, str, chQuote); 
} 


inline void MakeQuoted(CStringW& str, wchar_t chQuote = L'"') 
{ 
    if (str.IsEmpty() || str[0] != chQuote) 
     str.Format(L"%c%s%c", chQuote, str, chQuote); 
} 

Sicherlich ist dies der einfachste Weg, es zu tun, ohne Makros zu verwenden, vorausgesetzt, dass Ihr Grund ist Bibliothek eine Template-basierte Lösung mit String-Dienstprogramme für den Versuch.

Sie können gemeinsame Funktionalität für lange und komplizierte Funktionen ausklammern:

template<typename CStrT, typename CharT> 
inline void MakeQuotedImpl(CStrT& str, CharT chQuote, 
    const CharT* literal) 
{ 
    if (str.IsEmpty() || str[0] != chQuote) 
     str.Format(literal, chQuote, str, chQuote); 

} 

inline void MakeQuoted(CStringA& str, char chQuote = '"') 
{ 
    MakeQuotedImpl(str, chQuote, "%c%s%c"); 
} 


inline void MakeQuoted(CStringW& str, wchar_t chQuote = L'"') 
{ 
    MakeQuotedImpl(str, chQuote, L"%c%s%c"); 
} 
+0

Genau das mache ich gerade. Es skaliert nicht sonderlich gut (wir haben über hundert ähnliche Funktionen wie oben). – Mordachai

+0

@Mordachai: Laut Ihrem Kommentar zu John Diblings Antwort scheint es, als wollten Sie eine Bibliothek erstellen, die sowohl mit char- als auch mit wchar_t-basierten Strings in einem einzigen Projekt arbeiten kann, ohne die Compiler-Einstellungen zu berücksichtigen. Leider gibt es nicht viel mehr, was Sie tun können - das Überladen von Funktionen ist der "einfachste" Weg, dies in diesem speziellen Fall zu tun. Ich weiß, dass die Windows-API zwei Versionen für jede Funktion hat, die eine Zeichenfolge akzeptiert (z. B. "CreateWindowA()' vs. 'CreateWindowW()'). –

+0

@Mordachai: Beachten Sie, dass Sie möglicherweise nicht für jede Funktion in der Bibliothek für Zeichenfolgenfunktionen auf Funktionsüberladung zurückgreifen müssen. Wenn Sie Funktionen haben, die ohne Probleme wie diese in Template-Funktionen "umgewandelt" werden können, dann können Sie das natürlich für diese Funktionen tun. –

-1

OK, also, wenn Sie wirklich diese Vorlage zu verwenden wollen, glaube ich, das Beste, was ich in der Lage gewesen, mit zu kommen eine Vorlagenklasse, die Ihre Literale basierend auf this discussion speichert. Etwas wie folgt aus:

template <typename T> class Literal; 
template <> class Literal<char> 
{ 
public: 
    static const char Quote = '"'; 
}; 
template <> class Literal<wchar_t> 
{ 
public: 
    static const wchar_t Quote = L'"'; 
}; 

Dann Sie Literal<CHAR_T>::Quote in Ihre nicht-spezialisierten aber Templat-Funktionen verwenden würde. Irgendwie chaotisch, schätze ich, aber es hat den Vorteil, dass Sie Ihre Funktionslogik unduplicated belassen und Ihnen templated String-Literale gibt.

+0

Gleiche Antwort wie oben: Dies variiert je nach Compiler-Build-Ziel, nicht durch übergeben in String-Typ. Ich habe Projekte, die mit schmalen und breiten Zeichenfolgen innerhalb desselben Builds arbeiten müssen (Legacy-Dateien, die eng und breit für Win32-APIs usw. sind) – Mordachai

+0

Meine Antwort wurde aktualisiert. –

+0

Danke - Ich dachte darüber nach, dies generell als Policy-Klasse zu tun. Es würde funktionieren, solange ich bereit wäre, alle möglichen willkürlichen Zeichenfolgen für den Verbrauch zu verwenden (ich könnte dies in einen Details-Namespace nur für diesen Header einfügen ...). – Mordachai

-1

Sie können die Teilspezialisierung der Vorlage für MarkQuoted verwenden und das Angebot basierend auf dem Typ angeben.

1

Dieses Stück ist mein persönliches winziges kleines Genie.

#include <malloc.h> 
template<typename to, int size> to* make_stack_temporary(const char(&lit)[size], to* memory = (to*)_alloca(sizeof(to)*size)) { 
    for(int i = 0; i < size; i++) 
     memory[i] = lit[i]; 
    return memory; 
} 

Wenn Sie alloca in einem Standardargument verwenden, es ist eigentlich die Anrufer ‚s Stack, so dass Sie Arrays auf den Heap, ohne auf das Rück zugewiesen wird. Keine dynamische Zuweisung, keine Speicherbefreiung. _alloca ist eine CRT-Funktion von MSVC, daher gebe ich keine Portabilitätsgarantien - aber wenn Sie ATL verwenden, ist das wahrscheinlich kein Problem. Natürlich bedeutet dies auch, dass der Zeiger nicht über die aufrufende Funktion hinaus gehalten werden kann, aber für temporäre Verwendungen wie Formatzeichenfolgen sollte er ausreichen. Es gibt auch einige Vorbehalte, die mit der Ausnahmebehandlung zu tun haben, auf die Sie wahrscheinlich nicht stoßen werden (überprüfen Sie MSDN für Details), und natürlich wird es nur für Charaktere funktionieren, die die gleiche binäre Darstellung haben, die meines Wissens jedes Zeichen ist in ein schmales String-Literal eingeben. Ich schätze, dass dies nur eine Teilmenge der tatsächlich aufgetretenen Probleme löst, aber es ist eine weit überlegene Lösung für diese spezifische Teilmenge als das Makro, oder jedes Literal zweimal angeben, etc.

Sie können auch die definitiv hässlichere verwenden aber mehr verhaltenskonsistente Aggregatinitialisierung.

template<typename T> some_type some_func() { 
    static const T array[] = { 'a', ' ', 's', 't', 'r', 'i', 'n', 'g', ' ', 'l', 'i', 't', 'e', 'r', 'a', 'l', '\0' }; 
} 

In C++ 0x mit variadische Vorlagen kann es möglich sein, diese Lösung nicht zu saugen. Ich bin nah an einer besseren Lösung, die C++ 03 ist, aber halte nicht den Atem an.

Edit: Sie können dies tun, welche IMO die beste Lösung ist, beinhaltet immer noch einige Unordnung.

#include <iostream> 
#include <array> 
#include <string> 

struct something { 
    static const char ref[]; 
}; 

const char something::ref[] = ""; 

template<int N, const char(*t_ref)[N], typename to> struct to_literal { 
private: 
    static to hidden[N]; 
public: 
    to_literal() 
    : ref(hidden) { 
     for(int i = 0; i < N; i++) 
      hidden[i] = (*t_ref)[i]; 
    } 
    const to(&ref)[N]; 
}; 
template<int N, const char(*t_ref)[N], typename to> to to_literal<N, t_ref, to>::hidden[]; 

template<int N, const char(&ref)[N], typename to> const to* make_literal() { 
    return to_literal<N, &ref, to>().ref; 
} 

int main() { 
    std::wcout << make_literal<sizeof(something::ref), something::ref, wchar_t>(); 
    std::wcin.get(); 
} 

Sie haben durch alle wörtlichen gehen und es ein statisches Element einer Struktur zu machen, dann verweisen sie, aber es funktioniert viel besser.

+0

@Mordachai: Ich denke auch, dass die Sprache in dieser Hinsicht schlecht ist und könnte erheblich verbessert werden. – Puppy

0

Ich habe eine ähnliche Situation. Ich habe 1 Quellcode-Datei und eine Header-Datei (natürlich) gemacht, die ich vom Aufbau ausschließe. Dann erstellt 2 andere Quelldateien, die die ursprüngliche Quelle enthalten, über eine Direktive #include. In einer Datei definiere ich UNICODE (wenn nicht bereits definiert) vor dem Include. In der anderen Datei habe ich #undef UNICODE (falls definiert). Die Quelldatei enthält einige statische Strukturen und eine Reihe von Funktionen, die für beide char-Sätze identisch sind (im Text) (nicht beim Kompilieren). Wenn jede Funktion entweder wchar_t oder char als Parameter hat, ergibt sich diese Methode in 2 Mengen überladener Funktionen oder 2 Mengen unterschiedlich benannter Funktionen (hängt davon ab, wie die Header-Datei geschrieben wird, nehmen Sie tchar.h als Beispiel). Jetzt sind sowohl UNICODE- als auch ANSI-Versionen der Funktionen für eine Anwendung verfügbar und wenn die Header-Datei korrekt geschrieben ist, ist auch die Standardversion für TCHAR. Wenn Sie möchten, dass ich darauf eingehen kann, sagen Sie es einfach.

+0

Der Wrapping-Trick kann auch für eine Header-Datei verwendet werden, aber achten Sie darauf, UNICODE neu zu definieren oder neu zu definieren. –

+0

Ich dachte darüber nach, dies zu tun. Ich denke, solange Sie _T, TEXT und UNICODE einrichten, würde es meinen Bedürfnissen entsprechen. Aber wenn Sie versuchen, Dinge wie _ttoi oder _tcscpy usw. zu nennen, werden diese nicht vorher entweder als _mbs oder als str definiert - verwenden die CRT-Header #pragma nicht einmal? – Mordachai

+0

Ich selbst habe keine CRT-Routinen verwendet, sondern die WINDOWS-API (um Multi-Threading-Probleme zu vermeiden). Aber Sie sollten es versuchen, wenn es funktioniert sparen Sie sich viel Arbeit und Debugging. Stellen Sie jedoch sicher, dass Sie keine vorkompilierten Header verwenden, da dies die Ausführung dieser Technik verhindert. Nur zum Umbrechen von Quellen kann Ihre andere Quelle pp. –