-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
pimpl
103 lines (87 loc) · 5.16 KB
/
pimpl
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/*
Copyright (C) 2018-2024 Geoffrey Daniels. https://gpdaniels.com/
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef GTL_UTILITY_PIMPL_HPP
#define GTL_UTILITY_PIMPL_HPP
// Summary: Statically sized pointer to implementation wrapper.
namespace gtl {
// Forward declare class to allow a unique definition of operator new using it as a parameter.
template <typename implemented_class, unsigned long long int implementation_size, unsigned long long alignment_size>
class pimpl;
}
namespace {
// Operator new requires the size_t type for its size argument.
using size_t = decltype(sizeof(0));
}
/// @brief Custom placement operator new to avoid including the (massive) <new> header.
/// @tparam implemented_class The class type that is being extended by the pimpl.
/// @tparam implementation_size The size of the internal pimpl implementataion.
/// @tparam alignment_size Alignment of the data in the pimpl implementation.
/// @param size The size of the data to placement new on.
/// @param pointer The pointer of the data to placement new on.
/// @param unused_type_tag An unused type tag used to make this placement new operator function unique.
template <typename implemented_class, unsigned long long int implementation_size, unsigned long long alignment_size = implementation_size>
inline void* operator new(size_t size, void* pointer, gtl::pimpl<implemented_class, implementation_size, alignment_size>* unused_type_tag) {
static_cast<void>(size);
static_cast<void>(unused_type_tag);
return pointer;
}
/// @brief Custom placement operator delete to avoid compilers complaing about potential memory leaks.
/// @tparam implemented_class The class type that is being extended by the pimpl.
/// @tparam implementation_size The size of the internal pimpl implementataion.
/// @tparam alignment_size Alignment of the data in the pimpl implementation.
/// @param data The pointer of the data to placement delete on.
/// @param pointer The pointer of the data to placement delete on.
/// @param unused_type_tag An unused type tag used to make this placement delete operator function unique.
template <typename implemented_class, unsigned long long int implementation_size, unsigned long long alignment_size = implementation_size>
inline void operator delete(void* data, void* pointer, gtl::pimpl<implemented_class, implementation_size, alignment_size>* unused_type_tag) {
static_cast<void>(data);
static_cast<void>(pointer);
static_cast<void>(unused_type_tag);
}
namespace gtl {
/// @brief pimpl is a sized pointer to implementation class to enable hiding implemtation details.
/// @tparam implemented_class The class type that is being extended by the pimpl.
/// @tparam implementation_size The size of the internal pimpl implementataion.
/// @tparam alignment_size Alignment of the data in the pimpl implementation.
template <typename implemented_class, unsigned long long int implementation_size, unsigned long long alignment_size = implementation_size>
class pimpl {
private:
/// @brief The implementation class is declared here to allow operations on it in this class.
class implementation;
private:
/// @brief The raw memory of the implementation filled using placement new.
alignas(alignment_size) unsigned char implementation_data[implementation_size];
public:
/// @brief Destructor calls the destructor of the implementation.
~pimpl() {
reinterpret_cast<implementation*>(&this->implementation_data[0])->~implementation();
}
/// @brief Constructor forwards arguments to the constructor of the implementation.
/// @param arguments The arguments to forward to the constructor of the implementation.
template<typename ... argument_types>
pimpl(argument_types&& ... arguments) {
static_assert(sizeof(implementation) == implementation_size, "The implementation must have the same size as the pre-allocated array.");
new (this->implementation_data, static_cast<pimpl*>(nullptr)) implementation(static_cast<argument_types&&>(arguments)...);
}
/// @brief Member access operator to allow easy use of internal implementation members by using "(*this)->".
implementation* operator->() {
return reinterpret_cast<implementation*>(this->implementation_data);
}
/// @brief Constant member access operator to allow easy use of internal implementation members by using "(*this)->".
const implementation* operator->() const {
return reinterpret_cast<const implementation*>(this->implementation_data);
}
};
}
#endif // GTL_UTILITY_PIMPL_HPP