Skip to content

refactor: replace private access hacks with accessor pattern - #336

Merged
zccrs merged 10 commits into
linuxdeepin:masterfrom
zccrs:master
May 25, 2026
Merged

zccrs merged 10 commits into
linuxdeepin:masterfrom
zccrs:master

Conversation

@zccrs

@zccrs zccrs commented May 25, 2026

Copy link
Copy Markdown
Member

Replace all '#define private/protected public' access hacks with a proper C++ template-based private accessor pattern based on [temp.explicit].

Add src/util/dprivateaccessor_p.h with:

  • D_DECLARE_PRIVATE_MEMBER / D_DECLARE_PRIVATE_METHOD macros
  • D_DECLARE_AUTO_PRIVATE_MEMBER_TAG for members with private types
  • D_PRIVATE_MEMBER / D_PRIVATE_CALL helper macros

@zccrs
zccrs requested a review from Copilot May 25, 2026 05:23
@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

Copilot AI left a comment

Copy link
Copy Markdown

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 replaces widespread #define private/protected public access hacks with a template-based “private accessor” pattern (explicit-instantiation trick) and updates build plumbing/CI to support the new header usage across XCB/Wayland codepaths.

Changes:

  • Introduce src/util/dprivateaccessor_p.h and migrate multiple call sites to D_PRIVATE_MEMBER / D_PRIVATE_CALL accessors.
  • Remove private/protected macro hacks from XCB/Wayland sources and adjust a few call sites to use public accessors where available.
  • Add CI workflows to build in Deepin (crimson) and Arch Linux containers; update CMake include paths to expose src/ headers where needed.

Reviewed changes

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

Show a summary per file
File Description
xcb/xcbnativeeventfilter.cpp Replace private member access with accessor tags/calls for QXcb connection internals.
xcb/xcb.cmake Add ${CMAKE_SOURCE_DIR}/src include path for XCB build to find the new util header.
xcb/windoweventhook.cpp Replace private/protected hacks with accessor tags for drag/keyboard internals and adjust usage sites.
xcb/dxcbwmsupport.cpp Update compositing state write to use private member accessor.
xcb/dplatformwindowhook.h Remove private macro hack from header include path usage.
xcb/dplatformwindowhelper.h Remove private macro hack from header include path usage.
xcb/dplatformintegration.h Remove private protected macro hack around Qt private header include.
xcb/dplatformintegration.cpp Introduce multiple private accessor tags and migrate internal field accesses; adjust QGuiApplicationPrivate access.
xcb/dplatformbackingstorehelper.cpp Replace backing store private member access with accessor.
xcb/dplatformbackingstore.h Remove private macro hack around platform backing store include.
xcb/dnotitlebarwindowhelper.cpp Replace protected QWindow::event hooking with accessor-based member pointer retrieval.
xcb/dforeignplatformwindow.h Remove private macro hack around qxcbwindow include.
wayland/wayland-shell/main.cpp Replace private access hack with accessor-based call to create shell integration.
wayland/wayland-shell/dwaylandshellmanager.h Remove private macro hack around QtWayland private header includes.
wayland/wayland-shell/dwaylandshellmanager.cpp Remove protected macro hack around QWindow include.
wayland/dwayland/dwaylandinterfacehook.cpp Remove private macro hack around QtWayland native interface include.
wayland/dwayland/dwaylandintegration.cpp Replace private field accesses for cursor theme internals with accessor tags/calls.
wayland/dwayland/dnotitlebarwindowhelper_wl.cpp Replace protected QWindow::event hooking with accessor-based member pointer retrieval.
tests/src/ut_ddesktopinputselectioncontrol.cpp Update unit test private-member access to use new accessor macros.
src/util/dprivateaccessor_p.h Add the accessor pattern implementation and macro API used throughout the PR.
src/src.cmake Register the new util header in the global headers list.
src/dnativesettings.h Remove protected macro hack around qobject private header include.
src/dnativesettings.cpp Replace call to QObject private method with accessor-based member pointer call.
.github/workflows/qt5platform-plugins-deepin-build.yml Add Deepin crimson container build workflow producing deb artifacts.
.github/workflows/qt5platform-plugins-archlinux-build.yml Add Arch Linux container build workflow producing install zip artifacts.

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

