How do I convert from _TCHAR * to char * when using C++ variable-length args?
Asked Answered
R

4

8

We need to pass a format _TCHAR * string, and a number of char * strings into a function with variable-length args:

inline void FooBar(const _TCHAR *szFmt, const char *cArgs, ...) {
  //...
}

So it can be called like so:

char *foo = "foo";
char *bar = "bar";
LogToFileA(_T("Test %s %s"), foo, bar);

Obviously a simple fix would be to use _TCHAR instead of char, but we don't have that luxury unfortunately.

We need to use this with va_start, etc so we can format a string:

va_list args;
_TCHAR szBuf[BUFFER_MED_SIZE];

va_start(args, cArgs);
_vstprintf_s(szBuf, BUFFER_MED_SIZE, szFmt, args);
va_end(args);

Unfortunately we cannot use this because it give us this error:

Unhandled exception at 0x6a0d7f4f (msvcr90d.dll) in foobar.exe:
0xC0000005: Access violation reading location 0x2d86fead.

I'm thinking we need to convert our char * to _TCHAR * - but how?

Roband answered 23/10, 2009 at 12:49 Comment(5)
if you have the luxury of using a wchar* instead of a char*, i think that would fix you problemTelemetry
Hmm, unfortunately not - the current system is a mish-mash of char* and _TCHAR*Roband
thats too bad... is efficiency an issue? if not, take a look at my answer, it worked for meTelemetry
Why is a ...A() function allowing TCHAR parameters to being with? That only makes sense if there is a cooresponding ...W() function for Unicode.Clardy
Remy, maybe you could take a look at my answer, and suggest how I should improve it? Baring in mind that the function params cannot be altered.Roband
C
3

Use %hs or %hS instead of %s. That will force the parameters to be interpretted as char* in both Ansi and Unicode versions of printf()-style functions, ie:

inline void LogToFile(const _TCHAR *szFmt, ...)
{  
  va_list args;
  TCHAR szBuf[BUFFER_MED_SIZE];

  va_start(args, szFmt);
  _vstprintf_s(szBuf, BUFFER_MED_SIZE, szFmt, args);
  va_end(args);
}  

{
  char *foo = "foo"; 
  char *bar = "bar"; 
  LogToFile(_T("Test %hs %hs"), foo, bar); 
}
Clardy answered 23/10, 2009 at 23:53 Comment(0)
B
2

Usually it looks like the following:

char *foo = "foo";
char *bar = "bar";
#ifdef UNICODE
LogToFileW( L"Test %S %S", foo, bar); // big S
#else
LogToFileA( "Test %s %s", foo, bar);
#endif

Your question is not completely clear. How your function is implemented and how do you use it?

Bromal answered 23/10, 2009 at 13:16 Comment(1)
Use %hs or %hS instead of %s and %S. That will force char* in both Ansi and Unicode. Then you can remove the check for UNICODE.Clardy
R
1

Here was my solution - I welcome suggestions for improvement!

inline void FooBar(const _TCHAR *szFmt, const char *cArgs, ...) {

    va_list args;
    _TCHAR szBuf[BUFFER_MED_SIZE];

    // Count the number of arguments in the format string.
    const _TCHAR *at = _tcschr(szFmt, '%');
    int argCount = 0;
    while(at) {
        argCount++;
        at = _tcschr(at + 1, '%');
    }

    CA2W *ca2wArr[100];
    LPWSTR szArgs[100];
    va_start(args, cArgs);
    for (int i = 1; i < argCount + 1; i++) {
        CA2W *ca2w = new CA2W(cArgs);
        szArgs[i] = ca2w->m_psz;
        ca2wArr[i] = ca2w;
        cArgs = va_arg(args, const char *);
    }
    va_end(args);

    // Use the new array we just created (skips over first element).
    va_start(args, szArgs[0]);
    _vstprintf_s(szBuf, BUFFER_MED_SIZE, szFmt, args);
    va_end(args);

    // Free up memory used by CA2W objects.
    for (int i = 1; i < argCount + 1; i++) {
        delete ca2wArr[i];
    }

    // ... snip ... - code that uses szBuf
}
Roband answered 24/10, 2009 at 10:18 Comment(2)
Your loop to count arguments does not allow for "%%" literals, and your use of va_arg() does not allow for parameters that are larger than 32-bit. Besides, since _vstprintf_s() already supports both Ansi and Unicode, there is no need for such elaborate code to begin with. Was there a problem with the code I gave you earlier?Clardy
Ah you're correct about the counter. Regarding your code, yes this would work fine I assume, and we're going to use this for NEW code that we implement, however for existing (flawed) usages we must use the temporary hack function in my answer.Roband
T
0

this is something I have used before to convert a TCHAR to char, hope it helps, although I wasn't really looking for optimization, so it's not the fastest way.. but it worked!

    TCHAR tmp[255];
::GetWindowText(hwnd, tmp, 255);
std::wstring s = tmp;

//convert from wchar to char
const wchar_t* wstr = s.c_str();
size_t wlen = wcslen(wstr) + 1;
char newchar[100];
size_t convertedChars = 0;
wcstombs_s(&convertedChars, newchar, wlen, wstr, _TRUNCATE);
Telemetry answered 23/10, 2009 at 13:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.