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: 2023-05-29 16:11:08 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.14