事情起因
作为一名喜欢
PaperMC
控制台样式的 MC 玩家,我在尝试用 C++ 来还原一些
PaperMC
当中的控制台样式。我的
KlyLogger
就仿照了
PaperMC
的样式。
可是光有输出模仿出来了,输入还没有,这也不够啊?奈何 C++ 里没有现成的
JLine
移植版,而我又不想再自己造轮子了
(其实是技术不到位)
,那我也只好在互联网上翻翻看咯。好在,我在 GitHub 上翻到了
replxx
这个库来处理用户输入,还搜出来了 C++ 版本的 brigadier:
brigadier-plus-plus
(其 README 里写明灵感来源于 Mojang 的 brigadier),但人家又使用的不是宽字符串,在遇到命令解析异常时抛出的错误信息可能就把一个完整的字截断,导致字符串无法解码 (如下图)。
于是,便有了接下来的故事…
大刀阔斧的“升级”
在我打开 brigadier.hpp 这个头文件准备开始动刀时,猛然发现这家伙竟然有足足三千行,这我要是挨个挨个改得改到什么时候啊?所以… Ctrl-H!直接查找
std::string
全部替换为
std::wstring
!再把
char
全部替换为
wchar_t
!再根据编译时的报错做点小调整!堪比完…美…??
然而当我兴致冲冲运行代码时,却发现… 我的高亮和 Tab 补充怎么炸了???
| 改动之前的效果 | 改动之后的效果 |
|---|---|
|
|
该死的“贪吃虫”
历经千辛万苦,终于在检查
brigadier::StringReader
构造函数接收的字符串时发现了问题所在:
std::wstring_view
这家伙居然把命令的第一个字符给吞了!
解决方式简单粗暴——弃用
std::wstring_view
,换回
std::wstring
!性能?Who cares?能跑起来才是王道!
贪吃虫的进食原理
“为什么会这样?”——我也很想知道。于是我开始查阅
string_view
相关的内容,这才恍然大悟:
原来,
std::wstring_view
本质上是个”视觉小偷”,它只负责看,不负责管饭:
1 |
std::wstring_view viewer; |
问题核心:
std::wstring_view
只是个轻量级的”视图”,它不拥有实际的数据。当原始字符串的生命周期结束时,
std::wstring_view
还在那傻乎乎地指着已经释放的内存。
所以根本不是它故意”吞”字,而是整个字符串都处于”薛定谔的猫”状态——你永远不知道读取时看到的是什么!
(可又为什么之前的
std::string_view
就不会吞字呢…也许只是运气好吧?)
(我说到底也只是个小白,哪懂这些复杂的东西…)
参考文本
标准库头文件
C++std::string_view完全指南:高性能字符串处理详解 · C++ 编程指南