LCOV - code coverage report
Current view: top level - snapdev - raii_generic_deleter.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 17 17 100.0 %
Date: 2022-01-29 18:20:26 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.13