Skip to content

refactor: replace private access hacks with accessor pattern#564

Open
zccrs wants to merge 1 commit into
linuxdeepin:masterfrom
zccrs:master
Open

refactor: replace private access hacks with accessor pattern#564
zccrs wants to merge 1 commit into
linuxdeepin:masterfrom
zccrs:master

Conversation

@zccrs
Copy link
Copy Markdown
Member

@zccrs zccrs commented May 20, 2026

Remove all '#define private/protected public' hacks and replace them with a proper C++ template-based private accessor pattern using explicit template instantiation (friend injection trick), mirroring the approach used in linuxdeepin/treeland#875.

Add src/util/dprivateaccessor_p.h with Accessor/AccessorImpl templates and D_DECLARE_PRIVATE_MEMBER, D_DECLARE_PRIVATE_METHOD, D_DECLARE_PRIVATE_CONST_METHOD, D_PRIVATE_MEMBER, D_PRIVATE_CALL macros. All helpers are in global namespace so ADL correctly finds the friend-injected get() function.

@zccrs zccrs requested a review from 18202781743 May 20, 2026 08:45
@zccrs zccrs force-pushed the master branch 3 times, most recently from 8e57e23 to 0d1a967 Compare May 21, 2026 08:08
Remove all '#define private/protected public' hacks and replace them
with a proper C++ template-based private accessor pattern using
explicit template instantiation (friend injection trick), mirroring
the approach used in linuxdeepin/treeland#875.

Add src/util/dprivateaccessor_p.h with Accessor/AccessorImpl
templates and D_DECLARE_PRIVATE_MEMBER, D_DECLARE_PRIVATE_METHOD,
D_DECLARE_PRIVATE_CONST_METHOD, D_PRIVATE_MEMBER, D_PRIVATE_CALL
macros. All helpers are in global namespace so ADL correctly finds
the friend-injected get() function.
@deepin-ci-robot
Copy link
Copy Markdown
Contributor

deepin pr auto review

这份 diff 包含了 CI/CD 工作流的构建配置、一个用于安全访问 Qt 私有成员的 C++ 模板黑魔法实现,以及相关代码的适配修改。整体来看,代码构思巧妙,但存在一些语法/兼容性隐患、CI 配置冗余以及潜在的安全风险。

以下是详细的审查意见和改进建议:

一、 语法与逻辑

1. D_DECLARE_PRIVATE_METHOD 宏在无参数方法下的兼容性问题

  • 问题:在 ddcifileengine.cpp 中,D_DECLARE_PRIVATE_CONST_METHOD(QFilePrivate_engine_tag, QFilePrivate, engine, QAbstractFileEngine *) 声明了一个无参数的 const 方法。但在宏定义 D_DECLARE_PRIVATE_METHODD_DECLARE_PRIVATE_CONST_METHOD 中,使用了 RetType (ClassName::*)(__VA_ARGS__)。当 __VA_ARGS__ 为空时,在 MSVC 等编译器下,展开可能会变成 RetType (ClassName::*)()(多出一个逗号或空括号),虽然大多数情况下 C++ 允许空参数列表写为 (),但 __VA_ARGS__ 为空时的处理在不同编译器上存在不一致的行为。
  • 建议:如果确定只用于无参方法,可以显式写出 ();若要支持可变参数,建议利用 C++20 的 __VA_OPT__,或者针对无参方法提供一个单独的宏 D_DECLARE_PRIVATE_CONST_METHOD_NO_ARGS

2. D_PRIVATE_CALL 宏的空参数展开问题

  • 问题#define D_PRIVATE_CALL(obj, tag, ...) ((obj).*dtk_private_detail::access(tag))(__VA_ARGS__)。当调用无参方法时(如 D_PRIVATE_CALL(..., QFilePrivate_engine_tag{})),__VA_ARGS__ 为空,展开后变为 ->engine)()。虽然语法上合法,但如果未来有人在 __VA_ARGS__ 位置不小心加上逗号,会导致编译失败。
  • 建议:确保调用无参方法时 tag 后面没有任何多余的逗号。

3. ddcifileengine.cpp 中的逻辑调用链

  • 问题:原代码 realDciFile.d_func()->engine()->syncToDisk() 被替换为 D_PRIVATE_CALL(*D_PRIVATE_CALL(realDciFile, QFile_d_func_tag{}), QFilePrivate_engine_tag{})->syncToDisk()
    • D_PRIVATE_CALL(realDciFile, QFile_d_func_tag{}) 返回 QFilePrivate *
    • 对其解引用 * 后得到 QFilePrivate &(或者如果返回值就是指针,这里应该是 ->?)。
    • 随后 D_PRIVATE_CALL(..., QFilePrivate_engine_tag{}) 展开为 (....*access(tag))()。这要求 engine() 是一个成员函数指针调用,前提是左操作数必须是对象引用或指针。
  • 建议:仔细验证 d_func() 的返回类型。如果 d_func() 返回的是指针 QFilePrivate*,那么应该使用 ->* 而不是 .*。如果返回的是引用,则当前逻辑正确。但从 Qt 惯例来看,d_func() 通常返回指针。如果返回指针,代码应修改为:
    return D_PRIVATE_CALL(D_PRIVATE_CALL(realDciFile, QFile_d_func_tag{}), QFilePrivate_engine_tag{})->syncToDisk();

二、 代码质量

