Commit | Line | Data |
---|---|---|
db5a1938 PP |
1 | /* |
2 | * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com> | |
3 | * | |
4 | * SPDX-License-Identifier: MIT | |
5 | */ | |
6 | ||
7 | #ifndef BABELTRACE_CPP_COMMON_BT2_OPTIONAL_BORROWED_OBJECT_HPP | |
8 | #define BABELTRACE_CPP_COMMON_BT2_OPTIONAL_BORROWED_OBJECT_HPP | |
9 | ||
10 | #include <type_traits> | |
11 | ||
12 | #include "common/assert.h" | |
13 | ||
14 | #include "borrowed-object-proxy.hpp" | |
15 | ||
16 | namespace bt2 { | |
17 | ||
18 | /* | |
19 | * An instance of this class template manages an optional contained | |
20 | * borrowed object of type `ObjT`, that is, a borrowed object that may | |
21 | * or may not be present. | |
22 | * | |
23 | * Such an object considers that a `nullptr` libbabeltrace2 object | |
24 | * pointer means none. Therefore, using an `OptionalBorrowedObject` | |
25 | * isn't more costly, in time and space, as using a libbabeltrace2 | |
26 | * object pointer in C, but offers the typical C++ optional interface. | |
27 | * | |
28 | * There's no `bt2s::nullopt` equivalent: just use the default | |
29 | * constructor or call reset(). | |
30 | * | |
31 | * There are a constructors an assignment operators which accept an | |
32 | * instance of another wrapper object or of another optional borrowed | |
33 | * object. For those, static assertions make sure that the assignment | |
34 | * would work with borrowed objects, for example: | |
35 | * | |
36 | * auto sharedIntVal = createValue(23L); | |
37 | * | |
38 | * OptionalBorrowedObject<Value> myVal {*sharedIntVal}; | |
39 | * | |
40 | * This is needed because `OptionalBorrowedObject` only keeps a | |
41 | * libbabeltrace2 pointer (`_mLibObjPtr`), therefore it doesn't | |
42 | * automatically know the relation between wrapper classes. | |
43 | */ | |
44 | template <typename ObjT> | |
45 | class OptionalBorrowedObject final | |
46 | { | |
47 | public: | |
48 | using Obj = ObjT; | |
49 | using LibObjPtr = typename ObjT::LibObjPtr; | |
50 | ||
51 | /* | |
52 | * Builds an optional borrowed object without an object. | |
53 | * | |
54 | * Intentionally not explicit. | |
55 | */ | |
56 | OptionalBorrowedObject() noexcept = default; | |
57 | ||
58 | /* | |
59 | * Builds an optional borrowed object with an instance of `ObjT` | |
60 | * wrapping the libbabeltrace2 pointer `libObjPtr`. | |
61 | * | |
62 | * Intentionally not explicit. | |
63 | */ | |
64 | OptionalBorrowedObject(const LibObjPtr libObjPtr) noexcept : _mLibObjPtr {libObjPtr} | |
65 | { | |
66 | } | |
67 | ||
68 | /* | |
69 | * Builds an optional borrowed object with an instance of `ObjT` | |
70 | * constructed from `obj`. | |
71 | * | |
72 | * It must be possible to construct an instance of `ObjT` with an | |
73 | * instance of `OtherObjT`. | |
74 | * | |
75 | * Intentionally not explicit. | |
76 | */ | |
77 | template <typename OtherObjT> | |
78 | OptionalBorrowedObject(const OtherObjT obj) noexcept : _mLibObjPtr {obj.libObjPtr()} | |
79 | { | |
80 | static_assert(std::is_constructible<ObjT, OtherObjT>::value, | |
81 | "`ObjT` is constructible with an instance of `OtherObjT`."); | |
82 | } | |
83 | ||
84 | /* | |
85 | * Builds an optional borrowed object from `optObj`, with or without | |
86 | * an object. | |
87 | * | |
88 | * It must be possible to construct an instance of `ObjT` with an | |
89 | * instance of `OtherObjT`. | |
90 | * | |
91 | * Intentionally not explicit. | |
92 | */ | |
93 | template <typename OtherObjT> | |
94 | OptionalBorrowedObject(const OptionalBorrowedObject<OtherObjT> optObj) noexcept : | |
95 | _mLibObjPtr {optObj.libObjPtr()} | |
96 | { | |
97 | static_assert(std::is_constructible<ObjT, OtherObjT>::value, | |
98 | "`ObjT` is constructible with an instance of `OtherObjT`."); | |
99 | } | |
100 | ||
101 | /* | |
102 | * Makes this optional borrowed object have an instance of `ObjT` | |
103 | * wrapping the libbabeltrace2 pointer `libObjPtr`. | |
104 | */ | |
105 | OptionalBorrowedObject& operator=(const LibObjPtr libObjPtr) noexcept | |
106 | { | |
107 | _mLibObjPtr = libObjPtr; | |
108 | return *this; | |
109 | } | |
110 | ||
111 | /* | |
112 | * Makes this optional borrowed object have an instance of `ObjT` | |
113 | * constructed from `obj`. | |
114 | * | |
115 | * It must be possible to construct an instance of `ObjT` with an | |
116 | * instance of `OtherObjT`. | |
117 | */ | |
118 | template <typename OtherObjT> | |
119 | OptionalBorrowedObject& operator=(const ObjT obj) noexcept | |
120 | { | |
121 | static_assert(std::is_constructible<ObjT, OtherObjT>::value, | |
122 | "`ObjT` is constructible with an instance of `OtherObjT`."); | |
123 | _mLibObjPtr = obj.libObjPtr(); | |
124 | return *this; | |
125 | } | |
126 | ||
127 | /* | |
128 | * Sets this optional borrowed object to `optObj`. | |
129 | * | |
130 | * It must be possible to construct an instance of `ObjT` with an | |
131 | * instance of `OtherObjT`. | |
132 | */ | |
133 | template <typename OtherObjT> | |
134 | OptionalBorrowedObject& operator=(const OptionalBorrowedObject<ObjT> optObj) noexcept | |
135 | { | |
136 | static_assert(std::is_constructible<ObjT, OtherObjT>::value, | |
137 | "`ObjT` is constructible with an instance of `OtherObjT`."); | |
138 | _mLibObjPtr = optObj.libObjPtr(); | |
139 | return *this; | |
140 | } | |
141 | ||
142 | /* Wrapped libbabeltrace2 object pointer (may be `nullptr`) */ | |
143 | LibObjPtr libObjPtr() const noexcept | |
144 | { | |
145 | return _mLibObjPtr; | |
146 | } | |
147 | ||
148 | ObjT object() const noexcept | |
149 | { | |
150 | BT_ASSERT_DBG(_mLibObjPtr); | |
151 | return ObjT {_mLibObjPtr}; | |
152 | } | |
153 | ||
154 | ObjT operator*() const noexcept | |
155 | { | |
156 | return this->object(); | |
157 | } | |
158 | ||
159 | /* | |
160 | * We want to return the address of an `ObjT` instance here, but we | |
161 | * only have a library pointer (`_mLibObjPtr`) because an `ObjT` | |
162 | * instance may not wrap `nullptr`. | |
163 | * | |
164 | * Therefore, return a proxy object which holds an internal | |
165 | * `ObjT` instance and implements operator->() itself. | |
166 | * | |
167 | * In other words, doing `myOptObj->something()` is equivalent to | |
168 | * calling `myOptObj.operator->().operator->()->something()`. | |
169 | */ | |
170 | BorrowedObjectProxy<ObjT> operator->() const noexcept | |
171 | { | |
172 | return BorrowedObjectProxy<ObjT> {_mLibObjPtr}; | |
173 | } | |
174 | ||
175 | bool hasObject() const noexcept | |
176 | { | |
177 | return _mLibObjPtr; | |
178 | } | |
179 | ||
180 | operator bool() const noexcept | |
181 | { | |
182 | return this->hasObject(); | |
183 | } | |
184 | ||
185 | void reset() noexcept | |
186 | { | |
187 | _mLibObjPtr = nullptr; | |
188 | } | |
189 | ||
190 | private: | |
191 | typename ObjT::LibObjPtr _mLibObjPtr = nullptr; | |
192 | }; | |
193 | ||
194 | } /* namespace bt2 */ | |
195 | ||
196 | #endif /* BABELTRACE_CPP_COMMON_BT2_OPTIONAL_BORROWED_OBJECT_HPP */ |