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