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