c++ - 字符串/wchar 内容不一致取决于代码的位置?

标签 c++ visual-studio c++11

我有 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)。我缺少什么?

更新。按照 Omnifarious 的建议,对局部变量使用 static,我在 v1 和 v3 中得到了不一致的结果(当一个接一个地使用时):

enter image description here

最佳答案

理清所有这些问题有点棘手,但是,这里肯定有一个问题。这段代码:

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)...));
}

This version I put on Godbolt应该完美地工作。查看最后的示例,了解如何使用它。

关于c++ - 字符串/wchar 内容不一致取决于代码的位置?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54043665/

相关文章:

python - Visual Basic 开发人员想要编写 Linux 应用程序

c++ - 在 C++ 中鸭子打字(通过其非类型模板参数的值专门化模板函数)

c++ - 在 Visual Studio 或 GCC 中的 c++ 中使用表情符号作为标识符名称

visual-studio - Visual Studio 模块负载限制?

c++ - 使用 MinGW-w64 为 Windows 编译 LAPACK

c++ - 为什么此代码段在 VS 2013 中不起作用?

c++ - static_cast 和显式转换运算符

c++ - 从 std::set 中提取仅 move 类型

c++ - std::is_trivially_copyable 错了吗?

c++ - 一旦代码进入另一个函数如何保留值