2010-11-23 20 views
4

NEW:これを手伝ってくれてありがとう皆さん!その答えは、以下のマークされ、私は私の質問で機能バージョンと答えに拡大してきた、(QV)以下:テンプレートと文字列リテラルとユニコード


私たちの文字列ユーティリティを更新している間、私は(このような状況の中に多くのことを実行しているように見えますライブラリ):

さまざまな文字列リテラルを使用するcharとwchar_tの両方で動作するテンプレートを用意する方法が必要です。コンパイル時に文字列リテラルを狭くするかワイド文字にするかを知りません。すぐに我々は '"'(2つの文字列リテラルに問題がある

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

、:

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

は、私が代わりにそれをテンプレートにしたい:

は検討のために、以下のTCHARベースの機能を取ります"%c%s%c")。

上記のCSTRING_T = CStringA、CHAR_T = charの場合は上記のリテラルは問題ありませんが、CStringWとwchar_tで呼び出された場合はy必要(L '' '、L "%c%c%c")。

だから私のようなものを行うにいくつかの方法が必要です。

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

を私が失われていますところそれはです:世界で私はリテラル(文字列または文字リテラル)を作るために何ができるか、実際の結果L "文字列"または "文字列"に応じてCHAR_T?

編集:100以上の関数がありますが、その中にはより多くの複雑な文字列リテラルがあり、幅の狭い文字列と幅広い文字列の両方で使用できる必要があります。そのような関数をすべてコピーしておき、それぞれを幅広くまたは狭く編集するのではなく、確かにCHAR_Tごとに異なる単一の定義を許可するテクニックがありますか?

私はマーク身代金を与え、私は(世話を誰のため)、より完全なソリューションを含めたかったので、ここにあるハイブリッドマクロ+テンプレートに答えを与えている

// 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 

はまた、私は&理解を読み取るためにCStringT <と一緒にこの概念を利用筆記テンプレート>少し簡単に/よりよいを作るために、いくつかのヘルパーを作成しました:

// 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 

上記では、CStringT <>またはchar/wchar_t引数に基づいて、正しいCStringの種類を生成するテンプレートを記述することができます。例えば:私はあなたがTEXT MFCマクロをしたいと考えてい

// 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

私は、関数のオーバーロードを使って、単一のテンプレートを使うのではなく、MakeQuotedの2つの定義を生成できることに気付いています。その時点で、リテラルについて心配する必要はありません(@In silicoの答えは以下を参照してください)。しかし、それは、リテラルの2つの異なるセットを供給するだけで、すべてのコードをそのまま繰り返していることを考えると、ばかげているようです。確かに、正確なリテラル(タイプ依存のリテラル)をオンザフライで生成するためにメタプログラミングを使用する方法がいくつかあります。 – Mordachai

+0

@Mordachai:メタプログラミングを使用していません。もしあれば、文字列を切り替えることもできます。この関数を*使用するために使用したい構文の例を挙げることができますか? –

+0

これが当てはまるかどうかはわかりません。テンプレートは、テンプレートに提供される型パラメータに基づいて変化するコードを生成します。私がここで探しているのは、それに供給される型によって異なるリテラルです。たぶんテンプレートの専門性?それは、コンパイラが文字列リテラルの自動型宣伝をしたいようなものです(スカラーリテラルは適切な型に自動的に昇格されるため、テンプレートは努力なしで動作しますが、文字列リテラルではそうではありません)。 : – Mordachai

答えて

5

概念は、リテラルの両方の形式(charwchar_t)を生成するためにマクロを使用し、テンプレート関数がどちらがコンテキストに適しているかを選択させることです。

テンプレート関数は、呼び出しを行う他のコードがない限り、実際にコードを生成しません。ほとんどの場合、これは問題ではありませんが、図書館にとってはそうです。

このコードはテストされていませんが、動作すると思います。

#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

私が見ることができる主な問題は、呼び出し側がchQuoteを置き換えたい場合です.LITCHARマクロにさらされなければならないということです。その点からはきわめてきれいではありません。 – Puppy

+0

@DeadMG、私はマクロに呼び出しコードを公開しないようにする方法はありません。あなた自身を繰り返すことなく、リテラルのchar型とwchar_t型を同時に作成する必要があります。 –

+1

私は決して言語を純粋にしていません。私は非常に使いやすいコードを望んでおり、これは有望です。私はそれを打ち、私はそれを動作させることができるかどうかを確認します。何千もの世界(Win32プログラミング)で1つの余分なマクロで暮らすことは、まったく面倒なことではありません。 – Mordachai

-1

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

あなたが質問しなかった質問に答えたら、教えてください&私はこれを削除します –

+0

これは私が必要とするのは、処理される文字列の型ではなく、コンパイラの設定によって異なる文字列です。これはクライアントコードではうまく機能しますが(アプリケーションソース)、コンパイラのUNICODEまたはMBCSビルドターゲットに関係なく、単一のプロジェクト内の狭い文字列と広い文字列の両方で使用されるライブラリコードには役立ちません。 – Mordachai

0

あなたはMakeQuoted()を使用する唯一の方法は2つあります考慮すると、このような何かのためのテンプレートを使用する必要はありません。あなたは、同じ目的のためのオーバーロード機能を使用することができます。

​​

確かにこれはそれがあなたの文字列ユーティリティライブラリとテンプレートベースのソリューションをしようとするためのあなたの理由だと仮定すると、マクロを使用することなく、それを行うための最も簡単な方法です。

あなたが長くて複雑な機能のための共通の機能くくり出すことができます。

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

これはまさに私が今やっていることです。それはひどくうまくスケールされません(私たちは上記と同様の100以上の関数を持っています)。 – Mordachai

+0

@Mordachai:John Diblingの答えによると、コンパイラ設定に関係なく、単一のプロジェクト内の 'char'と' wchar_t'ベースの文字列の両方で動作するライブラリを作成したいと思うようです。残念なことに、それ以上のことはできません。関数のオーバーロードは、この特定のケースでは「最も簡単な」方法です。 Windows APIには、文字列を受け取るすべての関数(たとえば、 'CreateWindowA()'と 'CreateWindowW()')の2つのバージョンがあります。 –

+0

@Mordachai:文字列ユーティリティライブラリのすべての関数の関数オーバーロードに頼る必要はないかもしれないことに注意してください。このような問題なしにテンプレート関数に "変換"できる関数を持っているのであれば、それらの関数に対して確かに行うことができます。 –

-1

OKを、そう、あなたが本当にこれをテンプレートにしたい場合は、私は私が思い付くことができました最高のものだと思いますthis discussionに基づいてリテラルを格納するテンプレートクラスです。このような何か:

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

その後、あなたはあなたの非専門化が、テンプレート機能でLiteral<CHAR_T>::Quoteを使用すると思います。ちょっと混乱しているようですが、それはあなたの関数ロジックを重複せずに残す利点があり、テンプレート文字列リテラルを提供します。

+0

上記と同じ回答です:これはコンパイラビルドターゲットによって異なり、文字列型ではなく渡されます。私は、同じビルド内で狭い文字列と広い文字列の両方を扱わなければならないプロジェクトを持っています(狭いレガシーファイル、Win32 APIなど) – Mordachai

+0

私の答えが更新されました。 –

+0

ありがとうございます - 私は一般的にこれをポリシークラスとして行うことを考えました。消費するためにあらゆる種類の任意の文字列を入れることができれば、それはうまくいくでしょう(私はこれをヘッダのための詳細な名前空間に入れることができます...)。 – Mordachai

-1

MarkQuotedにテンプレートの部分特殊化を使用し、その型に基づいて引用符を付けることができます。

1

この作品は、私自身の個人的な小さな天才です。

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

デフォルト引数にはallocaを使用する場合、それは実際にあなたがヒープに頼ることなく配列を返すようにできるように、呼び出し側のスタックをオフに割り当てられています。ダイナミックな割り当てはありません。メモリは解放されません。 _allocaはMSVCが提供するCRT関数なので、移植性を保証するものではありませんが、もしATLを使用しているのであれば問題はないでしょう。もちろん、これはポインタが呼び出し関数を超えて保持されないことを意味しますが、フォーマット文字列のような一時的な使用で十分です。例外処理にはいくつかの注意点があります(詳細についてはMSDNをチェックしてください)。もちろん、同じバイナリ表現を持つ文字に対してのみ動作します。狭い文字列リテラルを入れる。私はこれがあなたが遭遇したかもしれない実際の問題のサブセットを解決するだけであることを認めていますが、マクロよりもその特定のサブセットに対してはるかに優れた解決策です。より一貫的に統一された集約の初期化を行います。

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

variadicテンプレートを使用するC++ 0xでは、この解決策が吸い込まれない可能性があります。私はC++ 03のよりよい解決策に近づいていますが、あなたの息を止めません。

編集:あなたはこれを行うことができます。これは最高の解決策ですが、まだまだ混乱します。

#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(); 
} 

