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.):
You can use template partial specialization for MarkQuoted, and quote based on the type.
This piece is my own personal tiny little bit of genius.
#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;
}
When you use alloca in a default argument, it's actually allocated off the caller's stack, allowing you to return arrays without resorting to the heap. No dynamic allocation, no memory freeing. _alloca is a CRT function provided by MSVC, so I don't give any portability guarantees - but if you're using ATL that's likely no problem anyway. Of course, this also means that the pointer cannot be held past the calling function, but it should suffice for temporary uses like format strings. There are also some caveats to do with exception handling that you are unlikely to come across (check MSDN for details), and of course, it will only work for characters which have the same binary representation, which to my knowledge is every character you can put in a narrow string literal. I appreciate that this only solves a subset of the actual problems you may have encountered, but it's a far superior solution to that specific subset than the macro, or specifying every literal twice, etc.
You can also use the definitely uglier but more behaviourally consistent aggregate initialization.
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 with variadic templates, it may be possible for this solution to not suck. I'm CLOSE to a better solution which is C++03, but don't hold your breath.
Edit: You can do this, which imo is the best solution, still involves some messing around.
#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();
}
You have to go through every literal and make it a static member of a struct, then reference it, but it works much better.
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);
}
I believe you want the TEXT
MFC macro:
TCHAR* psz = TEXT("Hello, generic string");
You don't need to use templates for something like this, considering there's only two ways to use MakeQuoted()
. You can use function overloading for the same purpose:
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);
}
Surely this is the easiest way to do it without having to use macros, assuming that's your reason for attempting a template-based solution with your string utilities library.
You can factor out common functionality for long and complicated functions:
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");
}
I have a similar situation. I have made 1 source-code file and a header-file (of course) which I exclude from building. Then created 2 other source-files which contain the original source via an #include directive. In one file I #define UNICODE (if not already defined) before the include. In the other file I #undef UNICODE (if defined). the source file contains a few static structures and a number of functions ,which are identical (in text) for both sets of char (not when compiled). If every function has either wchar_t or char as a parameter results this method in 2 sets of overloaded functions or 2 sets of differently named functions (depends on how the header file is written ,take tchar.h as example). Now both UNICODE and ANSI versions of the functions are available for an application and if the header-file is correctly written also default version for TCHAR. If you wish I can ellaborated on it ,just say so.