Comment on lines +64 to +70
namespace dtk_private_detail {
template<typename Tag>
inline constexpr typename Tag::MemberPtr access(Tag t) noexcept { return get(t); }

// For auto-type tags (D_DECLARE_AUTO_PRIVATE_MEMBER_TAG) that don't define MemberPtr.
template<typename Tag>
inline auto access_auto(Tag t) noexcept -> decltype(get(t)) { return get(t); }
Comment on lines +102 to +110
#define D_DECLARE_AUTO_PRIVATE_MEMBER_TAG(TagName, ClassName, Member) \
struct TagName { \
friend auto get(TagName) noexcept; \
}; \
template<auto Ptr> \
struct TagName##Impl { \
friend auto get(TagName) noexcept { return Ptr; } \
}; \
template struct TagName##Impl<&ClassName::Member>
#include "dnotitlebarwindowhelper.h"
#include "util/dprivateaccessor_p.h"

D_DECLARE_PRIVATE_METHOD(QWindow_event, QWindow, event, bool, QEvent *);
#include <QWindow>
#undef protected
#include "util/dprivateaccessor_p.h"
D_DECLARE_PRIVATE_METHOD(QWindow_event, QWindow, event, bool, QEvent *);
Comment on lines +72 to +73
D_DECLARE_PRIVATE_MEMBER(QXcbCursor_m_gtkCursorThemeInitialized, QXcbCursor, m_gtkCursorThemeInitialized, bool);
D_DECLARE_PRIVATE_MEMBER(QXcbCursor_m_callbackForPropertyRegistered, QXcbCursor, m_callbackForPropertyRegistered, bool);
Replace all '#define private/protected public' access hacks with a
proper C++ template-based private accessor pattern based on [temp.explicit].

Add src/util/dprivateaccessor_p.h with:
- D_DECLARE_PRIVATE_MEMBER / D_DECLARE_PRIVATE_METHOD macros
- D_DECLARE_AUTO_PRIVATE_MEMBER_TAG for members with private types
- D_PRIVATE_MEMBER / D_PRIVATE_CALL helper macros
zccrs and others added 9 commits May 25, 2026 18:46
The KF5Wayland cmake module is provided by kwayland5, not kwayland
(which is the KF6/Qt6 version). Fix the package name so that
find_package(KF5Wayland REQUIRED) in wayland-shell.cmake succeeds.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
tar is always available in base system, no pacman install needed

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove pipe from cmake --install to properly propagate exit code.
Add directory listing before tar to diagnose missing install dir.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Without set -o pipefail, cmake failures in piped commands are masked.
This ensures the step fails properly if cmake configure or build fails.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@deepin-ci-robot

Copy link
Copy Markdown
Contributor

deepin pr auto review

这份代码提交的核心目的是消除代码中广泛存在的 #define private public 和 #define protected public 这种极其危险且属于未定义行为(UB)的宏黑客手法,转而使用 C++ 模板显式实例化技术(Attorney-Client Idiom 的一种变体)来合法地访问 Qt 私有/受保护的成员和方法。这是一个非常积极且高质量的改进!

以下是对本次代码审查的详细意见,分为语法逻辑、代码质量、代码性能和代码安全四个方面:

