Несовместимое содержимое строки / wchar в зависимости от местоположения кода? - PullRequest
0 голосов
/ 04 января 2019

У меня 3 разных вида результатов, в зависимости от используемых мной бесплатных функций:

struct __declspec(dllexport) TimerPair final
{
    long long Time{};
    string Descr;
};

template<typename... T>
wchar_t* Message(T &&... args)
{
    wchar_t message[100];
    swprintf(message, 100, forward<T>(args)...);
    return message;
}

template<typename... T>
void LogMessage(T &&... args)
{
    Logger::WriteMessage(Message(forward<T>(args)...));
}

const wchar_t* ToWchar(string arg)
{
    std::wstring widestr = std::wstring(arg.begin(), arg.end());
    return widestr.c_str();
}

и код в модульных тестах: (o - это TimerPair структура)

// v1
LogMessage(L"%s : %.4fms\n", ToWchar(o.Descr), (float)o.Time / 1000000);

// v2
std::wstring widestr = std::wstring(o.Descr.begin(), o.Descr.end());
Logger::WriteMessage(Message(L"%s : %.4fms\n", widestr.c_str(), (float)o.Time / 1000000));

// v3
std::wstring widestr = std::wstring(o.Descr.begin(), o.Descr.end());
wchar_t message[100];
swprintf(message, 100, L"%s : %.4fms\n", widestr.c_str(), (float)o.Time / 1000000);
Logger::WriteMessage(message);

Logger::WriteMessage взят из среды модульного тестирования MSFT (using namespace Microsoft::VisualStudio::CppUnitTestFramework;)

В первых двух случаях я получаю следующие результаты:

enter image description here

вместо ожидаемого (случай 3):

enter image description here

Похоже, что есть проблема с указателем, но код выглядит корректно, особенно с аргументом значения (long long). Чего мне не хватает?

Обновление . Используя static для локальных переменных, как предложено Omnifarious , я получил противоречивые результаты как в v1, так и в v3 (при использовании одна за другой):

enter image description here

1 Ответ

0 голосов
/ 04 января 2019

Немного сложно распутать все это, но здесь определенно есть одна проблема. Этот код:

const wchar_t* ToWchar(string arg)
{
    std::wstring widestr = std::wstring(arg.begin(), arg.end());
    return widestr.c_str();
}

возвращает указатель на мертвую память. Память, на которую он возвращает указатель, освобождается при выходе из функции и widestr выходит из области видимости. Если вы изменили это на это:

const wchar_t* ToWchar(string arg)
{
    static ::std::wstring widestr;

    widestr = std::wstring(arg.begin(), arg.end());
    return widestr.c_str();
}

это может начать работать. Но тогда функция больше не будет входящей, и совершенно определенно не будет потокобезопасной.

Точно такая же ситуация для этой функции:

template<typename... T>
wchar_t* Message(T &&... args)
{
    wchar_t message[100];
    swprintf(message, 100, forward<T>(args)...);
    return message;
}

message выходит из области видимости, и вы возвращаете указатель на пространство мертвого стека. Опять же, если вы изменили функцию на это:

template<typename... T>
wchar_t* Message(T &&... args)
{
    static wchar_t message[100];
    swprintf(message, 100, forward<T>(args)...);
    return message;
}

он, вероятно, начнет работать, но опять-таки больше не будет входящим или поточно-ориентированным.

Я бы переосмыслил способ, которым вы справляетесь с этим. Для решения этой проблемы используются гарантии C ++ относительно времени жизни временных пользователей:

#include <string>
#include <cstring>

extern void fake_logger_writemessage(wchar_t const *);

template <class T>
class WCharWrapper {
 public:
    WCharWrapper() = delete;  // Make it unconstructable
};

template <>
class WCharWrapper<wchar_t const *> {
 public:
    WCharWrapper() = delete;
    explicit WCharWrapper(wchar_t const *s) : s_(s) {}

    operator wchar_t const *() const { return s_; }

 private:
    wchar_t const * const s_;
};

template <>
class WCharWrapper<::std::wstring const &> {
 public:
    WCharWrapper() = delete;
    explicit WCharWrapper(::std::wstring const &s) : s_(s) {}

    operator wchar_t const *() const { return s_.c_str(); }

 private:
    ::std::wstring const &s_;
};

template <>
class WCharWrapper<char const *> {
 public:
    WCharWrapper() = delete;
    explicit WCharWrapper(char const *s) : s_(s, s + ::std::strlen(s)) {}

    operator wchar_t const *() const { return s_.c_str(); }

 private:
    ::std::wstring const s_;
};

template <>
class WCharWrapper<::std::string const &> {
 public:
    WCharWrapper() = delete;
    explicit WCharWrapper(::std::string const &s) : s_(s.begin(), s.end()) {}

    operator wchar_t const *() const { return s_.c_str(); }

 private:
    ::std::wstring const s_;
};

template <typename T>
T widen_strings(T &&arg)
{
    return ::std::forward(arg);
}

WCharWrapper<char const *> widen_strings(char const *arg)
{
    return WCharWrapper<char const *>(arg);
}

WCharWrapper<::std::string const &> widen_strings(::std::string const &arg)
{
    return WCharWrapper<::std::string const &>(arg);
}

// Capture non-const as well, to make sure they aren't passed through unchanged.
WCharWrapper<::std::string const &> widen_strings(::std::string &arg)
{
    return WCharWrapper<::std::string const &>(arg);
}

template <typename T>
T &&widen_strings(T && arg)
{
    return arg;
}

template<typename... T>
wchar_t const *Message(wchar_t *out, T &&... args)
{
    swprintf(out, widen_strings(::std::forward<T>(args))...);
    return out;
}

template<typename... T>
void LogMessage(T &&... args)
{
    wchar_t msgbuf[100];
    fake_logger_writemessage(Message(msgbuf, ::std::forward<T>(args)...));
}

Эта версия, которую я поставил на Godbolt , должна работать отлично. Посмотрите на самый конец для примера того, как он используется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...