模板和字符串文字和 UNICODE
新:感谢所有帮助我的人!答案标记在下面,我在下面(q.v.)的问题中使用功能版本扩展了答案:
NEW: Thank you everyone who helped me with this! The answer is marked below, and I've expanded on the answer with a functioning version in my question, below (q.v.):
我似乎经常遇到这种情况(在更新我们的字符串实用程序库时):
I seem to be running into this situation a lot (while updating our string utilities library):
我需要一种方法来创建一个适用于 char 和 wchar_t 的模板,该模板使用各种字符串文字.目前我发现这很有挑战性,因为我不知道如何在编译时将字符串文字更改为窄字符或宽字符.
I need a way to have a template which works for both char and wchar_t, which uses various string-literals. Currently I'm finding this challenging because I don't know how to have a compile-time way to alter string literals to be narrow or wide character.
考虑一下,采用以下基于 TCHAR 的函数:
For consideration, take the following TCHAR based function:
// 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")的问题.
Immediately we have a problem with the two string literals ('"', and "%c%s%c").
如果为 CSTRING_T = CStringA, CHAR_T = char 调用上述内容,则上述文字没问题.但是如果它是为CStringW和wchar_t调用的,那我真的需要(L'"', and L"%c%c%c").
If the above is invoked for CSTRING_T = CStringA, CHAR_T = char, then the above literals are fine. But if it is invoked for CStringW and wchar_t, then I really need (L'"', and L"%c%c%c").
所以我需要一些方法来做类似的事情:
So I need some way to do something like:
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(string-or-character-literal) 实际上根据 CHAR_T 生成 L"string" 或 "string"?
And that's where I am lost: What in the world can I do to make Literal(string-or-character-literal) that actually results in L"string" or "string" depending on CHAR_T?
有超过一百个函数,其中许多更复杂,其中包含更多字符串文字,需要同时用于窄字符串和宽字符串.除了复制每个这样的函数,然后将每个函数编辑为宽或窄,肯定有一种技术可以允许单个定义随 CHAR_T 变化吗?
There are over a hundred functions, many of them more complex with more string-literals in them, that need to be available both for narrow and wide strings. Short of copying every such function and then editing each one to either be wide or narrow, surely there is a technique that would allow a single definition that varies by CHAR_T?
我正在回答 Mark Ransom 提供的混合宏 + 模板,但我想包含一个更完整的解决方案(对于任何关心的人),所以这里是:
I'm giving the answer to the hybrid macro + template that Mark Ransom supplied, but I wanted to include a more complete solution (for anyone who cared), so here it is:
// 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<> 结合使用的编写模板更容易/更好地阅读 &领悟:
In addition, I created some helpers to make writing templates that utilized this concept in conjunction with CStringT<> a bit easier / nicer to read & comprehend:
// 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 种类的模板.例如:
With the above, it is possible to write templates which generate the correct CString variety based on CStringT<> or char/wchar_t arguments. For example:
// 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;
}
推荐答案
概念是使用宏来生成两种形式的文字,char
和wchar_t
,然后让模板函数选择适合上下文的模板函数.
The concept is to use a macro to generate both forms of the literal, char
and wchar_t
, then let a template function choose which one is appropriate for the context.
请记住,模板函数实际上不会生成任何代码,除非您有其他代码调用它们.大多数时候这无关紧要,但对于图书馆来说却是这样.
Remember that template functions don't actually generate any code until you have other code that makes a call to them. Most of the time this doesn't matter, but it would for a library.
此代码未经测试,但我相信它会起作用.
This code is untested, but I believe it will work.
#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);
}
相关文章