perf(size): 用自写整数格式化替换全部 std::to_wstring / std::to_string
背景:Code Review 报告 §10.3 / §11.5 建议「替换 std::to_wstring / std::to_string,
可去掉 gdtoa 链(≈33 KB)」。本轮把它做完,并给出这条建议的**真实边界**。
一、新增 `Utf8Util::FormatInteger*`(手写十进制展开,不经 printf)
* `FormatInteger(long long)` / `FormatIntegerUnsigned(unsigned long long)` /
`FormatIntegerNarrow(long long)`(后者供 MatLoader 拼 HDF5 成员路径)。
* 语义与 `std::to_wstring` / `std::to_string` 完全一致:`0` → `"0"`、负数带 `-`、
`LLONG_MIN` 用模 2^64 语义取绝对值(不会溢出)。
* 这样做的直接理由:预编译 libstdc++ 的 `to_string` 内部转调 `__mingw_swprintf`,
工程里只要留一处,MinGW 的 printf 引擎(`mingw_wpformat.o`)+ gdtoa 就必然被链入。
二、全仓替换(76 处 `to_wstring` + 2 处 `to_string`,20 个文件)
* `MatLoader.cpp` 的 `TrimNumber` 整数分支也改为 `FormatInteger`(它曾是产物里
**最后一个**引用 MinGW printf 的调用点)。
* 需要的地方补 `#include "Utf8Util.hpp"`(仅 `MatLoader.cpp` 一个新 include,其余由
`PlotViewInternal` / `SignalPanelInternal` / `ApplicationInternal` / `DataModelInternal`
已经包含)。
三、⚠️ 实测结论:gdtoa **去不掉**(把这件事彻底查清并写进 Makefile 注释)
* 只替换 `to_wstring`/`to_string`:**−1,024 B**(787,968 → 786,944)。
* 再叠加 `-D__USE_MINGW_ANSI_STDIO=0`(把 `swprintf` 让给系统 UCRT):**0 字节**。
* 根因(定位到文件与行):libstdc++ 的
`lib/gcc/.../include/c++/x86_64-w64-mingw32/bits/os_defines.h:48-51` **无条件**执行
`#undef __USE_MINGW_ANSI_STDIO` + `#define __USE_MINGW_ANSI_STDIO 1`
(注释:libstdc++ depends on POSIX-definitions of those functions)。
`g++ -E -dM` 实测最终值恒为 1 ⇒ **任何 C++ TU 的 `swprintf` / `strtod` 都是 MinGW 版本**,
命令行无法覆盖。
* 要真省这 33 KB 只能自己实现「浮点 → 十进制(正确舍入)」与「浮点解析的
十六进制/inf/nan 分支」,等价于重写 gdtoa。**判定:不做**(风险 >> 收益)。
* 该开关已回退,Makefile 里保留一段说明,避免后续接手者重复这两次实验。
四、验证
* 新增单元测试 `integer_replacement_matches_std`:12 个有符号样本(含 LLONG_MIN/MAX)
+ 5 个无符号样本(含 ULLONG_MAX),与 `std::to_wstring` **逐字符对照**,
并校验窄/宽版本内容一致。
* `UnitTests`:`checks=260 failed=0 cases=19`(新增 41 条断言)。
* `OffscreenChecks -all`:6 个检查全 PASS,`checks=87 failed=0`。
* `panels -mode compare`:7 个面板像素哈希与几何**逐项不变**
(整数文本从 `to_wstring` 换到手写实现后,渲染结果零差异 —— 这是最强的语义证据)。
* release 全量零 error / 零 warning;产物 787,968 → **786,944 B**。
This commit is contained in:
1 parent
abcf3e6c44
commit
34365b3c1c
23 files changed
+256
-94
No files matched your search
@@ -265,7 +265,7 @@ namespace jdi
|
||||
// 取当前未被占用的最小编号,使关闭标签页后编号可以被复用(3 开 2 关再新建回到「视图 2」)。
|
||||
for (int number = 1; number <= static_cast<int>(m_tabs.size()) + 1; ++number)
|
||||
{
|
||||
const std::wstring candidate = L"视图 " + std::to_wstring(number);
|
||||
const std::wstring candidate = L"视图 " + FormatInteger(number);
|
||||
bool used = false;
|
||||
for (const TabConfig& tab : m_tabs)
|
||||
{
|
||||
@@ -280,7 +280,7 @@ namespace jdi
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return L"视图 " + std::to_wstring(m_tabs.size() + 1);
|
||||
return L"视图 " + FormatInteger(m_tabs.size() + 1);
|
||||
}
|
||||
|
||||
void Application::Relayout()
|
||||
@@ -1879,7 +1879,7 @@ namespace jdi
|
||||
|
||||
if (loaded > 0)
|
||||
{
|
||||
SetStatusMessage(L"已加载 " + std::to_wstring(loaded) +
|
||||
SetStatusMessage(L"已加载 " + FormatInteger(loaded) +
|
||||
L" 个文件,请在左侧勾选需要查看的信号。",
|
||||
false);
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace jdi
|
||||
}
|
||||
for (int suffix = 2; suffix < 1000; ++suffix)
|
||||
{
|
||||
const std::wstring candidate = base + L" (" + std::to_wstring(suffix) + L")";
|
||||
const std::wstring candidate = base + L" (" + FormatInteger(suffix) + L")";
|
||||
if (!taken(candidate))
|
||||
{
|
||||
return candidate;
|
||||
@@ -319,7 +319,7 @@ namespace jdi
|
||||
|
||||
if (loaded > 0)
|
||||
{
|
||||
SetStatusMessage(L"已加载 " + std::to_wstring(loaded) +
|
||||
SetStatusMessage(L"已加载 " + FormatInteger(loaded) +
|
||||
L" 个文件,请在左侧勾选需要查看的信号。",
|
||||
false);
|
||||
}
|
||||
|
||||
@@ -59,14 +59,14 @@ namespace jdi
|
||||
{
|
||||
ComputeWeakPeakAdjust(source.values, static_cast<double>(request.amountPercent) / 100.0,
|
||||
request.iterations, derived_values);
|
||||
derived.name = baseName + L" (弱峰调整 " + std::to_wstring(request.amountPercent) + L"% " +
|
||||
std::to_wstring(request.iterations) + L"次迭代)";
|
||||
derived.name = baseName + L" (弱峰调整 " + FormatInteger(request.amountPercent) + L"% " +
|
||||
FormatInteger(request.iterations) + L"次迭代)";
|
||||
}
|
||||
else
|
||||
{
|
||||
ComputeMovingAverageIterated(source.values, request.points, request.iterations, derived_values);
|
||||
derived.name = baseName + L" (" + std::to_wstring(request.points) + L"点均值 " +
|
||||
std::to_wstring(request.iterations) + L"次迭代)";
|
||||
derived.name = baseName + L" (" + FormatInteger(request.points) + L"点均值 " +
|
||||
FormatInteger(request.iterations) + L"次迭代)";
|
||||
}
|
||||
|
||||
// 2. 组装新信号(外观一律用默认值:变换已经烘进采样数据,不再是"继承的指纹")。
|
||||
@@ -654,7 +654,7 @@ namespace jdi
|
||||
}
|
||||
if (built > 0)
|
||||
{
|
||||
SetStatusMessage(L"已构建 " + std::to_wstring(built) + L" 条重采样信号(频率 " +
|
||||
SetStatusMessage(L"已构建 " + FormatInteger(built) + L" 条重采样信号(频率 " +
|
||||
FormatFrequency(lastInterval > 0.0 ? 1.0 / lastInterval : 0.0) +
|
||||
L")。",
|
||||
false);
|
||||
@@ -692,7 +692,7 @@ namespace jdi
|
||||
if (built > 0)
|
||||
{
|
||||
SetStatusMessage((items.front().destructive ? L"已截取 " : L"已截取为新信号 ") +
|
||||
std::to_wstring(built) + L" 条。",
|
||||
FormatInteger(built) + L" 条。",
|
||||
false);
|
||||
}
|
||||
return built > 0;
|
||||
@@ -770,16 +770,16 @@ namespace jdi
|
||||
|
||||
if (built > 0)
|
||||
{
|
||||
std::wstring text = L"已构建 " + std::to_wstring(built) + L" 条 ";
|
||||
std::wstring text = L"已构建 " + FormatInteger(built) + L" 条 ";
|
||||
if (last.weakPeak)
|
||||
{
|
||||
text += L"弱峰调整 " + std::to_wstring(last.amountPercent) + L"% " +
|
||||
std::to_wstring(last.iterations) + L"次迭代信号。";
|
||||
text += L"弱峰调整 " + FormatInteger(last.amountPercent) + L"% " +
|
||||
FormatInteger(last.iterations) + L"次迭代信号。";
|
||||
}
|
||||
else
|
||||
{
|
||||
text += std::to_wstring(last.points) + L"点均值 " +
|
||||
std::to_wstring(last.iterations) + L"次迭代滤波信号。";
|
||||
text += FormatInteger(last.points) + L"点均值 " +
|
||||
FormatInteger(last.iterations) + L"次迭代滤波信号。";
|
||||
}
|
||||
SetStatusMessage(text, false);
|
||||
}
|
||||
|
||||
@@ -76,10 +76,10 @@ namespace jdi
|
||||
timeNames += axis.name;
|
||||
}
|
||||
std::wstring message =
|
||||
L"已加载 组" + std::to_wstring(lastGroupIndex + 1) + L"|" + loadedGroup->name +
|
||||
L"已加载 组" + FormatInteger(lastGroupIndex + 1) + L"|" + loadedGroup->name +
|
||||
L"(" + FormatCount(static_cast<unsigned long long>(rowCount)) + L" 行采样," +
|
||||
std::to_wstring(loadedGroup->signals.size()) + L" 条信号," +
|
||||
std::to_wstring(loadedGroup->axes.size()) + L" 条时间轴:" + timeNames + L")";
|
||||
FormatInteger(loadedGroup->signals.size()) + L" 条信号," +
|
||||
FormatInteger(loadedGroup->axes.size()) + L" 条时间轴:" + timeNames + L")";
|
||||
if (anyNonMonotonic)
|
||||
{
|
||||
message += L" 提示:时间列非单调递增,光标取值使用最近邻策略。";
|
||||
@@ -280,7 +280,7 @@ namespace jdi
|
||||
|
||||
if (loaded > 0)
|
||||
{
|
||||
SetStatusMessage(L"已加载 " + std::to_wstring(loaded) +
|
||||
SetStatusMessage(L"已加载 " + FormatInteger(loaded) +
|
||||
L" 个文件,请在左侧勾选需要查看的信号。",
|
||||
false);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace jdi
|
||||
|
||||
MenuItem header;
|
||||
header.kind = MenuItemKind::HEADER;
|
||||
header.label = L"网格 " + std::to_wstring(laneIndex + 1);
|
||||
header.label = L"网格 " + FormatInteger(laneIndex + 1);
|
||||
items.push_back(header);
|
||||
|
||||
// 当前网格的 Y 轴上下限值直接在菜单内的输入框里显示,单击即可就地编辑(菜单不关闭)。
|
||||
@@ -64,7 +64,7 @@ namespace jdi
|
||||
MenuItem splitLane;
|
||||
const std::size_t laneSignalCount = m_plot.LaneSignalCount(laneIndex);
|
||||
splitLane.label = (laneSignalCount > 1)
|
||||
? (L"拆分当前网格(" + std::to_wstring(laneSignalCount) + L"个信号)")
|
||||
? (L"拆分当前网格(" + FormatInteger(laneSignalCount) + L"个信号)")
|
||||
: L"拆分当前网格";
|
||||
splitLane.id = PLOT_CMD_LANE_SPLIT;
|
||||
splitLane.enabled = laneSignalCount > 1;
|
||||
|
||||
Reference in new issue
Block a user