Skip to content

refactor: replace private access hacks with accessor pattern#315

Open
zccrs wants to merge 2 commits into
linuxdeepin:masterfrom
zccrs:master
Open

refactor: replace private access hacks with accessor pattern#315
zccrs wants to merge 2 commits into
linuxdeepin:masterfrom
zccrs:master

Conversation

@zccrs
Copy link
Copy Markdown
Member

@zccrs zccrs commented May 25, 2026

Use C++ [temp.explicit]/12 explicit instantiation to bypass access control instead of #define private/protected public hacks.

@zccrs zccrs requested a review from Copilot May 25, 2026 05:32
@deepin-ci-robot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 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

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes the #define private/protected public access hack in the platform theme plugin and replaces it with a “private accessor” pattern based on explicit template instantiation ([temp.explicit]/12), and adds CI workflows to build in Deepin/Arch container environments.

Changes:

  • Introduce dprivateaccessor_p.h macros to access private/static Qt internals without preprocessor access hacks.
  • Update qdeepintheme.cpp to use the new accessor macros for QHighDpiScaling private statics across Qt versions.
  • Add GitHub Actions workflows for Deepin crimson and Arch Linux container builds.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
platformthemeplugin/qdeepintheme.cpp Replaces direct private static access with D_PRIVATE_STATIC_MEMBER(...) accessor usage.
platformthemeplugin/dprivateaccessor_p.h Adds the explicit-instantiation-based private member accessor infrastructure.
platformthemeplugin/CMakeLists.txt Registers the new private accessor header in the plugin headers list.
.github/workflows/qt5integration-deepin-build.yml Adds a Deepin crimson container build workflow producing .deb artifacts.
.github/workflows/qt5integration-archlinux-build.yml Adds an Arch container build workflow producing an installation zip artifact.
Comments suppressed due to low confidence (2)

platformthemeplugin/dprivateaccessor_p.h:53

  • dtk_private_detail::access() is declared constexpr but calls get(t), which is not constexpr here. With C++17 this typically makes the function ill-formed (a constexpr function can’t call a non-constexpr function). Either remove constexpr from access() or make the friend get(Tag) declaration/definition constexpr as well.
namespace dtk_private_detail {
    template<typename Tag>
    inline constexpr typename Tag::MemberPtr access(Tag t) noexcept { return get(t); }
}

.github/workflows/qt5integration-deepin-build.yml:51

  • apt-get install -y --allow-unauthenticated wlr-protocols || true both bypasses package authentication and masks installation failures. If the package is optional, prefer checking availability explicitly and keep signature verification enabled; otherwise, remove --allow-unauthenticated and the unconditional || true so failures are actionable.
          set -euxo pipefail
          apt-get install -y --allow-unauthenticated wlr-protocols || true
          cd /tmp
          git clone --depth=1 https://github.com/linuxdeepin/treeland-protocols.git

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread platformthemeplugin/dprivateaccessor_p.h
Comment on lines +17 to +20
set -euxo pipefail
echo "deb [trusted=yes] http://mirrors.kernel.org/deepin/beige/ crimson main commercial community" > /etc/apt/sources.list
echo "deb-src [trusted=yes] http://mirrors.kernel.org/deepin/beige/ crimson main commercial community" >> /etc/apt/sources.list
rm -rf /etc/apt/sources.list.d
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Deepin crimson repository does not distribute a GPG signing key publicly, so [trusted=yes] is required. This only affects the isolated CI sandbox environment and has no impact on production deployments.

jobs:
container:
runs-on: ubuntu-latest
container: archlinux:latest
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arch Linux is a rolling release; pinning to a timestamped image causes partial-upgrade breakage (pacman does not support partial upgrades). archlinux:latest ensures a consistent package state, which is the correct approach for Arch CI.

@zccrs zccrs force-pushed the master branch 6 times, most recently from 9ef30a8 to 563c066 Compare May 26, 2026 06:18
JiDe Zhang added 2 commits May 26, 2026 18:37
Add CI workflows to verify the project builds correctly on both Arch Linux
(latest) and Deepin crimson. This helps catch any platform-specific
compilation issues introduced by the accessor pattern refactor.
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.

Adds platformthemeplugin/dprivateaccessor_p.h with Accessor/AccessorImpl
templates and D_DECLARE_PRIVATE_MEMBER, D_PRIVATE_MEMBER macros. All helpers
live at global scope so ADL correctly resolves the friend-injected get()
function.
@deepin-ci-robot
Copy link
Copy Markdown
Contributor

deepin pr auto review

你好!我是CodeGeeX。我已经仔细审查了你提供的Git Diff,该变更主要包含两部分:一是新增了两个GitHub Actions CI工作流(Arch Linux和Deepin),二是引入了一个利用C++显式模板实例化技术来访问Qt私有成员的宏工具集,以替代破坏性的#define private public做法。

总体来说,这是一个质量很高的提交,特别是用符合C++标准的方式替换#define private public是非常值得称赞的改进。不过,在代码逻辑、CI配置、代码性能和安全性方面,我还有以下改进建议:

1. 代码逻辑与语法

1.1 宏定义末尾缺少分号

dprivateaccessor_p.h 中,D_DECLARE_PRIVATE_* 系列宏的最后一行缺少了分号:

// 当前代码
template struct Qt5IntegrationPrivateAccessorImpl<TagName, &ClassName::MemberName>
// 建议修改为
template struct Qt5IntegrationPrivateAccessorImpl<TagName, &ClassName::MemberName>;

