c++ - 不一致的字符串/wchar 内容取决于代码的位置?
问题描述
我有 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;
)
在前两种情况下,我得到如下结果:
而不是预期的(案例3):
看起来有一个指针问题,但代码看起来是正确的,尤其是值参数 ( long long
)。我错过了什么?
更新。使用Omnifariousstatic
提议的局部变量,我在 v1 和 v3 中得到了不一致的结果(当一个接一个地使用时):
解决方案
解开这一切有点棘手,但是,这里肯定有一个问题。这段代码:
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 上的这个版本应该可以完美运行。看看最后一个它是如何使用的例子。
推荐阅读
- excel - 解构字符串并重新格式化
- javascript - 不推荐使用 Rxjs toPromise()
- laravel - Laravel:由于内存不足,无法安装干预/图像
- flutter - Flutter web view windows桌面支持
- javascript - window.location.search 或 window.location.hash 在开发环境中是空的吗?
- html - 如何使 ::after 和 ::before 高度相等并对齐?
- php - 在php中对具有经度和纬度的多维数组进行排序
- delphi - 从 LM75 I2C 读取温度
- android - 如何在 Flutter listview 流生成器中停止自动滚动?
- python - Interactive Brokers API - 如何断开 IB Gateway 的现有连接并使用 Python 建立新连接?