代码之家  ›  专栏  ›  技术社区  ›  Mordachai

模板、字符串文本和UNICODE

  •  4
  • Mordachai  · 技术社区  · 14 年前

    新:谢谢所有帮助我的人!答案如下所示,我在下面的问题(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)。

    所以我需要一些方法来做如下事情:

    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“字符串”或“字符串”取决于字符?

    编辑:有100多个函数,其中许多函数更复杂,包含更多的字符串文字,需要对窄字符串和宽字符串都可用。除了复制每个这样的函数,然后将每个函数编辑为宽或窄之外,肯定有一种技术允许单个定义随字符而变化?


    我给出了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一起编写的模板更易于阅读和理解:

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

    使用上面的方法,可以编写基于CString t<>或char/wchar_t参数生成正确CString类型的模板。例如:

    // 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 回复  |  直到 14 年前
        1
  •  5
  •   Mark Ransom    7 年前

    其概念是使用宏生成文本的两种形式, char wchar_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);
    }
    
        2
  •  1
  •   Puppy    14 年前

    这篇文章是我个人的一点点天才。

    #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,但不要屏住呼吸。

    编辑:你可以这样做,imo是最好的解决方案,但仍然需要一些麻烦。

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

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

        3
  •  0
  •   In silico    14 年前

    考虑到只有两种方法可以使用模板,因此不需要使用模板 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");
    } 
    
        4
  •  0
  •   engf-010    14 年前

    我也有类似的情况。我已经做了一个源代码文件和一个头文件(当然)我排除了建设。然后通过include指令创建了另外两个包含原始源的源文件。在一个文件中,我在include之前定义UNICODE(如果尚未定义)。在另一个文件中,I#undef UNICODE(如果已定义)。源文件包含一些静态结构和一些函数,这两组char(编译时不是)在文本中是相同的。如果每个函数都有wchar_t或char作为参数,则此方法将导致两组重载函数或两组不同名称的函数(取决于头文件的写入方式,以tchar.h为例)。现在,UNICODE和ANSI版本的函数都可用于应用程序,如果头文件正确写入,则TCHAR的默认版本也是如此。如果你想让我查一下,就这么说吧。

        5
  •  -1
  •   John Dibling    14 年前

    我相信你想要 TEXT MFC宏:

    TCHAR* psz = TEXT("Hello, generic string");
    
        6
  •  -1
  •   Rajivji    14 年前

    可以对MarkQuoted使用模板部分专用化,并根据类型使用quote。

        7
  •  -1
  •   Wyatt Anderson    14 年前

    好吧,如果你真的想模板化这个,我想我能想到的最好的东西是一个模板化的类,它存储你的文本,基于 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 在您的非专用但模板化的函数中。我想有点混乱,但它的好处是让函数逻辑保持不重叠,并为您提供模板化的字符串文本。