Ответ 1
Много хороших рекомендаций выше, которые будут работать в большинстве ситуаций. В моем случае, я в конечном итоге хотел загрузить строки из ресурса, и сохранить строковые ресурсы как можно ближе к .NET String.Format, как я мог, поэтому я сделал свой собственный. Рассмотрев некоторые из вышеперечисленных реализаций для идей, результирующая реализация была довольно короткой и легкой.
Существует класс String, который в моем случае происходит от Microsoft CString, но он может быть получен из любого строкового класса. Существует также класс StringArg - это задание - взять любой тип параметра и превратить его в строку (т.е. Имитирует ToString в .NET). Если новый объект должен быть ToString'd, вы просто добавляете другой конструктор. Конструктор допускает спецификатор формата printf для форматирования по умолчанию.
Затем класс String принимает идентификатор таблицы строк для исходной строки, число параметров StringArg и, наконец, необязательный HINSTANCE (я использую большое количество DLL, любая из которых может содержать таблицу строк, поэтому это позволило мне пройти это в или использовать DLL-специфический HINSTANCE по умолчанию).
Примеры использования:
dlg.m_prompt = String(1417); //"Welcome to Stackoverflow!"
MessageBox(String(1532, m_username)); //"Hi {0}"
Как бы то ни было, для ввода требуется только идентификатор строки, но было бы тривиально добавлять входную строку вместо идентификатора строки:
CString s = String.Format("Hi {0}, you are {1} years old in Hexidecimal", m_userName, StringArg(m_age, "%0X"));
Теперь для класса StringArg, который выполняет эквивалент ToString для переменных:
class StringArg
{
StringArg(); //not implemented
StringArg(const StringArg&); //not implemented
StringArg& operator=(const StringArg&); //not implemented
public:
StringArg(LPCWSTR val);
StringArg(const CString& val);
StringArg(int val, LPCWSTR formatSpec = NULL);
StringArg(size_t val, LPCWSTR formatSpec = NULL);
StringArg(WORD val, LPCWSTR formatSpec = NULL);
StringArg(DWORD val, LPCWSTR formatSpec = NULL);
StringArg(__int64 val, LPCWSTR formatSpec = NULL);
StringArg(double val, LPCWSTR formatSpec = NULL);
CString ToString() const;
private:
CString m_strVal;
};
extern HINSTANCE GetModuleHInst(); //every DLL implements this for getting it own HINSTANCE -- scenarios with a single resource DLL wouldn't need this
Для класса String существует множество функций-членов и конструкторов, которые принимают до 10 аргументов. В конечном итоге они называют CentralFormat, который выполняет настоящую работу.
class String : public CString
{
public:
String() { }
String(WORD stringTableID, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, hInst); }
String(WORD stringTableID, const StringArg& arg1, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, hInst); }
String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, hInst); }
String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, hInst); }
String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, hInst); }
String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, hInst); }
String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, arg6, hInst); }
String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, hInst); }
String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, hInst); }
String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, const StringArg& arg9, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, hInst); }
CString& Format(WORD stringTableID, HINSTANCE hInst = GetModuleHInst());
CString& Format(WORD stringTableID, const StringArg& arg1, HINSTANCE hInst = GetModuleHInst());
CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, HINSTANCE hInst = GetModuleHInst());
CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, HINSTANCE hInst = GetModuleHInst());
CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, HINSTANCE hInst = GetModuleHInst());
CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, HINSTANCE hInst = GetModuleHInst());
CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, HINSTANCE hInst = GetModuleHInst());
CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, HINSTANCE hInst = GetModuleHInst());
CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, HINSTANCE hInst = GetModuleHInst());
CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, const StringArg& arg9, HINSTANCE hInst = GetModuleHInst());
private:
void CentralFormat(WORD stringTableID, std::vector<const StringArg*>& args, HINSTANCE hInst);
};
Наконец, реализация (надеюсь, это нормально, чтобы опубликовать это много на StackOverflow, хотя основная часть его очень проста):
StringArg::StringArg(LPCWSTR val)
{
m_strVal = val;
}
StringArg::StringArg(const CString& val)
{
m_strVal = (LPCWSTR)val;
}
StringArg::StringArg(int val, LPCWSTR formatSpec)
{
if(NULL == formatSpec)
formatSpec = L"%d"; //GLOK
m_strVal.Format(formatSpec, val);
}
StringArg::StringArg(size_t val, LPCWSTR formatSpec)
{
if(NULL == formatSpec)
formatSpec = L"%u"; //GLOK
m_strVal.Format(formatSpec, val);
}
StringArg::StringArg(WORD val, LPCWSTR formatSpec)
{
if(NULL == formatSpec)
formatSpec = L"%u"; //GLOK
m_strVal.Format(formatSpec, val);
}
StringArg::StringArg(DWORD val, LPCWSTR formatSpec)
{
if(NULL == formatSpec)
formatSpec = L"%u"; //GLOK
m_strVal.Format(formatSpec, val);
}
StringArg::StringArg(__int64 val, LPCWSTR formatSpec)
{
if(NULL == formatSpec)
formatSpec = L"%I64d"; //GLOK
m_strVal.Format(formatSpec, val);
}
StringArg::StringArg(double val, LPCWSTR formatSpec)
{
if(NULL == formatSpec)
formatSpec = L"%f"; //GLOK
m_strVal.Format(formatSpec, val);
}
CString StringArg::ToString() const
{
return m_strVal;
}
void String::CentralFormat(WORD stringTableID, std::vector<const StringArg*>& args, HINSTANCE hInst)
{
size_t argsCount = args.size();
_ASSERT(argsCount < 10); //code below assumes a single character position indicator
CString tmp;
HINSTANCE hOld = AfxGetResourceHandle();
AfxSetResourceHandle(hInst);
BOOL b = tmp.LoadString(stringTableID);
AfxSetResourceHandle(hOld);
if(FALSE == b)
{
#ifdef _DEBUG
//missing string resource, or more likely a bad stringID was used -- tell someone!!
CString s;
s.Format(L"StringID %d could not be found! %s", stringTableID, hInst == ghCommonHInst ? L"CommonHInst was passed in" : L"CommonHInst was NOT passed in"); //GLOK
::MessageBeep(MB_ICONHAND);
::MessageBeep(MB_ICONEXCLAMATION);
::MessageBeep(MB_ICONHAND);
_ASSERT(0);
::MessageBox(NULL, s, L"DEBUG Error - Inform Development", MB_ICONSTOP | MB_OK | MB_SERVICE_NOTIFICATION); //GLOK
}
#endif //_DEBUG
CString::Format(L"(???+%d)", stringTableID); //GLOK
return;
}
//check for the degenerate case
if(0 == argsCount)
{
CString::operator=(tmp);
return;
}
GetBuffer(tmp.GetLength() * 3); //pre-allocate space
ReleaseBuffer(0);
LPCWSTR pStr = tmp;
while(L'\0' != *pStr)
{
bool bSkip = false;
if(L'{' == *pStr)
{
//is this an incoming string position?
//we only support 10 args, so the next char must be a number
if(wcschr(L"0123456789", *(pStr + 1))) //GLOK
{
if(L'}' == *(pStr + 2)) //and closing brace?
{
bSkip = true;
//this is a replacement
size_t index = *(pStr + 1) - L'0';
_ASSERT(index < argsCount);
_ASSERT(index >= 0);
if((index >= 0) && (index < argsCount))
CString::operator+=(args[index]->ToString());
else
{
//bad positional index
CString msg;
msg.Format(L"(??-%d)", index); //GLOK
CString::operator+=(msg);
}
pStr += 2; //get past the two extra characters that we skipped ahead and peeked at
}
}
}
if(false == bSkip)
CString::operator+=(*pStr);
pStr++;
}
}
CString& String::Format(WORD stringTableID, HINSTANCE hInst)
{
std::vector<const StringArg*> args;
CentralFormat(stringTableID, args, hInst);
return *this;
}
CString& String::Format(WORD stringTableID, const StringArg& arg1, HINSTANCE hInst)
{
std::vector<const StringArg*> args;
args.push_back(&arg1);
CentralFormat(stringTableID, args, hInst);
return *this;
}
CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, HINSTANCE hInst)
{
std::vector<const StringArg*> args;
args.push_back(&arg1);
args.push_back(&arg2);
CentralFormat(stringTableID, args, hInst);
return *this;
}
CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, HINSTANCE hInst)
{
std::vector<const StringArg*> args;
args.push_back(&arg1);
args.push_back(&arg2);
args.push_back(&arg3);
CentralFormat(stringTableID, args, hInst);
return *this;
}
CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, HINSTANCE hInst)
{
std::vector<const StringArg*> args;
args.push_back(&arg1);
args.push_back(&arg2);
args.push_back(&arg3);
args.push_back(&arg4);
CentralFormat(stringTableID, args, hInst);
return *this;
}
CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, HINSTANCE hInst)
{
std::vector<const StringArg*> args;
args.push_back(&arg1);
args.push_back(&arg2);
args.push_back(&arg3);
args.push_back(&arg4);
args.push_back(&arg5);
CentralFormat(stringTableID, args, hInst);
return *this;
}
CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, HINSTANCE hInst)
{
std::vector<const StringArg*> args;
args.push_back(&arg1);
args.push_back(&arg2);
args.push_back(&arg3);
args.push_back(&arg4);
args.push_back(&arg5);
args.push_back(&arg6);
CentralFormat(stringTableID, args, hInst);
return *this;
}
CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, HINSTANCE hInst)
{
std::vector<const StringArg*> args;
args.push_back(&arg1);
args.push_back(&arg2);
args.push_back(&arg3);
args.push_back(&arg4);
args.push_back(&arg5);
args.push_back(&arg6);
args.push_back(&arg7);
CentralFormat(stringTableID, args, hInst);
return *this;
}
CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, HINSTANCE hInst)
{
std::vector<const StringArg*> args;
args.push_back(&arg1);
args.push_back(&arg2);
args.push_back(&arg3);
args.push_back(&arg4);
args.push_back(&arg5);
args.push_back(&arg6);
args.push_back(&arg7);
args.push_back(&arg8);
CentralFormat(stringTableID, args, hInst);
return *this;
}
CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, const StringArg& arg9, HINSTANCE hInst)
{
std::vector<const StringArg*> args;
args.push_back(&arg1);
args.push_back(&arg2);
args.push_back(&arg3);
args.push_back(&arg4);
args.push_back(&arg5);
args.push_back(&arg6);
args.push_back(&arg7);
args.push_back(&arg8);
args.push_back(&arg9);
CentralFormat(stringTableID, args, hInst);
return *this;
}