模板和字符串文字和UNICODE

时间:2010-11-23 22:32:51

标签: c++ visual-studio templates unicode mfc

新:感谢所有帮助过我的人!答案在下面标出,我在我的问题中扩展了答案,功能正常,在下面(q.v.):


我似乎经常遇到这种情况(在更新我们的字符串实用程序库时):

我需要一种方法来获得一个适用于char和wchar_t的模板,它使用各种字符串文字。目前我发现这很有挑战性,因为我不知道如何使用编译时方式将字符串文字更改为窄字符或宽字符。

考虑一下,请采用以下基于TCHAR的功能:

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

我想改为模板:

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

我们立即遇到两个字符串文字('“'和”%c%s%c“)的问题。

如果对CSTRING_T = CStringA,CHAR_T = char调用上述内容,那么上面的文字就可以了。但如果为CStringW和wchar_t调用它,那么我真的需要(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);
}

这就是我迷失的地方:我可以做些什么来制作Literal(字符串或字符文字)实际上导致L“字符串”或“字符串”取决于CHAR_T?

编辑:有超过一百个函数,其中许多函数更复杂,其中包含更多的字符串文字,这些函数需要同时适用于窄字符串和宽字符串。如果没有复制每个这样的函数,然后将每个函数编辑为宽或窄,肯定有一种技术允许单个定义因CHAR_T而异吗?


我正在给出Mark Ransom提供的混合宏+模板的答案,但我想要包含一个更完整的解决方案(对于任何关心的人),所以这里是:

// 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&lt;&gt;结合使用。更简单/更好阅读&amp;理解:

// 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&lt;&gt;生成正确CString种类的模板。或char / wchar_t参数。例如:

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

7 个答案:

答案 0 :(得分: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);
}

答案 1 :(得分: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' };
}

在带有可变参数模板的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();
}

你必须遍历每个文字并使它成为结构的静态成员,然后引用它,但它的效果要好得多。

答案 2 :(得分:0)

考虑到只有两种方法可以使用MakeQuoted(),您不需要使用这样的模板。您可以将函数重载用于相同的目的:

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

当然,这是最简单的方法,无需使用宏,假设您是使用字符串实用程序库尝试基于模板的解决方案的理由。

您可以分解长而复杂功能的常用功能:

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

答案 3 :(得分:0)

我有类似的情况。我已经制作了1个源代码文件和一个头文件(当然)。然后通过#include指令创建了2个包含原始源的其他源文件。在一个文件中,我在include之前#define UNICODE(如果尚未定义)。在另一个文件中我#undef UNICODE(如果已定义)。源文件包含一些静态结构和许多函数,这两个函数对于两组char都是相同的(在文本中)(不是在编译时)。如果每个函数都有wchar_t或char作为参数,则在2组重载函数或2组不同命名函数中生成此方法(取决于头文件的编写方式,以tchar.h为例)。现在,UNICODE和ANSI版本的函数都可用于应用程序,如果正确写入头文件,也是TCHAR的默认版本。如果你希望我能够就此进行阐述,那就这么说吧。

答案 4 :(得分:-1)

我相信你想要TEXT MFC宏:

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

答案 5 :(得分:-1)

好的,所以,如果你真的想要模板化这个,我认为我能够想出的最好的东西是一个模板化的类,它存储你的文字,基于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。有点凌乱,我猜,但它的好处是让您的函数逻辑不重复,并为您提供模板化的字符串文字。

答案 6 :(得分:-1)

您可以对MarkQuoted使用模板部分特化,并根据类型进行引用。