Как преобразовать _TCHAR * в char * при использовании аргументов переменной длины С++?

Нам нужно передать строку формата _TCHAR * и количество строк char * в функцию с аргументами переменной длины:

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

Так что это можно назвать так:

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

Очевидно, что простым решением будет использование _TCHAR вместо char, но, к сожалению, у нас нет такой роскоши.

Нам нужно использовать это с va_start и т. д., чтобы мы могли отформатировать строку:

va_list args;
_TCHAR szBuf[BUFFER_MED_SIZE];

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

К сожалению, мы не можем использовать это, потому что это дает нам эту ошибку:

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

Я думаю, нам нужно преобразовать наш char * в _TCHAR * - но как?


person Nick Bolton    schedule 23.10.2009    source источник
comment
если у вас есть возможность использовать wchar* вместо char*, я думаю, это решит вашу проблему   -  person David Menard    schedule 23.10.2009
comment
Хм, к сожалению, нет - текущая система представляет собой мешанину из char* и _TCHAR*.   -  person Nick Bolton    schedule 23.10.2009
comment
это очень плохо ... является ли эффективность проблемой? если нет, взгляните на мой ответ, у меня это сработало   -  person David Menard    schedule 23.10.2009
comment
Почему функция ...A() позволяет использовать параметры TCHAR? Это имеет смысл только в том случае, если есть соответствующая функция ...W() для Unicode.   -  person Remy Lebeau    schedule 24.10.2009
comment
Реми, может быть, ты мог бы взглянуть на мой ответ и предложить, как мне его улучшить? Имея в виду, что параметры функции не могут быть изменены.   -  person Nick Bolton    schedule 24.10.2009


Ответы (4)


Используйте %hs или %hS вместо %s. Это заставит интерпретировать параметры как char* как в Ansi, так и в Unicode версиях функций в стиле printf(), т.е.:

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); 
}
person Remy Lebeau    schedule 23.10.2009

Обычно это выглядит следующим образом:

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

Ваш вопрос не совсем ясен. Как реализована ваша функция и как вы ее используете?

person Kirill V. Lyadvinsky    schedule 23.10.2009
comment
Используйте %hs или %hS вместо %s и %S. Это заставит использовать char* как в Ansi, так и в Unicode. Потом можно убрать проверку на UNICODE. - person Remy Lebeau; 24.10.2009

Вот мое решение - я приветствую предложения по улучшению!

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
}
person Nick Bolton    schedule 24.10.2009
comment
Ваш цикл для подсчета аргументов не допускает использование литералов %%, а использование вами va_arg() не позволяет использовать параметры, размер которых превышает 32 бита. Кроме того, поскольку _vstprintf_s() уже поддерживает и Ansi, и Unicode, для начала нет необходимости в таком сложном коде. Была ли проблема с кодом, который я дал вам ранее? - person Remy Lebeau; 28.10.2009
comment
Ах, вы правы насчет счетчика. Что касается вашего кода, да, я полагаю, это будет работать нормально, и мы собираемся использовать это для НОВОГО кода, который мы реализуем, однако для существующих (несовершенных) применений мы должны использовать временную функцию взлома в моем ответе. - person Nick Bolton; 28.10.2009

это то, что я использовал раньше, чтобы преобразовать TCHAR в char, надеюсь, это поможет, хотя на самом деле я не искал оптимизацию, так что это не самый быстрый способ ... но это сработало!

    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);
person David Menard    schedule 23.10.2009