原因:虽然C++中显式模板实例化声明template struct ...即使不加末尾分号,大多数编译器也会正常通过,但这不符合C++的语法规范,且在某些严格的编译器或静态分析工具下可能会引发警告。

1.2 宏定义中使用了保留标识符

D_DECLARE_PRIVATE_MEMBER 等宏中,使用了 ClassNameMemberName 作为结构体(TagName)内部 using 声明的依赖名:

using MemberPtr = MemberType ClassName::*;

建议:如果 ClassName 本身是模板类,这种写法需要加上 typename 关键字(如 typename MemberType ClassName::*)。此外,宏参数在展开时如果遇到复杂的类型可能会产生解析问题。建议在宏内部使用更清晰的括号包裹:using MemberPtr = MemberType (ClassName::*);

2. 代码性能

2.1 频繁的静态成员访问开销

qdeepintheme.cpp 中,诸如 D_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_logicalDpi_tag{}).first 的调用被多次使用:

D_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_logicalDpi_tag{}).first = dpi.first;
// ...
D_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_logicalDpi_tag{}).second = dpi.second;

分析D_PRIVATE_STATIC_MEMBER 展开后会调用 dtk_private_detail::access(tag),虽然 accessinline 函数且 get 也是内联的,但在一次逻辑操作中连续多次构造 tag 并调用函数链,可能会阻止编译器进行最优的寄存器优化。
建议:在需要连续访问同一个私有成员的代码块中,可以先获取一次指针或引用,然后直接使用:

auto& logicalDpi = D_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_logicalDpi_tag{});
if (dpi.first > 0) {
    logicalDpi.first = dpi.first;
} else if (qIsNull(dpi.first)) {
    logicalDpi.first = qGuiApp->primaryScreen()->handle()->logicalDpi().first;
}
// ... 同理 second

3. 代码安全与健壮性

3.1 dtk_private_detail::access 缺少对 get(Tag) 的包含约束

dprivateaccessor_p.h 中的 access 函数依赖 ADL(参数依赖查找)来找到 get(Tag)

namespace dtk_private_detail {
    template<typename Tag>
    inline typename Tag::MemberPtr access(Tag t) noexcept { return get(t); }
}

隐患:如果在包含此头文件之前,或其他包含的命名空间中存在通用的 get(Tag)get(T) 模板函数(例如某些序列化库或 std::get),可能会产生 ADL 冲突或匹配到错误的 get 函数,导致难以调试的编译错误。
建议:虽然注释中提到了必须将 friend 声明放在 Tag 内部以支持 ADL,但为了绝对安全,可以在 TagName 的定义中,将 get 函数的声明严格限制为只能通过 ADL 找到,并确保没有其他无约束的 get 污染全局命名空间。目前的写法在 Qt 项目中是相对常见的,但需警惕未来的命名污染。

4. CI/CD 配置改进

4.1 Arch Linux CI 中 pacman-key 初始化不完整

      - name: Initialize pacman and system update
        run: |
          pacman-key --init
          pacman --noconfirm --noprogressbar -Syu

问题:在 Docker 容器中运行 archlinux:latest 时,仅执行 pacman-key --init 是不够的,通常还需要填充密钥环,否则在安装包时可能会因为 GPG 签名验证失败而报错。
建议修改为

      - name: Initialize pacman and system update
        run: |
          pacman-key --init
          pacman-key --populate archlinux
          pacman --noconfirm --noprogressbar -Syu

4.2 Deepin CI 中 APT 源配置存在潜在风险

          echo "deb [trusted=yes] http://mirrors.kernel.org/deepin/beige/ crimson main commercial community" > /etc/apt/sources.list

问题:使用 [trusted=yes] 绕过了 GPG 安全校验,这虽然方便,但存在供应链攻击风险。另外,硬编码镜像源地址在网络波动或镜像站维护时会导致 CI 失败。
建议:如果可能,尽量导入 Deepin 官方的 GPG Key 并移除 [trusted=yes]。或者使用更稳定的官方 CDN 源。

4.3 Deepin CI 中 dpkg -i ../*.deb 2>/dev/null || apt-get install -f -y 的隐患

          dpkg -i ../*.deb 2>/dev/null || apt-get install -f -y

问题2>/dev/null 会隐藏 dpkg 的所有错误信息,如果包构建本身有问题,这里会丢失关键排错日志。另外,../*.deb 可能会匹配到之前步骤遗留的无关 .deb 文件。
建议

  1. 移除 2>/dev/null,保留错误输出以便调试。
  2. 使用更精确的包名匹配,或者在每一步构建后清理上级目录的 deb 文件,确保只安装当前步骤的产物。

4.4 SPDX 版权年份问题

+# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.

问题:版权年份写的是 2026,当前是 2024 年,这通常是一个笔误。
建议:修改为 20242022 - 2024 等符合实际的时间范围。

总结

核心的 C++ Hack(dprivateaccessor_p.h)设计非常精妙,完全符合 C++ 标准关于显式实例化不受访问控制约束的规定,比 #define private public 安全且不会引发 ODR(One Definition Rule)问题。只需补全宏末尾的分号,并稍微优化高频调用处的性能即可。CI 部分的修改建议主要是为了提升构建的稳定性和安全性,特别是 Arch Linux 的密钥环初始化问题,建议务必修复,否则 CI 随时可能因包签名校验失败而挂掉。

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