Skip to content

Commit 0d1a967

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/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.
1 parent 7e19ab8 commit 0d1a967

5 files changed

Lines changed: 245 additions & 6 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: Build dtkcore 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 clang make
28+
29+
- name: Install dtkcore Qt5 system dependencies
30+
if: matrix.qt_version == 5
31+
run: |
32+
pacman -S --noconfirm --noprogressbar \
33+
qt5-base qt5-tools \
34+
icu uchardet dbus \
35+
gtest spdlog \
36+
gsettings-qt \
37+
dtkcommon dtklog
38+
39+
- name: Install dtkcore Qt6 system dependencies
40+
if: matrix.qt_version == 6
41+
run: |
42+
pacman -S --noconfirm --noprogressbar \
43+
qt6-base qt6-tools \
44+
icu uchardet dbus \
45+
gtest spdlog \
46+
dtkcommon dtk6log
47+
48+
- uses: actions/checkout@v4
49+
with:
50+
fetch-depth: 0
51+
52+
- name: Configure and build dtkcore
53+
run: |
54+
cmake -B build \
55+
-GNinja \
56+
-DCMAKE_INSTALL_PREFIX=/usr \
57+
-DCMAKE_INSTALL_LIBDIR=lib \
58+
-DCMAKE_BUILD_TYPE=Release \
59+
-DMKSPECS_INSTALL_DIR=lib/qt/mkspecs/modules \
60+
-DBUILD_DOCS=OFF \
61+
-DBUILD_EXAMPLES=OFF \
62+
-DBUILD_TESTING=OFF \
63+
-DDTK5=${{ matrix.dtk5 }}
64+
cmake --build build -j$(nproc)
65+
echo "✅ dtkcore Qt${{ matrix.qt_version }} built successfully!"
66+
67+
- name: Install dtkcore to staging directory
68+
run: |
69+
DESTDIR=/tmp/dtkcore-install cmake --install build
70+
echo "Total: $(find /tmp/dtkcore-install -type f | wc -l) files"
71+
72+
- name: Create installation package
73+
run: |
74+
cd /tmp/dtkcore-install
75+
pacman -S --noconfirm zip
76+
zip -r /tmp/dtkcore-archlinux-qt${{ matrix.qt_version }}-$(date +%Y%m%d-%H%M%S).zip .
77+
ls -la /tmp/dtkcore-archlinux-qt${{ matrix.qt_version }}-*.zip
78+
79+
- name: Upload dtkcore Arch Linux build artifacts
80+
if: ${{ !env.ACT }}
81+
uses: actions/upload-artifact@v4
82+
with:
83+
name: dtkcore-archlinux-qt${{ matrix.qt_version }}-build
84+
path: "/tmp/dtkcore-archlinux-qt${{ matrix.qt_version }}-*.zip"
85+
if-no-files-found: error
86+
retention-days: 30
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: Build dtkcore 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: Install dtkcore build dependencies
54+
run: |
55+
set -euxo pipefail
56+
mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control
57+
58+
- name: Build dtkcore deb packages
59+
run: |
60+
set -euxo pipefail
61+
dpkg-buildpackage -uc -us -b -P${{ matrix.dtk_profile }}
62+
echo "✅ dtkcore Qt${{ matrix.qt_version }} deb packages built successfully!"
63+
ls -la ../
64+
65+
- name: Collect deb artifacts
66+
run: |
67+
mkdir -p dist
68+
mv ../*.deb dist/
69+
ls -la dist
70+
71+
- name: Upload dtkcore deb packages as artifacts
72+
uses: actions/upload-artifact@v4
73+
with:
74+
name: dtkcore-deepin-deb-packages-qt${{ matrix.qt_version }}
75+
path: dist/*.deb
76+
if-no-files-found: error
77+
retention-days: 30

src/dci/private/ddcifileengine.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2021 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: LGPL-3.0-or-later
44
#include <QtGlobal>
@@ -7,18 +7,18 @@
77
#include <filesystem> //Avoid changing the access control of the standard library
88
#endif
99

10-
#define private public
11-
#define protected public
1210
#include <private/qfile_p.h>
13-
#undef private
14-
#undef protected
1511

12+
#include "util/dprivateaccessor_p.h"
1613
#include "ddcifileengine_p.h"
1714
#include "dci/ddcifile.h"
1815

1916
#include <QBuffer>
2017
#include <QLoggingCategory>
2118

19+
D_DECLARE_PRIVATE_METHOD(QFile_d_func_tag, QFile, d_func, QFilePrivate *);
20+
D_DECLARE_PRIVATE_CONST_METHOD(QFilePrivate_engine_tag, QFilePrivate, engine, QAbstractFileEngine *);
21+
2222
DCORE_BEGIN_NAMESPACE
2323

2424
#ifdef QT_DEBUG
@@ -306,7 +306,7 @@ bool DDciFileEngine::syncToDisk()
306306
{
307307
if (!flush())
308308
return false;
309-
return realDciFile.d_func()->engine()->syncToDisk();
309+
return D_PRIVATE_CALL(*D_PRIVATE_CALL(realDciFile, QFile_d_func_tag{}), QFilePrivate_engine_tag{})->syncToDisk();
310310
}
311311

312312
qint64 DDciFileEngine::size() const

src/util/dprivateaccessor_p.h

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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+
// Private member accessor using the explicit template instantiation technique.
8+
//
9+
// C++ Standard [temp.explicit]/12 states:
10+
// "The usual access checking rules do not apply to names used to
11+
// specify explicit instantiation definitions."
12+
//
13+
// This allows passing pointers to private/protected data members and
14+
// member functions as template arguments in explicit instantiations,
15+
// bypassing normal access control — without modifying the class definition
16+
// and without the UB caused by "#define private public".
17+
//
18+
// NOTE: These helper structs must be in the SAME namespace as the Tag structs
19+
// (global namespace, since the macros expand at file scope). If they were in a
20+
// sub-namespace, the friend definition would create a different function than
21+
// the friend declaration in the Tag struct, causing undefined-reference errors.
22+
23+
#if defined(__GNUC__) && !defined(__clang__)
24+
# pragma GCC diagnostic push
25+
# pragma GCC diagnostic ignored "-Wnon-template-friend"
26+
#endif
27+
28+
template<typename Tag>
29+
struct DtkCorePrivateAccessor
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 DtkCorePrivateAccessorImpl : DtkCorePrivateAccessor<Tag>
37+
{
38+
friend typename Tag::MemberPtr get(Tag) noexcept { return Ptr; }
39+
};
40+
41+
#if defined(__GNUC__) && !defined(__clang__)
42+
# pragma GCC diagnostic pop
43+
#endif
44+
45+
#define D_DECLARE_PRIVATE_MEMBER(TagName, ClassName, Member, MemberType) \
46+
struct TagName { \
47+
using MemberPtr = MemberType ClassName::*; \
48+
friend MemberPtr get(TagName) noexcept; \
49+
}; \
50+
template struct DtkCorePrivateAccessorImpl<TagName, &ClassName::Member>
51+
52+
#define D_DECLARE_PRIVATE_METHOD(TagName, ClassName, MethodName, RetType, ...) \
53+
struct TagName { \
54+
using MemberPtr = RetType (ClassName::*)(__VA_ARGS__); \
55+
friend MemberPtr get(TagName) noexcept; \
56+
}; \
57+
template struct DtkCorePrivateAccessorImpl<TagName, &ClassName::MethodName>
58+
59+
#define D_DECLARE_PRIVATE_CONST_METHOD(TagName, ClassName, MethodName, RetType, ...) \
60+
struct TagName { \
61+
using MemberPtr = RetType (ClassName::*)(__VA_ARGS__) const; \
62+
friend MemberPtr get(TagName) noexcept; \
63+
}; \
64+
template struct DtkCorePrivateAccessorImpl<TagName, &ClassName::MethodName>
65+
66+
// Trampoline: ensures get(tag) is called from a context with no class-scope
67+
// get() member that might suppress ADL (C++ [basic.lookup.argdep] para 3).
68+
namespace dtk_private_detail {
69+
template<typename Tag>
70+
inline constexpr typename Tag::MemberPtr access(Tag t) noexcept { return get(t); }
71+
}
72+
73+
#define D_PRIVATE_MEMBER(obj, tag) ((obj).*dtk_private_detail::access(tag))
74+
#define D_PRIVATE_CALL(obj, tag, ...) ((obj).*dtk_private_detail::access(tag))(__VA_ARGS__)
75+

tools/dci/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ target_include_directories(${BIN_NAME} PUBLIC
2727
../../include/DtkCore/
2828
../../include/base/
2929
../../include/global/
30+
../../src/
3031
)
3132
set_target_properties(${BIN_NAME} PROPERTIES OUTPUT_NAME ${TARGET_NAME})
3233
#end dci

0 commit comments

Comments
 (0)