Line data Source code
1 : // Copyright (c) 2018-2023 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snapdev
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 : #pragma once
19 :
20 : /** \file
21 : * \brief A set of templates to help with RAII.
22 : *
23 : * Resources that are pointers to C++ objects can easily be managed
24 : * with std::unique_ptr<>() and std::shared_ptr<>().
25 : *
26 : * Pointers to objects which are not C++ objects can be managed
27 : * by std::unique_ptr<>() and a deleter. To help with that case,
28 : * though, we add a template class which allows us to create a
29 : * typedef. Otherwise you have to specify the deleter to the
30 : * std::unique_ptr<>() each time you allocate such an object!
31 : *
32 : * Finally, many resources use a variable type which is not
33 : * a pointer at all. For those, it becomes very annoying to use
34 : * an std::unique_ptr<>() _by hand_ each time. Each instance
35 : * need to have access to that resource type deleter. Instead,
36 : * we offer a template which incorporates the deleter in one
37 : * typedef and it handles all the work of whether the value
38 : * is considered to be nullptr (i.e. a file descriptor obtained
39 : * with `open(2)` returns -1 as an equivalent to a nullptr.)
40 : */
41 :
42 : // snapdev
43 : //
44 : #include <snapdev/not_used.h>
45 :
46 :
47 : // C++
48 : //
49 : #include <memory>
50 : #include <iostream>
51 :
52 :
53 : // C
54 : //
55 : #include <unistd.h>
56 : #include <stdio.h>
57 :
58 :
59 :
60 : namespace snapdev
61 : {
62 :
63 :
64 : /** \brief A template used to allow unique_ptr<>() of nearly any type.
65 : *
66 : * This template is used with nearly any type representing a resource
67 : * that you would like to automatically delete (RAII) by using a
68 : * unique_ptr<>() definition.
69 : *
70 : * Note that it only works with resources which type is not a pointer.
71 : * For example, a file descriptor, which uses an `int`, works with this
72 : * class.
73 : *
74 : * For resources that have a pointer, use the raii_pointer_deleter instead.
75 : *
76 : * For example, a file descriptor can use this template class as follow:
77 : *
78 : * \code
79 : * typedef std::unique_ptr<int, snap::raii_generic_deleter<int, -1, decltype(&::close), &::close>> raii_fd_t;
80 : *
81 : * raii_fd_t fd(open("/tmp/test.tmp", O_RDWR));
82 : * \endcode
83 : *
84 : * \note
85 : * We actually offer the raii_fd_t as a default since it can be used by
86 : * many other implementations.
87 : *
88 : * To access the value, use the `get()` function of your pointer.
89 : * For example:
90 : *
91 : * \code
92 : * raii_fd_t safe_fd;
93 : * safe_fd.reset(socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
94 : * std::cout << safe_fd.get() << std::endl;
95 : * write(safe_fd.get(), "MSG\n", 4);
96 : * \endcode
97 : *
98 : * \warning
99 : * This class doesn't properly handle a case where an invalid resource can
100 : * be represented by more than one value. For a file descriptor, we expect
101 : * the invalid value to be exactly -1. If a function call may return other
102 : * negative values, then this implementation currently fails to handle such.
103 : * It is your responsibility to fix the return value of the function or
104 : * use a two step initialization such as in:
105 : *
106 : * \code
107 : * int r(this_func("open/something", "rw"));
108 : * if(r < 0)
109 : * {
110 : * return; // it failed. r may not just be -1
111 : * }
112 : * raii_fd_t safe_fd(r);
113 : * ...
114 : * \endcode
115 : *
116 : * \note
117 : * This template class is based on code found in this answer on Stackoverflow:
118 : * https://stackoverflow.com/questions/15756960/using-unique-ptr-to-control-a-file-descriptor#48575395
119 : *
120 : * \tparam T The type being managed such as 'int'.
121 : * \param[in] null_value The value representing "nullptr" for that type. For
122 : * a file descriptor this is -1.
123 : * \tparam D The type of the deleter function.
124 : * \param[in] deleter The deleter function (i.e. 'deleter(p)' gets rid of `T p`).
125 : */
126 : template<class T, T null_value, class D, D deleter>
127 : class raii_generic_deleter
128 : {
129 : public:
130 : /** \brief The pointer class.
131 : *
132 : * This class must be called "pointer" because the unique_ptr<>()
133 : * template definition checks the deleter using the following
134 : * code:
135 : *
136 : * \code
137 : * template<typename _Up>
138 : * static typename _Up::pointer __test(typename _Up::pointer*);
139 : * [...]
140 : * typedef decltype(__test<_Del>(0)) type;
141 : * \endcode
142 : *
143 : * As we can see it looks for a class named pointer. If it exists,
144 : * then it defines the type using that pointer class pointer operator
145 : * (i.e. the `\<type> operator *`) instead of using an actual pointer
146 : * (i.e. it uses `deleter::pointer` instead of the default `T *`.)
147 : *
148 : * The C++ class is defined as a NullablePointer. It also needs one
149 : * operator function to retrieve the pointer (operator *).
150 : *
151 : * \see
152 : * https://en.cppreference.com/w/cpp/named_req/NullablePointer
153 : */
154 : class pointer
155 : {
156 : private:
157 : /** \brief The pointer being manage.
158 : *
159 : * This parameter is the resource pointer being managed.
160 : */
161 : T f_pointer = null_value;
162 :
163 : public:
164 : /** \brief Create a pointer with a value of type T.
165 : *
166 : * This constructor creates an explicit instance of a pointer with
167 : * a type T object.
168 : *
169 : * \param[in] p The pointer being managed by this pointer class.
170 : */
171 1 : pointer(T p)
172 1 : : f_pointer(p)
173 : {
174 1 : }
175 :
176 : /** \brief Allow assigning the nullptr to this type.
177 : *
178 : * This constructor allows for the nullptr value to be assigned to
179 : * this type. This is a test run at compile time. If not present
180 : * the resulting std::unique_ptr<>() would not compile.
181 : *
182 : * \param[in] p The null pointer.
183 : */
184 2 : pointer(std::nullptr_t p = nullptr)
185 2 : : f_pointer(null_value)
186 : {
187 2 : NOT_USED(p);
188 2 : }
189 :
190 :
191 : // /** \brief Assign the nullptr to this pointer.
192 : // *
193 : // * This function is used to assign `nullptr` to this pointer.
194 : // *
195 : // * \warning
196 : // * It is assumed that this is called only when the pointer
197 : // * was released by a call to the `deleter` function.
198 : // *
199 : // * \param[in] h The `nullptr` being assigned.
200 : // *
201 : // * \return A reference to this pointer.
202 : // */
203 : // pointer & operator = (std::nullptr_t h)
204 : // {
205 : // NOT_USED(h);
206 : //
207 : // f_pointer = null_value;
208 : //
209 : // return *this;
210 : // }
211 :
212 : /** \brief Convert pointer to bool.
213 : *
214 : * This operator allows for converting the pointer to a bool
215 : * which is often done in an `if(ptr)` or equivalent statement.
216 : *
217 : * \return true if the pointer is not `null_value`.
218 : */
219 : explicit operator bool () const
220 : {
221 : return f_pointer != null_value;
222 : }
223 :
224 : /** \brief Convert pointer to "not bool".
225 : *
226 : * This operator allows for converting the pointer to a bool
227 : * when preceded by a Boolean NOT operator, which is often
228 : * done in an `if(!ptr)` or equivalent statement.
229 : *
230 : * \return true if the pointer is `null_value`.
231 : */
232 : bool operator ! () const
233 : {
234 : return f_pointer == null_value;
235 : }
236 :
237 : /** \brief Compare two pointers against each other for equality.
238 : *
239 : * This function compares the 'this' and \p rhs pointers against
240 : * each other. If equal then it returns true.
241 : *
242 : * \param[in] rhs The right hand side pointer being compared against
243 : * `this` pointer.
244 : *
245 : * \return true if both pointers are equal.
246 : */
247 : bool operator == (pointer const rhs) const
248 : {
249 : return f_pointer == rhs.f_pointer;
250 : }
251 :
252 : /** \brief Compare two pointers against each other for inequality.
253 : *
254 : * This function compares the `this` and \p rhs pointers against
255 : * each other. If not equal then it returns true.
256 : *
257 : * \param[in] rhs The right hand side pointer being compared against
258 : * `this` pointer.
259 : *
260 : * \return true if both pointers are not equal.
261 : */
262 : bool operator != (pointer const rhs) const
263 : {
264 : return f_pointer != rhs.f_pointer;
265 : }
266 :
267 : /** \brief Compare this pointer against nullptr for equality.
268 : *
269 : * This function is used whenever this pointer gets compared
270 : * against the special value `nullptr`.
271 : *
272 : * \note
273 : * From what I've seen, this function doesn't get called.
274 : * Instead, they do: "_internal_ptr == pointer()".
275 : *
276 : * \param[in] rhs The nullptr.
277 : *
278 : * \return true when this pointer is the `nullptr`.
279 : */
280 : bool operator == (std::nullptr_t rhs) const
281 : {
282 : NOT_USED(rhs);
283 : return f_pointer == null_value;
284 : }
285 :
286 : /** \brief Compare this pointer against nullptr for inequality.
287 : *
288 : * This function is used whenever this pointer gets compared
289 : * against the special value `nullptr` for inequality.
290 : *
291 : * \note
292 : * This function gets called in many circumstances where the
293 : * unique_ptr<>() implementation checks whether the pointer
294 : * is null or not. It's a simplification that may help the
295 : * compiler.
296 : *
297 : * \param[in] rhs The nullptr.
298 : *
299 : * \return true when this pointer is not the `nullptr`.
300 : */
301 1 : bool operator != (std::nullptr_t rhs) const
302 : {
303 1 : NOT_USED(rhs);
304 1 : return f_pointer != null_value;
305 : }
306 :
307 : /** \brief Retrieve the "pointer".
308 : *
309 : * This function is used to retrieve the "pointer" value.
310 : *
311 : * \note
312 : * This class does not offer an `operator * ()` because that
313 : * would need to return a reference to the value which is not
314 : * possible with a value other than an actual real pointer.
315 : * Otherwise it becomes a reference to a temporary pointer.
316 : * This is because unique_ptr<>() is implemented as:
317 : *
318 : * \code
319 : * pointer get() const { ...}
320 : * reference operator * () const { return get(); }
321 : * \endcode
322 : *
323 : * \par
324 : * As we can see, the `get()` returns a `pointer` and the
325 : * `operator * ()` would return a reference on that temporary.
326 : *
327 : * \return A copy of the resource "pointer".
328 : */
329 1 : operator T () const
330 : {
331 1 : return f_pointer;
332 : }
333 : };
334 :
335 : /** \brief The function called to delete/release this pointer.
336 : *
337 : * This function gets called whenever the unique_ptr<>() or
338 : * shared_ptr<>() decides to delete the pointer.
339 : *
340 : * \note
341 : * The function gets called only if p does not represent nullptr
342 : * and the handle gets cleaned in the unique_ptr<>() as required.
343 : *
344 : * \param[in] p The pointer to delete.
345 : */
346 1 : void operator () (pointer p)
347 : {
348 : // we use static_cast<T>(p) in case the deleter does not
349 : // use a clear type as its parameter (these damn C functions...)
350 : //
351 1 : deleter(static_cast<T>(p));
352 1 : }
353 : };
354 :
355 :
356 : /** \brief Define a smart pointer for `fd`.
357 : *
358 : * The Unix system uses many file descriptors. This declaration
359 : * automatically handles the close() of the specified `fd` resource.
360 : *
361 : * It is expected that the "null pointer" is -1.
362 : */
363 : typedef std::unique_ptr<int, raii_generic_deleter<int, -1, decltype(&::close), &::close>> raii_fd_t;
364 :
365 :
366 :
367 : /** \brief A templated used to easily delete a unique_ptr<>() resource.
368 : *
369 : * This template is used with any type if resources represented by a pointer.
370 : * It automatically deletes (RAII) the resource one the unique_ptr<>()
371 : * goes out of scope.
372 : *
373 : * Note that it only works with resources which type is a pointer.
374 : * For example, a FILE object which uses a `FILE *` works with this
375 : * class.
376 : *
377 : * For resources that do not have a pointer, use the raii_generic_deleter
378 : * instead.
379 : *
380 : * For example, a FILE object can use this template class as follow:
381 : *
382 : * \code
383 : * typedef std::unique_ptr<FILE, raii_pointer_deleter<FILE, decltype(&::fclose), &::fclose>> raii_file;
384 : *
385 : * raii_file f(fopen("/tmp/test.tmp", "rw")); // f is automatically closed on a return or an interrupt
386 : * \endcode
387 : *
388 : * \tparam T The type being managed such as 'int'.
389 : * \tparam D The type of the deleter function.
390 : * \param[in] deleter The deleter function (i.e. 'deleter(p)' gets rid of `T * p`).
391 : */
392 : template<class T, class D, D deleter>
393 : class raii_pointer_deleter
394 : {
395 : public:
396 : /** \brief The function called to delete/release this type of pointer.
397 : *
398 : * This function gets called whenever the unique_ptr<>() or
399 : * shared_ptr<>() decides to delete the pointer.
400 : *
401 : * \note
402 : * The function gets called only if p does not represent nullptr
403 : * and the pointer gets cleaned in the unique_ptr<>() as required.
404 : *
405 : * \param[in] p The pointer to delete.
406 : */
407 67 : void operator () (T * p)
408 : {
409 : deleter(p);
410 67 : }
411 : };
412 :
413 :
414 : /** \brief A shared pointer with a deleter.
415 : *
416 : * A big problem with the std::shared_ptr<> is that it does not accept a
417 : * deleter, somehow. Yet, there should be no reason for such a limitation.
418 : * This class allows you to create generic deleter typedefs of smart
419 : * pointers with deleters.
420 : *
421 : * \code
422 : * // we need a special deleter because of the required pointer to pointer
423 : * void av_frame_free_ptr(AVFrame * ptr)
424 : * {
425 : * ::av_frame_free(&ptr);
426 : * }
427 : * typedef snap::raii_pointer_deleter<AVFrame, decltype(&av_frame_free_ptr), &av_frame_free_ptr> av_frame_deleter_t;
428 : * typedef snap::shared_ptr_with_deleter<AVFrame, av_frame_deleter_t> av_frame_t;
429 : * \endcode
430 : *
431 : * Source:
432 : *
433 : * https://stackoverflow.com/questions/27331315
434 : */
435 : template<class T, class D = std::default_delete<T>>
436 : struct shared_ptr_with_deleter
437 : : public std::shared_ptr<T>
438 : {
439 : /** \brief The constructor accepts a type t.
440 : *
441 : * Create the shared pointer with the deleter as specified in the
442 : * template.
443 : *
444 : * \param[in] t The bare pointer to save in this smart pointer.
445 : */
446 : explicit shared_ptr_with_deleter(T * t = nullptr)
447 : : std::shared_ptr<T>(t, D())
448 : {
449 : }
450 :
451 : /** \brief Call the reset function with the deleter.
452 : *
453 : * The std::shared_ptr<>() reset function also doesn't know anything
454 : * about the deleter and it has to be specified on the call.
455 : *
456 : * \param[in] t Another bare pointer to save in this smart pointer.
457 : */
458 : void reset(T* t = nullptr)
459 : {
460 : std::shared_ptr<T>::reset(t, D());
461 : }
462 : };
463 :
464 :
465 : /** \brief Handle the closure of a FILE handle.
466 : *
467 : * One of the common type of file handle is the FILE object. It manages
468 : * a file including an efficient client side buffering mechanism.
469 : *
470 : * This typedef makes sure that the file gets closed whenever the
471 : * handle goes out of scope.
472 : *
473 : * This is a pointer so the null (a.k.a. closed, already released) is
474 : * expected to be represented by a nullptr.
475 : */
476 : typedef std::unique_ptr<FILE, raii_pointer_deleter<FILE, decltype(&::fclose), &::fclose>> raii_file_t;
477 :
478 :
479 : } // namespace snapdev
480 : // vim: ts=4 sw=4 et
|