1. 替换 #define private public 是极佳的改进

  • 优点:通过显式模板实例化漏洞来访问私有成员,避免了修改宏定义带来的 ODR(One Definition Rule)违背和未定义行为。这是一个非常现代且符合标准的高级技巧,代码质量显著提升。
  • 隐患:这种技巧依赖于 C++ 标准对显式模板实例化的规定,但属于“边缘特性”,阅读和维护门槛较高。
  • 建议dprivateaccessor_p.h 中的注释已经非常详尽,这很好。建议在 ddcifileengine.cpp 调用 D_PRIVATE_CALL 的地方也加上简短注释,说明为什么这里要写这么长一串,以防其他开发者误以为是错误代码而尝试“优化”掉。

2. 命名规范与 Tag 结构体的声明位置

  • 问题QFile_d_func_tagQFilePrivate_engine_tag 被直接声明在 .cpp 文件的全局作用域。虽然宏展开必须在文件作用域(由于 friend 函数定义的限制),但全局作用域的命名容易与其他库产生冲突。
  • 建议:Tag 结构体命名加上更明确的前缀,如 Dtk_QFile_d_func_tag,以避免潜在的符号冲突。

3. CI 工作流中的冗余依赖

  • 问题:在 dtkcore-archlinux-build.yml 中,pacman -S ... base-devel git cmake ninja pkgconf clang make 安装了 clangmake,但在 Configure 阶段明确使用了 -GNinja 指定 Ninja 作为构建系统,且未看到切换到 Clang 编译器的配置(默认会使用 GCC)。
  • 建议:如果不需要 Clang 和 Make,可以从包列表中移除 clangmake,以减少 CI 环境的构建时间和镜像体积。

三、 代码性能

1. CI 中 Arch Linux 的包初始化策略

  • 问题pacman-key --initpacman -Syu 在每次 CI 运行时都会执行完整的系统更新,这在 Arch Linux 容器中通常会耗费大量时间(Arch 滚动更新包多)。
  • 建议:如果可能,考虑使用预构建的、包含基础依赖的 Docker 镜像作为 CI 运行环境,或者利用 GitHub Actions 的 Docker 层缓存,避免每次都从零开始全量更新系统。

2. CI 中 Deepin 的源替换策略

  • 问题rm -rf /etc/apt/sources.list.d 强行删除目录,并覆盖 /etc/apt/sources.list
  • 建议:虽然这能工作,但不够优雅。建议在覆盖前备份原文件,或者使用更标准的 sed 替换方式,以防未来基础镜像结构变化导致 apt 报错。

四、 代码安全

1. Deepin CI 中使用了 [trusted=yes] 且绕过包签名校验

  • 问题echo "deb [trusted=yes] http://mirrors.kernel.org/deepin/beige/ crimson main commercial community" > /etc/apt/sources.list[trusted=yes] 会跳过 GPG 签名验证,这容易遭受中间人攻击(MITM),攻击者可以在 CI 阶段注入恶意代码。
  • 建议:在 CI 环境中,正确的做法是使用 apt-key adv 或将仓库的 GPG 公钥放入 /usr/share/keyrings/,并在源列表中指定 [signed-by=/usr/share/keyrings/xxx.gpg],而不是盲目信任。

2. 版权年份的随意修改

  • 问题// SPDX-FileCopyrightText: 2021 - 2022 被修改为 2021 - 2026
  • 建议:版权结束年份通常是当前年份(如 2024),直接写 2026 可能是笔误。除非有特殊法律考量,建议修改为当前实际年份或保持原样。

3. CMake 中暴露内部源码路径

  • 问题:在 tools/dci/CMakeLists.txt 中添加了 ../../src/target_include_directoriesPUBLIC 属性中。
  • 建议PUBLIC 属性意味着任何链接该目标的库也会继承这个包含路径。这不仅暴露了内部实现细节,还可能导致符号冲突。如果仅是编译当前目标需要,请务必改为 PRIVATE
    target_include_directories(${BIN_NAME} PRIVATE
      ../../src/
    )

总结与修改示例

针对最关键的语法和逻辑问题,建议对 dprivateaccessor_p.h 及其调用处做如下调整:

1. 修改调用逻辑(确认 d_func 返回指针):

// ddcifileengine.cpp
// 如果 d_func 返回指针,则不需要解引用,直接用 ->* 或在宏外层用 ->
return D_PRIVATE_CALL(D_PRIVATE_CALL(realDciFile, QFile_d_func_tag{}), QFilePrivate_engine_tag{})->syncToDisk();

2. 宏定义兼容性增强(可选,针对无参数方法):

// 专门用于无参方法的宏,避免 __VA_ARGS__ 为空时的潜在问题
#define D_DECLARE_PRIVATE_CONST_METHOD_NO_ARGS(TagName, ClassName, MethodName, RetType) \
    struct TagName { \
        using MemberPtr = RetType (ClassName::*)() const; \
        friend MemberPtr get(TagName) noexcept; \
    }; \
    template struct DtkCorePrivateAccessorImpl<TagName, &ClassName::MethodName>

// 调用处:
D_DECLARE_PRIVATE_CONST_METHOD_NO_ARGS(QFilePrivate_engine_tag, QFilePrivate, engine, QAbstractFileEngine *);

3. 修复 CMake 的 PUBLIC/PRIVATE 属性:

target_include_directories(${BIN_NAME} PRIVATE
  ../../src/
)

@deepin-ci-robot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 18202781743, zccrs

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants