Множество хороших рекомендаций выше, которые будут работать в большинстве ситуаций. В моем случае, я в конечном итоге хотел загрузить строки из ресурса И сохранить строковые ресурсы как можно ближе к .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's 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;
}