Skip to content

Commit 4d5c9be

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. Adds src/util/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.
1 parent df9e39f commit 4d5c9be

3 files changed

Lines changed: 152 additions & 18 deletions

File tree

src/private/dbackdropnode.cpp

Lines changed: 36 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,33 @@
3431

3532
#include <algorithm>
3633

34+
#ifndef QT_NO_OPENGL
35+
D_DECLARE_PRIVATE_MEMBER(QRhi_d_tag, QRhi, d, QRhiImplementation*);
36+
#endif
37+
38+
// QSGRenderer keeps m_changed_emitted/m_is_rendering as private bit-fields
39+
// right after m_nodes_dont_preprocess, matching the layout used in Waylib.
40+
D_DECLARE_PRIVATE_METHOD(QSGRenderer_preprocess_tag, QSGRenderer, preprocess, void);
41+
D_DECLARE_PRIVATE_METHOD(QSGRenderer_render_tag, QSGRenderer, render, void);
42+
D_DECLARE_PRIVATE_BITFIELD(QSGRenderer_m_nodes_dont_preprocess_tag,
43+
QSGRenderer, m_nodes_dont_preprocess,
44+
QSet<QSGNode *>, uint);
45+
static constexpr unsigned k_m_changed_emitted_bit = 0;
46+
static constexpr unsigned k_m_is_rendering_bit = 1;
47+
48+
static_assert(sizeof(QSGRenderer) == 432,
49+
"QSGRenderer size changed — review qsgrenderer_p.h and update the bit-field accessor");
50+
51+
static inline void rendererPreprocess(QSGRenderer *renderer)
52+
{
53+
D_PRIVATE_CALL(*renderer, QSGRenderer_preprocess_tag{});
54+
}
55+
56+
static inline void rendererRender(QSGRenderer *renderer)
57+
{
58+
D_PRIVATE_CALL(*renderer, QSGRenderer_render_tag{});
59+
}
60+
3761
DQUICK_BEGIN_NAMESPACE
3862

3963
class Q_DECL_HIDDEN DataManagerBase : public QObject
@@ -360,7 +384,7 @@ class Q_DECL_HIDDEN RhiManager : public DataManager<RhiManager, void>
360384
QRhiGles2 *gles2Rhi = nullptr;
361385
if (forceSurface) {
362386
Q_ASSERT(rhi()->backend() == QRhi::OpenGLES2);
363-
gles2Rhi = static_cast<QRhiGles2*>(rhi()->d);
387+
gles2Rhi = static_cast<QRhiGles2*>(D_PRIVATE_MEMBER(*rhi(), QRhi_d_tag{}));
364388
fallbackSurface = gles2Rhi->fallbackSurface;
365389
gles2Rhi->fallbackSurface = forceSurface;
366390
}
@@ -383,17 +407,17 @@ class Q_DECL_HIDDEN RhiManager : public DataManager<RhiManager, void>
383407
oldCB = dc->currentFrameCommandBuffer();
384408
context->prepareSync(renderer->devicePixelRatio(), cb, graphicsConfiguration());
385409

386-
renderer->m_is_rendering = true;
387-
renderer->preprocess();
410+
D_PRIVATE_BF_SET(*renderer, QSGRenderer_m_nodes_dont_preprocess_tag, k_m_is_rendering_bit, true);
411+
rendererPreprocess(renderer);
388412

389413
return true;
390414
}
391415

392416
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;
417+
Q_ASSERT(D_PRIVATE_BF_GET(*renderer, QSGRenderer_m_nodes_dont_preprocess_tag, k_m_is_rendering_bit));
418+
rendererRender(renderer);
419+
D_PRIVATE_BF_SET(*renderer, QSGRenderer_m_nodes_dont_preprocess_tag, k_m_is_rendering_bit, false);
420+
D_PRIVATE_BF_SET(*renderer, QSGRenderer_m_nodes_dont_preprocess_tag, k_m_changed_emitted_bit, false);
397421

