Code Review :代码审查 + 产物体积 −12.2%(890,880 → 782,336 B)+ 面板结构收敛 + 离屏验证工具入库 #3
No files matched your search
@@ -359,19 +359,21 @@ namespace jdi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ⚠️ 跳过根控件:它的矩形与工具栏区域完全重合(已经被上一层登记过)。
|
// ⚠️ 跳过根控件:它的矩形与工具栏区域完全重合(已经被上一层登记过)。
|
||||||
for (const std::unique_ptr<UiWidget>& button : m_root->Children())
|
//
|
||||||
|
// ⚠️⚠️ **这里绝不能出现 `dynamic_cast`**:旧写法用它把 `UiWidget*` 转成
|
||||||
|
// `UiButtonWidget*`,但 `Button::widget` 本来就是 `UiButtonWidget*`。
|
||||||
|
// 一个多余的转换会链入 RTTI 元数据 + `cp-demangle.o`(Release map 里
|
||||||
|
// **56,320 B**;最小复现程序实测 **+75,264 B**),并逼得全项目无法用
|
||||||
|
// `-fno-rtti`。现在改为直接遍历 `m_buttons`(与控件树同源,且无需转换)。
|
||||||
|
for (const Button& spec : m_buttons)
|
||||||
{
|
{
|
||||||
if (button == nullptr)
|
if (spec.widget == nullptr)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
boxes.Add(button->Bounds(), 1);
|
boxes.Add(spec.widget->Bounds(), 1);
|
||||||
// 按钮**内容**(文本或齿轮图标)单独上报:它是居中绘制的,与按钮矩形不同。
|
// 按钮**内容**(文本或齿轮图标)单独上报:它是居中绘制的,与按钮矩形不同。
|
||||||
const UiButtonWidget* raw = dynamic_cast<const UiButtonWidget*>(button.get());
|
boxes.Add(spec.widget->ContentRect(), 2);
|
||||||
if (raw != nullptr)
|
|
||||||
{
|
|
||||||
boxes.Add(raw->ContentRect(), 2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -192,6 +192,15 @@ ifeq ($(strip $(NO_EXCEPTIONS)), 1)
|
|||||||
CXXFLAGS += -fno-exceptions
|
CXXFLAGS += -fno-exceptions
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# ---- RTTI:全项目不使用 `dynamic_cast` / `typeid` ----
|
||||||
|
# 2026-11-04 审查:唯一的 `dynamic_cast`(Toolbar::BuildBoxes)是多余的强转,
|
||||||
|
# 已改为直接使用已有的 `UiButtonWidget*`。RTTI 会链入 `cp-demangle.o`
|
||||||
|
# (Release map 实测 56,320 B)+ `type_info` 表,最小复现程序实测 +75,264 B。
|
||||||
|
NO_RTTI = 1
|
||||||
|
ifeq ($(strip $(NO_RTTI)), 1)
|
||||||
|
CXXFLAGS += -fno-rtti
|
||||||
|
endif
|
||||||
|
|
||||||
# 丢弃未被引用的函数/数据(非 LTO 时还需要配合 -ffunction-sections 才有粒度)。
|
# 丢弃未被引用的函数/数据(非 LTO 时还需要配合 -ffunction-sections 才有粒度)。
|
||||||
GC_SECTIONS = -Wl,--gc-sections
|
GC_SECTIONS = -Wl,--gc-sections
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,35 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief 「未捕获异常 / 分配失败」的可见提示(`std::terminate` 的兜底路径)。
|
||||||
|
*
|
||||||
|
* ⚠️ **为什么放在全局匿名命名空间**:下面要在 `namespace __gnu_cxx` 里覆盖
|
||||||
|
* libstdc++ 的默认终止处理器,而 `__gnu_cxx` 必须是**全局**命名空间
|
||||||
|
* (不能嵌套在本项目自己的 `jdi` 里)。
|
||||||
|
*
|
||||||
|
* ⚠️ 本来这个函数由 libstdc++ 的 `__gnu_cxx::__verbose_terminate_handler()` 提供,
|
||||||
|
* 但它所在的 `vterminate.o` 会链入 `cp-demangle.o`
|
||||||
|
* (Release map 实测 `.text` 49,600 + `.rdata` 6,720 ≈ **56 KB** 的 C++ 名字反修饰器,
|
||||||
|
* 对一个不打印栈回溯的 GUI 程序毫无用处)。
|
||||||
|
* 本文件自行定义该函数 ⇒ 归档成员 `vterminate.o` 不再被拉入 ⇒ demangler 消失
|
||||||
|
* (2026-11-04 代码审查,实测 −55 KB 量级)。
|
||||||
|
*
|
||||||
|
* ⚠️ 这里**不能**用任何会再次分配内存的机制(此刻堆可能已不可用):
|
||||||
|
* 只用静态字面量与 Win32 API。
|
||||||
|
*/
|
||||||
|
void MemTerminateFallbackImpl()
|
||||||
|
{
|
||||||
|
MessageBoxW(nullptr,
|
||||||
|
L"内存不足:本次操作需要的内存超过系统可用量。\n\n"
|
||||||
|
L"建议:减少同时显示的网格数或信号数,或关闭占用内存较大的程序后重试。",
|
||||||
|
L"Just DataInspector", MB_ICONERROR | MB_OK | MB_SETFOREGROUND);
|
||||||
|
std::_Exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace jdi
|
namespace jdi
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -32,18 +61,13 @@ namespace jdi
|
|||||||
std::atomic<std::size_t> g_memCachedBytes{0};
|
std::atomic<std::size_t> g_memCachedBytes{0};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief terminate 处理:给出可见提示后退出。
|
* @brief 应用安装的 terminate 处理(`MemInstallTerminateHandler` 注册)。
|
||||||
*
|
*
|
||||||
* ⚠️ 这里**不能**用 MessageBoxW 之外的任何复杂机制:此刻堆可能已不可用,
|
* ⚠️ 与文件末尾的 `__gnu_cxx::__verbose_terminate_handler()` 覆盖共用同一条提示路径。
|
||||||
* 连 `std::wstring` 都不该构造(字面量是静态数据,安全)。
|
|
||||||
*/
|
*/
|
||||||
void OnTerminate()
|
void OnTerminate()
|
||||||
{
|
{
|
||||||
MessageBoxW(nullptr,
|
MemTerminateFallbackImpl();
|
||||||
L"内存不足:本次操作需要的内存超过系统可用量。\n\n"
|
|
||||||
L"建议:减少同时显示的网格数或信号数,或关闭占用内存较大的程序后重试。",
|
|
||||||
L"Just DataInspector", MB_ICONERROR | MB_OK | MB_SETFOREGROUND);
|
|
||||||
std::_Exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -99,3 +123,25 @@ namespace jdi
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace jdi
|
} // namespace jdi
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief **覆盖** libstdc++ 的默认终止处理器(省掉 56 KB 的 C++ 名字反修饰器)。
|
||||||
|
*
|
||||||
|
* `std::terminate()` 定义在 `eh_terminate.o` 里,它引用 `__gnu_cxx::__verbose_terminate_handler()`。
|
||||||
|
* 该符号原本由归档成员 `vterminate.o` 提供,而 `vterminate.o` 又引用 `__cxa_demangle`
|
||||||
|
* ⇒ 链接器被迫拉入 `cp-demangle.o`(Release map 实测 `.text` 49,600 B + `.rdata` 6,720 B)。
|
||||||
|
* 我们在自己的目标文件里提供同名定义,`eh_terminate.o` 的未定义引用就被满足,
|
||||||
|
* `vterminate.o` 与整个 demangler 都不会进入最终产物。
|
||||||
|
*
|
||||||
|
* ⚠️ 必须定义在**全局** `__gnu_cxx` 命名空间(不能嵌套在 `jdi` 内)。
|
||||||
|
* ⚠️ 应用启动后会通过 `jdi::MemInstallTerminateHandler()` 换上自己的处理函数,
|
||||||
|
* 因此这个默认实现只在「启动早期崩溃」时生效 —— 行为与 libstdc++ 的版本等效
|
||||||
|
* (libstdc++ 版会打印反修饰后的异常类型名,我们只给内存提示)。
|
||||||
|
*/
|
||||||
|
namespace __gnu_cxx
|
||||||
|
{
|
||||||
|
void __verbose_terminate_handler()
|
||||||
|
{
|
||||||
|
MemTerminateFallbackImpl();
|
||||||
|
}
|
||||||
|
} // namespace __gnu_cxx
|
||||||
Reference in new issue
Block a user