一、 语法逻辑

  1. 宏展开的命名空间问题(潜在编译错误)
    在 dprivateaccessor_p.h 中,注释明确说明了 Qt5PlatformPrivateAccessor 等辅助结构体必须在全局命名空间(与 Tag 相同)中。但是,如果使用者在某个自定义命名空间内调用 D_DECLARE_PRIVATE_MEMBER,Tag 结构体会被定义在该命名空间内,而宏展开出的 template struct Qt5PlatformPrivateAccessorImpl<...> 也会被错误地放入该命名空间,导致与全局命名空间的模板定义不匹配,引发链接错误。
    改进建议:在宏定义中确保模板显式实例化始终在全局命名空间进行,例如:

    #define D_DECLARE_PRIVATE_MEMBER(TagName, ClassName, Member, MemberType) \
        struct TagName { \
            using MemberPtr = MemberType ClassName::*; \
            friend MemberPtr get(TagName) noexcept; \
        }; \
        namespace { } /* 强制结束命名空间 */ \
        template struct Qt5PlatformPrivateAccessorImpl<TagName, &ClassName::Member>; \
        /* 注意:如果处于嵌套命名空间中,上述方法仍不完美,最佳实践是要求这些宏必须在全局作用域调用,并在文档/注释中严加声明 */
  2. D_DECLARE_AUTO_PRIVATE_MEMBER_TAG 中的 auto 返回类型推导问题
    在 dprivateaccessor_p.h 中:

    #define D_DECLARE_AUTO_PRIVATE_MEMBER_TAG(TagName, ClassName, Member) \
        struct TagName { \
            friend auto get(TagName) noexcept; \
        }; \
        template<auto Ptr> \
        struct TagName##Impl { \
            friend auto get(TagName) noexcept { return Ptr; } \
        }; \
        template struct TagName##Impl<&ClassName::Member>

    在 C++17 中,如果 friend auto get(TagName) 只有声明而没有定义,编译器无法推导 auto 的类型。虽然后面 TagName##Impl 中提供了定义,但某些严格编译器(如 MSVC,或在模块化构建时)可能会在看到声明时直接报错。
    改进建议:C++20 允许友元函数使用占位类型推导,但在 C++17 中需谨慎。如果必须支持 C++17,可能需要换一种实现方式,或者确保编译器延迟推导。如果项目已升级至 C++20,则无此问题。

  3. QXcbBasicConnection 的头文件包含缺失
    在 xcbnativeeventfilter.cpp 中新增了 #include "qxcbconnection_basic.h" 并使用了 QXcbBasicConnection 的私有成员。需确保 Qt 的开发包中确实存在此头文件,并且在旧版本 Qt(如 Qt 5.12 及以前)中该头文件是否叫其他名字,否则会破坏向下兼容性。

二、 代码质量

  1. dprivateaccessor_p.h 中的 QT_WARNING_PUSH/POP 兼容性
    代码使用了 QT_WARNING_PUSH 等 Qt 宏来屏蔽 -Wnon-template-friend 警告。如果该头文件被非 Qt 项目或剥离了 Qt 核心依赖的场景复用,会导致编译失败。
    改进建议:可以增加一层检测,或者直接使用编译器内置宏:

    #ifdef QT_WARNING_PUSH
        QT_WARNING_PUSH
        QT_WARNING_DISABLE_GCC("-Wnon-template-friend")
    #elif defined(__GNUC__)
        #pragma GCC diagnostic push
        #pragma GCC diagnostic ignored "-Wnon-template-friend"
    #endif
    // ... code ...
    #ifdef QT_WARNING_POP
        QT_WARNING_POP
    #elif defined(__GNUC__)
        #pragma GCC diagnostic pop
    #endif
  2. translateModifiers 函数的模板化
    在 windoweventhook.cpp 中,将原来的 QXcbKeyboard::_mod_masks 改为了模板 template<typename T> static Qt::KeyboardModifiers translateModifiers(const T &rmod_masks, int s)。
    改进建议:虽然语法上没问题,但过度模板化会降低代码可读性。既然已经使用了 D_AUTO_PRIVATE_MEMBER 获取成员,完全可以使用 auto 推导其类型,并保留原有的强类型函数签名:

    using RmodMasksType = decltype(D_AUTO_PRIVATE_MEMBER(*me->connection()->keyboard(), QXcbKeyboard_rmod_masks));
    static Qt::KeyboardModifiers translateModifiers(const RmodMasksType &rmod_masks, int s)
  3. CI 工作流 的改进

    • Arch Linux 构建中,pacman-key --init 在某些容器环境下可能会因为熵不足而卡住很久。
    • Deepin 构建中,硬编码了 http://mirrors.kernel.org/deepin/beige/。如果内核镜像站宕机或同步延迟,会导致 CI 失败。
      改进建议:Arch 构建可考虑添加 haveged 或类似方式加速初始化;Deepin 构建建议将镜像源设为环境变量,默认使用官方源或社区高速镜像。