398422
context->prepareSync(oldDPR, oldCB, graphicsConfiguration());
399423

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: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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 <cstddef>
8+
#include <QtCore/qcompilerdetection.h>
9+
10+
// Private member accessor using the explicit template instantiation technique.
11+
//
12+
// C++ Standard [temp.explicit]/12 states:
13+
// "The usual access checking rules do not apply to names used to
14+
// specify explicit instantiation definitions."
15+
//
16+
// This allows passing pointers to private/protected data members and
17+
// member functions as template arguments in explicit instantiations,
18+
// bypassing normal access control — without modifying the class definition
19+
// and without the UB caused by "#define private public".
20+
//
21+
// NOTE: These helper structs must be in the SAME namespace as the Tag structs
22+
// (global namespace, since the macros expand at file scope). If they were in a
23+
// sub-namespace, the friend definition would create a different function than
24+
// the friend declaration in the Tag struct, causing undefined-reference errors.
25+
26+
QT_WARNING_PUSH
27+
QT_WARNING_DISABLE_GCC("-Wnon-template-friend")
28+
29+
template<typename Tag>
30+
struct DtkDeclarativePrivateAccessor
31+
{
32+
using MemberPtr = typename Tag::MemberPtr;
33+
friend constexpr MemberPtr get(Tag) noexcept;
34+
};
35+
36+
template<typename Tag, typename Tag::MemberPtr Ptr>
37+
struct DtkDeclarativePrivateAccessorImpl : DtkDeclarativePrivateAccessor<Tag>
38+
{
39+
friend constexpr typename Tag::MemberPtr get(Tag) noexcept { return Ptr; }
40+
};
41+
42+
QT_WARNING_POP
43+
44+
#define D_DECLARE_PRIVATE_MEMBER(TagName, ClassName, Member, MemberType) \
45+
struct TagName { \
46+
using MemberPtr = MemberType ClassName::*; \
47+
friend constexpr MemberPtr get(TagName) noexcept; \
48+
}; \
49+
template struct DtkDeclarativePrivateAccessorImpl<TagName, &ClassName::Member>
50+
51+
#define D_DECLARE_PRIVATE_METHOD(TagName, ClassName, MethodName, RetType, ...) \
52+
struct TagName { \
53+
using MemberPtr = RetType (ClassName::*)(__VA_ARGS__); \
54+
friend constexpr MemberPtr get(TagName) noexcept; \
55+
}; \
56+
template struct DtkDeclarativePrivateAccessorImpl<TagName, &ClassName::MethodName>
57+
58+
#define D_DECLARE_PRIVATE_CONST_METHOD(TagName, ClassName, MethodName, RetType, ...) \
59+
struct TagName { \
60+
using MemberPtr = RetType (ClassName::*)(__VA_ARGS__) const; \
61+
friend constexpr MemberPtr get(TagName) noexcept; \
62+
}; \
63+
template struct DtkDeclarativePrivateAccessorImpl<TagName, &ClassName::MethodName>
64+
65+
#define D_DECLARE_PRIVATE_BITFIELD(TagName, ClassName, PrevMember, PrevMemberType, BfStorageType) \
66+
struct TagName { \
67+
using MemberPtr = PrevMemberType ClassName::*; \
68+
friend constexpr MemberPtr get(TagName) noexcept; \
69+
}; \
70+
template struct DtkDeclarativePrivateAccessorImpl<TagName, &ClassName::PrevMember>; \
71+
struct TagName##_bf_layout_mirror { PrevMemberType prev; BfStorageType storage; }; \
72+
struct TagName##_Bits { \
73+
static constexpr std::size_t kOffset = \
74+
__builtin_offsetof(TagName##_bf_layout_mirror, storage) - \
75+
__builtin_offsetof(TagName##_bf_layout_mirror, prev); \
76+
static BfStorageType *storagePtr(ClassName *obj) noexcept { \
77+
PrevMemberType &prev_ref = (*obj).*get(TagName{}); \
78+
return reinterpret_cast<BfStorageType*>( \
79+
reinterpret_cast<char *>(&prev_ref) + kOffset); \
80+
} \
81+
static const BfStorageType *storagePtr(const ClassName *obj) noexcept { \
82+
return storagePtr(const_cast<ClassName *>(obj)); \
83+
} \
84+
static bool getBit(const ClassName *obj, unsigned bit) noexcept { \
85+
return (*storagePtr(obj) >> bit) & BfStorageType{1}; \
86+
} \
87+
static void setBit(ClassName *obj, unsigned bit, bool val) noexcept { \
88+
BfStorageType *p = storagePtr(obj); \
89+
if (val) *p |= (BfStorageType{1} << bit); \
90+
else *p &= ~(BfStorageType{1} << bit); \
91+
} \
92+
}
93+
94+
// Trampoline: ensures get(tag) is called from a context with no class-scope
95+
// get() member that might suppress ADL (C++ [basic.lookup.argdep] para 3).
96+
namespace dtk_private_detail {
97+
template<typename Tag>
98+
inline typename Tag::MemberPtr access(Tag t) noexcept { return get(t); }
99+
}
100+
101+
#define D_PRIVATE_MEMBER(obj, tag) ((obj).*dtk_private_detail::access(tag))
102+
#define D_PRIVATE_CALL(obj, tag, ...) ((obj).*dtk_private_detail::access(tag))(__VA_ARGS__)
103+
#define D_PRIVATE_BF_GET(obj, TagName, bit_pos) \
104+
TagName##_Bits::getBit(&(obj), (bit_pos))
105+
#define D_PRIVATE_BF_SET(obj, TagName, bit_pos, val) \
106+
TagName##_Bits::setBit(&(obj), (bit_pos), (val))

0 commit comments

Comments
 (0)