Skip to content

Commit 71e070c

Browse files
committed
refactor: replace private access hacks with accessor pattern
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/private/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.
1 parent df9e39f commit 71e070c

5 files changed

Lines changed: 298 additions & 18 deletions

File tree

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
name: Build dtkdeclarative on Arch Linux
2+
3+
on:
4+
push:
5+
6+
pull_request:
7+
8+
jobs:
9+
container:
10+
runs-on: ubuntu-latest
11+
container: archlinux:latest
12+
strategy:
13+
matrix:
14+
include:
15+
- qt_version: 5
16+
dtk5: "ON"
17+
- qt_version: 6
18+
dtk5: "OFF"
19+
steps:
20+
- name: Initialize pacman and system update
21+
run: |
22+
pacman-key --init
23+
pacman --noconfirm --noprogressbar -Syu
24+
25+
- name: Install base build tools
26+
run: |
27+
pacman -S --noconfirm --noprogressbar base-devel git cmake ninja pkgconf
28+
29+
- name: Install dtkdeclarative Qt5 system dependencies
30+
if: matrix.qt_version == 5
31+
run: |
32+
pacman -S --noconfirm --noprogressbar \
33+
qt5-base qt5-tools qt5-declarative qt5-quickcontrols2 \
34+
qt5-wayland qt5-svg qt5-imageformats \
35+
extra-cmake-modules \
36+
gtest \
37+
librsvg libraw \
38+
libqtxdg kwayland \
39+
icu uchardet dbus spdlog gsettings-qt \
40+
dtkcommon dtklog \
41+
treeland-protocols dtkcore dtkgui
42+
43+
- name: Install dtkdeclarative Qt6 system dependencies
44+
if: matrix.qt_version == 6
45+
run: |
46+
pacman -S --noconfirm --noprogressbar \
47+
qt6-base qt6-tools qt6-declarative qt6-quickcontrols2 \
48+
qt6-wayland qt6-svg qt6-imageformats \
49+
extra-cmake-modules \
50+
gtest \
51+
librsvg libraw \
52+
icu uchardet dbus spdlog \
53+
dtkcommon dtk6log \
54+
treeland-protocols dtk6core dtk6gui
55+
56+
- uses: actions/checkout@v4
57+
with:
58+
fetch-depth: 0
59+
60+
- name: Configure and build dtkdeclarative
61+
run: |
62+
cmake -B build \
63+
-GNinja \
64+
-DCMAKE_INSTALL_PREFIX=/usr \
65+
-DCMAKE_INSTALL_LIBDIR=lib \
66+
-DCMAKE_BUILD_TYPE=Release \
67+
-DMKSPECS_INSTALL_DIR=lib/qt/mkspecs/modules \
68+
-DQML_INSTALL_DIR=lib/qt/qml \
69+
-DBUILD_DOCS=OFF \
70+
-DBUILD_EXAMPLES=OFF \
71+
-DBUILD_TESTING=OFF \
72+
-DDTK5=${{ matrix.dtk5 }}
73+
cmake --build build -j$(nproc)
74+
echo "✅ dtkdeclarative Qt${{ matrix.qt_version }} built successfully!"
75+
76+
- name: Install dtkdeclarative to staging directory
77+
run: |
78+
DESTDIR=/tmp/dtkdeclarative-install cmake --install build
79+
echo "Total: $(find /tmp/dtkdeclarative-install -type f | wc -l) files"
80+
81+
- name: Create installation package
82+
run: |
83+
cd /tmp/dtkdeclarative-install
84+
pacman -S --noconfirm zip
85+
zip -r /tmp/dtkdeclarative-archlinux-qt${{ matrix.qt_version }}-$(date +%Y%m%d-%H%M%S).zip .
86+
ls -la /tmp/dtkdeclarative-archlinux-qt${{ matrix.qt_version }}-*.zip
87+
88+
- name: Upload dtkdeclarative Arch Linux build artifacts
89+
if: ${{ !env.ACT }}
90+
uses: actions/upload-artifact@v4
91+
with:
92+
name: dtkdeclarative-archlinux-qt${{ matrix.qt_version }}-build
93+
path: "/tmp/dtkdeclarative-archlinux-qt${{ matrix.qt_version }}-*.zip"
94+
if-no-files-found: error
95+
retention-days: 30
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
name: Build dtkdeclarative on Deepin crimson
2+
3+
on:
4+
push:
5+
6+
pull_request:
7+
8+
jobs:
9+
container:
10+
runs-on: ubuntu-latest
11+
container: linuxdeepin/deepin:crimson
12+
strategy:
13+
matrix:
14+
include:
15+
- qt_version: 5
16+
dtk_profile: nodtk6
17+
- qt_version: 6
18+
dtk_profile: nodtk5
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- name: Setup apt sources and build tools
23+
run: |
24+
set -euxo pipefail
25+
echo "deb [trusted=yes] http://mirrors.kernel.org/deepin/beige/ crimson main commercial community" > /etc/apt/sources.list
26+
echo "deb-src [trusted=yes] http://mirrors.kernel.org/deepin/beige/ crimson main commercial community" >> /etc/apt/sources.list
27+
rm -rf /etc/apt/sources.list.d
28+
apt-get update
29+
apt-get install -y devscripts equivs git
30+
31+
- name: Build and install dtkcommon from source
32+
run: |
33+
set -euxo pipefail
34+
cd /tmp
35+
git clone --depth=1 https://github.com/linuxdeepin/dtkcommon.git
36+
cd dtkcommon
37+
mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control
38+
dpkg-buildpackage -uc -us -b
39+
dpkg -i ../*.deb 2>/dev/null || apt-get install -f -y
40+
echo "✅ dtkcommon installed"
41+
42+
- name: Build and install dtklog from source
43+
run: |
44+
set -euxo pipefail
45+
cd /tmp
46+
git clone --depth=1 https://github.com/linuxdeepin/dtklog.git
47+
cd dtklog
48+
mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control
49+
dpkg-buildpackage -uc -us -b -P${{ matrix.dtk_profile }}
50+
dpkg -i ../*.deb 2>/dev/null || apt-get install -f -y
51+
echo "✅ dtklog installed"
52+
53+
- name: Build and install treeland-protocols from source
54+
run: |
55+
set -euxo pipefail
56+
apt-get install -y --allow-unauthenticated wlr-protocols || true
57+
cd /tmp
58+
git clone --depth=1 https://github.com/linuxdeepin/treeland-protocols.git
59+
cd treeland-protocols
60+
mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control
61+
dpkg-buildpackage -uc -us -b
62+
dpkg -i ../*.deb 2>/dev/null || apt-get install -f -y
63+
echo "✅ treeland-protocols installed"
64+
65+
- name: Build and install dtkcore from source
66+
run: |
67+
set -euxo pipefail
68+
cd /tmp
69+
git clone --depth=1 https://github.com/linuxdeepin/dtkcore.git
70+
cd dtkcore
71+
mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control
72+
dpkg-buildpackage -uc -us -b -P${{ matrix.dtk_profile }}
73+
dpkg -i ../*.deb 2>/dev/null || apt-get install -f -y
74+
echo "✅ dtkcore installed"
75+
76+
- name: Build and install dtkgui from source
77+
run: |
78+
set -euxo pipefail
79+
cd /tmp
80+
git clone --depth=1 https://github.com/linuxdeepin/dtkgui.git
81+
cd dtkgui
82+
mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control
83+
dpkg-buildpackage -uc -us -b -P${{ matrix.dtk_profile }}
84+
dpkg -i ../*.deb 2>/dev/null || apt-get install -f -y
85+
echo "✅ dtkgui installed"
86+
87+
- name: Install dtkdeclarative build dependencies
88+
run: |
89+
set -euxo pipefail
90+
mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control
91+
92+
- name: Build dtkdeclarative deb packages
93+
run: |
94+
set -euxo pipefail
95+
dpkg-buildpackage -uc -us -b -P${{ matrix.dtk_profile }}
96+
echo "✅ dtkdeclarative Qt${{ matrix.qt_version }} deb packages built successfully!"
97+
ls -la ../
98+
99+
- name: Collect deb artifacts
100+
run: |
101+
mkdir -p dist
102+
mv ../*.deb dist/
103+
ls -la dist
104+
105+
- name: Upload dtkdeclarative deb packages as artifacts
106+
uses: actions/upload-artifact@v4
107+
with:
108+
name: dtkdeclarative-deepin-deb-packages-qt${{ matrix.qt_version }}
109+
path: dist/*.deb
110+
if-no-files-found: error
111+
retention-days: 30

src/private/dbackdropnode.cpp

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: LGPL-3.0-or-later
44

55
#include <QDebug>
6-
#define protected public
7-
#define private public
86
#include <rhi/qrhi.h>
97
#include <private/qsgrenderer_p.h>
10-
#undef protected
11-
#undef private
128

139
#include "dbackdropnode_p.h"
1410
#include "dqmlglobalobject_p.h"
11+
#include "util/dprivateaccessor_p.h"
1512

1613
#include <QQuickItem>
1714
#include <QRunnable>
@@ -34,6 +31,10 @@
3431

3532
#include <algorithm>
3633

34+
#ifndef QT_NO_OPENGL
35+
D_DECLARE_PRIVATE_MEMBER(QRhi_d_tag, QRhi, d, QRhiImplementation*);
36+
#endif
37+
3738
DQUICK_BEGIN_NAMESPACE
3839

3940
class Q_DECL_HIDDEN DataManagerBase : public QObject
@@ -360,7 +361,7 @@ class Q_DECL_HIDDEN RhiManager : public DataManager<RhiManager, void>
360361
QRhiGles2 *gles2Rhi = nullptr;
361362
if (forceSurface) {
362363
Q_ASSERT(rhi()->backend() == QRhi::OpenGLES2);
363-
gles2Rhi = static_cast<QRhiGles2*>(rhi()->d);
364+
gles2Rhi = static_cast<QRhiGles2*>(D_PRIVATE_MEMBER(*rhi(), QRhi_d_tag{}));
364365
fallbackSurface = gles2Rhi->fallbackSurface;
365366
gles2Rhi->fallbackSurface = forceSurface;
366367
}
@@ -383,17 +384,14 @@ class Q_DECL_HIDDEN RhiManager : public DataManager<RhiManager, void>
383384
oldCB = dc->currentFrameCommandBuffer();
384385
context->prepareSync(renderer->devicePixelRatio(), cb, graphicsConfiguration());
385386

386-
renderer->m_is_rendering = true;
387-
renderer->preprocess();
387+
renderer->prepareSceneInline();
388388

389389
return true;
390390
}
391391

392392
bool render(qreal oldDPR, QRhiCommandBuffer* &oldCB) {
393-
Q_ASSERT(renderer->m_is_rendering);
394-
renderer->render();
395-
renderer->m_is_rendering = false;
396-
renderer->m_changed_emitted = false;
393+
renderer->renderSceneInline();
394+
renderer->clearChangedFlag();
397395

398396
context->prepareSync(oldDPR, oldCB, graphicsConfiguration());
399397

src/private/dqmlglobalobject.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22
//
33
// SPDX-License-Identifier: LGPL-3.0-or-later
44

5-
#define private public
65
#include <QSGNode>
7-
#undef private
86

97
#include "dqmlglobalobject_p.h"
108
#include "dqmlglobalobject_p_p.h"
9+
#include "util/dprivateaccessor_p.h"
1110
#include "dquickcontrolpalette_p.h"
1211
#include "dquickdciicon_p.h"
1312
#include "dquickimageprovider_p.h"
@@ -36,6 +35,11 @@
3635

3736
DGUI_USE_NAMESPACE
3837

38+
D_DECLARE_PRIVATE_MEMBER(QSGNode_m_subtreeRenderableCount_tag, QSGNode, m_subtreeRenderableCount, int);
39+
D_DECLARE_PRIVATE_MEMBER(QSGNode_m_firstChild_tag, QSGNode, m_firstChild, QSGNode *);
40+
D_DECLARE_PRIVATE_MEMBER(QSGNode_m_lastChild_tag, QSGNode, m_lastChild, QSGNode *);
41+
D_DECLARE_PRIVATE_MEMBER(QSGNode_m_parent_tag, QSGNode, m_parent, QSGNode *);
42+
3943
DQUICK_BEGIN_NAMESPACE
4044

4145
// ###(zccrs): The offset must greater than QColor::Spec
@@ -550,22 +554,22 @@ QSGRootNode *DQMLGlobalObject::getRootNode(QQuickItem *item)
550554

551555
int &DQMLGlobalObject::QSGNode_subtreeRenderableCount(QSGNode *node)
552556
{
553-
return node->m_subtreeRenderableCount;
557+
return D_PRIVATE_MEMBER(*node, QSGNode_m_subtreeRenderableCount_tag{});
554558
}
555559

556560
QSGNode *&DQMLGlobalObject::QSGNode_firstChild(QSGNode *node)
557561
{
558-
return node->m_firstChild;
562+
return D_PRIVATE_MEMBER(*node, QSGNode_m_firstChild_tag{});
559563
}
560564

561565
QSGNode *&DQMLGlobalObject::QSGNode_lastChild(QSGNode *node)
562566
{
563-
return node->m_lastChild;
567+
return D_PRIVATE_MEMBER(*node, QSGNode_m_lastChild_tag{});
564568
}
565569

566570
QSGNode *&DQMLGlobalObject::QSGNode_parent(QSGNode *node)
567571
{
568-
return node->m_parent;
572+
return D_PRIVATE_MEMBER(*node, QSGNode_m_parent_tag{});
569573
}
570574
#endif
571575

src/util/dprivateaccessor_p.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: LGPL-3.0-or-later
4+
5+
#pragma once
6+
7+
#include <QtCore/qcompilerdetection.h>
8+
9+
// Private member accessor using the explicit template instantiation technique.
10+
//
11+
// C++ Standard [temp.explicit]/12 states:
12+
// "The usual access checking rules do not apply to names used to
13+
// specify explicit instantiation definitions."
14+
//
15+
// This allows passing pointers to private/protected data members and
16+
// member functions as template arguments in explicit instantiations,
17+
// bypassing normal access control — without modifying the class definition
18+
// and without the UB caused by "#define private public".
19+
//
20+
// NOTE: These helper structs must be in the SAME namespace as the Tag structs
21+
// (global namespace, since the macros expand at file scope). If they were in a
22+
// sub-namespace, the friend definition would create a different function than
23+
// the friend declaration in the Tag struct, causing undefined-reference errors.
24+
25+
QT_WARNING_PUSH
26+
QT_WARNING_DISABLE_GCC("-Wnon-template-friend")
27+
28+
template<typename Tag>
29+
struct DtkDeclarativePrivateAccessor
30+
{
31+
using MemberPtr = typename Tag::MemberPtr;
32+
friend MemberPtr get(Tag) noexcept;
33+
};
34+
35+
template<typename Tag, typename Tag::MemberPtr Ptr>
36+
struct DtkDeclarativePrivateAccessorImpl : DtkDeclarativePrivateAccessor<Tag>
37+
{
38+
friend typename Tag::MemberPtr get(Tag) noexcept { return Ptr; }
39+
};
40+
41+
QT_WARNING_POP
42+
43+
#define D_DECLARE_PRIVATE_MEMBER(TagName, ClassName, Member, MemberType) \
44+
struct TagName { \
45+
using MemberPtr = MemberType ClassName::*; \
46+
friend MemberPtr get(TagName) noexcept; \
47+
}; \
48+
template struct DtkDeclarativePrivateAccessorImpl<TagName, &ClassName::Member>
49+
50+
#define D_DECLARE_PRIVATE_METHOD(TagName, ClassName, MethodName, RetType, ...) \
51+
struct TagName { \
52+
using MemberPtr = RetType (ClassName::*)(__VA_ARGS__); \
53+
friend MemberPtr get(TagName) noexcept; \
54+
}; \
55+
template struct DtkDeclarativePrivateAccessorImpl<TagName, &ClassName::MethodName>
56+
57+
#define D_DECLARE_PRIVATE_CONST_METHOD(TagName, ClassName, MethodName, RetType, ...) \
58+
struct TagName { \
59+
using MemberPtr = RetType (ClassName::*)(__VA_ARGS__) const; \
60+
friend MemberPtr get(TagName) noexcept; \
61+
}; \
62+
template struct DtkDeclarativePrivateAccessorImpl<TagName, &ClassName::MethodName>
63+
64+
// Trampoline: ensures get(tag) is called from a context with no class-scope
65+
// get() member that might suppress ADL (C++ [basic.lookup.argdep] para 3).
66+
namespace dtk_private_detail {
67+
template<typename Tag>
68+
inline typename Tag::MemberPtr access(Tag t) noexcept { return get(t); }
69+
}
70+
71+
#define D_PRIVATE_MEMBER(obj, tag) ((obj).*dtk_private_detail::access(tag))
72+
#define D_PRIVATE_CALL(obj, tag, ...) ((obj).*dtk_private_detail::access(tag))(__VA_ARGS__)

0 commit comments

Comments
 (0)