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