三、 代码性能

  1. 访问私有成员的运行时开销
    使用 D_PRIVATE_MEMBER 和 D_PRIVATE_CALL 机制,其本质是在编译期通过模板显式实例化获取成员指针,运行时通过 ((obj).*ptr) 进行访问。
    结论:现代编译器(GCC/Clang)对成员指针的解引用和函数调用优化极佳,这与直接访问 obj.member 的运行时开销完全一致,零性能损耗。这部分重构不会引入任何性能回退。

  2. D_AUTO_PRIVATE_MEMBER vs D_PRIVATE_MEMBER
    D_AUTO_PRIVATE_MEMBER 使用了 C++17 的 auto 非类型模板参数,其内部使用了 static decltype(auto) access(T &obj)。
    结论:同样会被完全内联优化,无性能问题。但建议统一访问形式,目前既有 D_PRIVATE_MEMBER,又有 D_AUTO_PRIVATE_MEMBER,还有 get(Tag{}),增加了使用者的心智负担。

四、 代码安全

  1. 消除了 #define private public 带来的 ODR 违背和 UB 风险(巨大提升)
    原来的宏修改方式是 C++ 中臭名昭著的 UB。当头文件被宏修改后,在不同翻译单元中同一个类可能有不同的内存布局假定(虽然实际中可能没崩,但违反了 ODR 原则),且在 LTO(链接时优化)时极易被优化器剔除或错误优化导致崩溃。使用 Attorney-Client Idiom 是标准合规且安全的做法,这是本次提交最大的安全贡献。

  2. 版权年份的修改(2022 -> 2026)
    代码中大量将版权年份从 2017 - 2022 修改为 2017 - 2026。
    改进建议:2026 年是未来年份。通常版权声明应记录的是首次发布和最后实质性修改的年份。如果是为了统一标定某个时间点,建议确认是否符合公司的法务规范,一般不建议提前写未来年份。

  3. reinterpret_cast 依然存在
    在 dplatformbackingstorehelper.cpp 中:

    QXcbShmImage *shm_image = reinterpret_cast<QXcbShmImage*>(D_PRIVATE_MEMBER(*bs, QXcbBackingStore_m_image{}));

    虽然移除了宏,但 reinterpret_cast 依然是不安全的类型双关。如果 Qt 后续版本修改了 m_image 的类型,这里依然会无警告地崩溃。
    改进建议:如果可能,引入编译期 static_assert 或概念检查,确保获取到的指针类型与预期转换的类型在继承关系上匹配,或至少大小一致。

总结

这是一次非常出色的重构,从根本上解决了一直以来 Qt 私有 API 访问的脏乱差问题。代码逻辑严谨,对 C++ 模板机制的运用非常专业。只需注意宏展开的命名空间陷阱、C++17 auto 返回值类型推导的边界情况,以及 CI 脚本的一些小优化即可。建议在合并前在多编译器(尤其是 Clang 和 MSVC)下进行全量编译测试,以确保模板魔法没有副作用。

@zccrs
zccrs merged commit 9313394 into linuxdeepin:master May 25, 2026
19 of 23 checks passed
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