事情起因
作为一名喜欢 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++ 编程指南

