PIMPL Development Standard¶
SARibbon uses the PIMPL (Private Implementation) pattern, encapsulating implementation details in an internal PrivateData class while exposing a public interface. This document details the PIMPL macro usage and coding conventions in SARibbon.
Why This Standard Exists¶
The PIMPL pattern is a common practice in Qt projects. It provides:
- Hidden implementation details: Reduces header file compilation dependencies, speeds up compilation
- Stable ABI: Modifying private members does not affect binary compatibility
- Encapsulated internal data: Public interface stays clear, private implementation can change freely
PIMPL Macro Reference¶
The macros required for SARibbon's PIMPL pattern live in src/SARibbonBar/SARibbonGlobal.h. The main macros are:
SA_RIBBON_DECLARE_PRIVATE¶
Declares the PIMPL private member in the class. It generates:
- Forward declaration of the internal class
PrivateData - Mutual friend declaration
std::unique_ptr<PrivateData> d_ptrmember variable
Macro definition (SARibbonGlobal.h:83-88):
1 2 3 4 | |
Usage. Place in the header file class definition, immediately after Q_OBJECT:
1 2 3 4 5 6 7 8 | |
Note
SA_RIBBON_DECLARE_PRIVATE uses std::unique_ptr<PrivateData> rather than Qt's QScopedPointer or raw pointers. This means the PrivateData destructor must be visible in the .cpp file, otherwise a compilation error occurs.
SA_RIBBON_DECLARE_PUBLIC¶
Declares the public member in the internal PrivateData class. It generates:
- Mutual friend declaration (allows PrivateData to access the owner class's private members)
classname* q_ptr { nullptr }back-pointer member variable- Deleted copy constructor and assignment operator
Macro definition (SARibbonGlobal.h:103-109):
1 2 3 4 5 | |
Usage. Place in the .cpp file within the PrivateData class definition:
1 2 3 4 5 6 7 8 | |
SA_RIBBON_IMPL_CONSTRUCT¶
A convenience macro for constructing PrivateData in the constructor initializer list:
Macro definition (SARibbonGlobal.h:122-124):
1 | |
Expanded equivalent:
1 2 3 4 | |
Construction pattern in practice
SA_RIBBON_IMPL_CONSTRUCT exists in SARibbonGlobal.h but is never used in the actual codebase. All real constructors use the explicit pattern d_ptr(new X::PrivateData(this)) instead of std::make_unique. Follow the actual code pattern, not the macro.
SA_D and SA_DC¶
Convenience macros for obtaining the d_ptr pointer:
- SA_D: Obtains
PrivateData*in non-const functions - SA_DC: Obtains
const PrivateData*in const functions
Macro definition (SARibbonGlobal.h:137-154):
1 2 | |
Usage example (SARibbonBar.cpp:1493-1496):
1 2 3 4 5 6 7 8 | |
Example from SARibbonMainWindow.cpp:202-207:
1 2 3 4 5 6 7 | |
SA_Q and SA_QC¶
Convenience macros for obtaining the q_ptr pointer from within PrivateData methods:
- SA_Q: Obtains non-const pointer to
q_ptr - SA_QC: Obtains const pointer to
q_ptr
Macro definition (SARibbonGlobal.h:167-184):
1 2 | |
SA_Q / SA_QC are unused
These macros are defined in SARibbonGlobal.h but have zero usages in the entire codebase. They are documented here for completeness. Existing code accesses the public class directly via q_ptr.
Usage¶
Complete PIMPL Class Structure¶
Below is the full class structure example using the PIMPL pattern in SARibbon, based on actual SARibbonCategory code:
Header file (.h) -- SARibbonCategory.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | |
Source file (.cpp) -- SARibbonCategory.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | |
PIMPL Usage Flow¶
flowchart TD
A[Header declaration SA_RIBBON_DECLARE_PRIVATE] --> B[Define PrivateData internal class in .cpp]
B --> C[Declare SA_RIBBON_DECLARE_PUBLIC in PrivateData]
C --> D[Constructor uses SA_RIBBON_IMPL_CONSTRUCT or manual initialization]
D --> E[Use SA_D / SA_DC / q_ptr in functions]
Notes
SA_Dis for non-const functions,SA_DCfor const functions. Do not mix them- The variable name
dis convention, but other names are acceptable - The
PrivateDataclass definition lives in the.cppfile, never in the header. This ensures private members are fully hidden - Use
///<trailing comments for member descriptions inPrivateData(e.g.bool isContextCategory { false }; ///< Whether this is a context category)
If a class uses PIMPL, the header file must never contain private member variable definitions. All private members belong in the PrivateData class.
References¶
- Related standards: Qt Integration Standard, Code Style and Commenting Standard
- Source location:
src/SARibbonBar/SARibbonGlobal.h(contains all PIMPL macro definitions)