Use format strings that contain %1, %2 etc. instead of %d, %s etc. - Linux, C++
As a follow-up of this question (Message com开发者_如何学Cpiler replacement in Linux gcc), I have the following problem:
When using MC.exe on Windows for compiling and generating messages, within the C++ code I call FormatMessage, which retrieves the message and uses the va_list *Arguments
parameter to send the varied message arguments.
For example:
MessageId=1
Severity=Error
SymbolicName=MULTIPLE_MESSAGE_OCCURED
Language=English
message %1 occured %2 times.
.
C++ code:
void GetMsg(unsigned int errCode, wstring& message,unsigned int paramNumber, ...)
{
HLOCAL msg;
DWORD ret;
LANGID lang = GetUserDefaultLangID();
try
{
va_list argList;
va_start( argList, paramNumber );
const TCHAR* dll = L"MyDll.dll";
_hModule = GetModuleHandle(dll);
ret =::FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE|FORMAT_MESSAGE_IGNORE_INSERTS,
_hModule,
errCode,
lang,
(LPTSTR) &msg,
0,
&argList );
if ( 0 != ret )
{
unsigned int count = 0 ;
message = msg;
if (paramNumber>0)
{
wstring::const_iterator iter;
for (iter = message.begin();iter!=message.end();iter++)
{
wchar_t xx = *iter;
if (xx ==L'%')
count++;
}
}
if ((count == paramNumber) && (count >0))
{
::LocalFree( msg );
ret =::FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_HMODULE,
_hModule,
errCode,
GetUserDefaultLangID(),
(LPTSTR) &msg,
0,
&argList );
}
else if (count != paramNumber)
{
wstringstream tmp;
wstring messNumber;
tmp << (errCode & 0xFFFF);
tmp >> messNumber;
message = message +L"("+ messNumber + L"). Bad Format String. ";
}
}
::LocalFree( msg );
}
catch (...)
{
message << L"last error: " << GetLastError();
}
va_end( argList );
}
Caller code:
wstring message;
GetMsg(MULTIPLE_MESSAGE_OCCURED, message,2, "Error message", 5);
Now, I wrote a simple script to generate a .msg file from the .mc file, and then I use gencat to generate a catalog from it.
But is there a way to use the formatted strings as they contain %1, %2, etc. and NOT the general (%d, %s...) format?
Please note, that the solution has to be generic enough for each possible message with each posible types\ arguments order...
Is it possible at all?
Thank you.
First of all functions like printf support positioned format:
printf("%2$s, %1$d", salary, name);
For C++, beside the C solution there is a boost::format
library:
std::cout << boost::format("%2%, %1%") % salary % name;
Also if you are moving software to Linux I would suggest use "different" approach for localization: use either gettext or boost.locale library.
And instead of this:
wstring message;
GetMsg(MULTIPLE_MESSAGE_OCCURED, message,2, "Error message", 5);
Use :
C/gettext:
snprintf(msg,sizeof(msg),gettext("This is the message to %1$s about %2$s"),who,what);
C++/gettext:
using boost::format;
std::ostringstream ss;
ss << format(gettext("This is the message to %1% about %2%")) % who % what;
C++ using boost.locale:
using boost::locale::format;
using boost::locale::translate;
std::ostringstream ss;
ss << format(translate("This is the message to {1} about {2}")) % who % what;
The FormatMessage() function does use printf-style format specifiers; they go inside exclamation points after the %1 or whatever. A placeholder with no format specifier is equivalent to a printf "%s."
What you'd need to do would be to transform the format strings a bit; change "%1" to "%1$s", "%2!u!" to "%2$u", "%3!4.5e!" to "%3$4.5e" and so on. Basically just change the ! characters around the format specifier into a single $ preceding it, and cope with the possibility of a bare "%number".
The problem with positional parameters like %1$anytype is that it must appear in the format string for a %2$anytype to work. Cf. printf("Today, %1$s received %2$d dollars in salary\n", name, salary);
vs. printf("Today, I received %2$d dollars in salary\n", name, salary);
(boom). So it does not always work, like when the user is free to provide the format string, and decides to omit a field. In that case, a named parameter approach seems preferable. libHX for example provides such where you could use "%(SALARY) %(NAME)"
.
精彩评论