すべてのリテラルを構造体の静的メンバーにして参照する必要がありますが、その方がはるかに優れています。

+0

@Mordachai:私はまた、この点で言語が貧しく、大幅に改善されると思う。 – Puppy

0

私は同様の状況があります。私は1つのソースコードファイルと、ビルドから除外するヘッダーファイル(もちろん)を作成しました。 #includeディレクティブで元のソースを含む2つのソースファイルを作成しました。あるファイルでは、インクルードの前にUNICODE(まだ定義されていない場合)を定義します。他のファイルでは、#define UNICODE(定義されている場合)。ソースファイルにはいくつかの静的構造といくつかの関数が含まれています。これらの関数は両方のchar(コンパイル時ではない)のセットと同じです(テキストで)。すべての関数がwchar_tまたはcharのいずれかをパラメータとして持つ場合、オーバーロードされた関数の2つのセットまたは異なる名前の関数の2つのセットでこのメソッドが実行されます(ヘッダーファイルの書き方によって異なります)。 UNICODEとANSIの両方のバージョンの関数がアプリケーションで利用可能になり、ヘッダファイルが正しく記述されていればTCHARのデフォルトバージョンも使用できます。もし私がそれについて黙想することができたら、ちょうどそう言いなさい。

+0

ラッピングトリックは、ヘッダファイルで使用することもできますが、UNICODEの再定義または再定義解除には注意が必要です。 –

+0

私はこれをやろうと考えました。 _T、TEXT、およびUNICODEをセットアップする限り、私のニーズには十分なはずです。しかし、_ttoiや_tcscpyなどを呼び出そうとすると、それ以前に_mbsやstrのように定義されていないのでしょうか?CRTヘッダーは#pragma onceを使用しませんか? – Mordachai

+0

私はCRTルーチンを使わず、WINDOWS APIを使っていました(マルチスレッドの問題を避けるため)。しかし、それはあなたが仕事とデバッグの多くを自分自身を保存する場合は、それを試してみてください。ただし、事前にコンパイルされたヘッダーを使用しないようにしてください。これは、この技法が機能しないようにするためです。ソースをラッピングするだけで、あなたの他のソースはppを使うことができます –

関連する問題