 221658299d
			
		
	
	221658299d
	
	
	
		
			
			This is done to make it clear that the class is intended to be used as an unary function.
		
			
				
	
	
		
			300 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			300 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #pragma once
 | |
| /*
 | |
|  * Copyright (c) 2016 MariaDB Corporation Ab
 | |
|  *
 | |
|  * Use of this software is governed by the Business Source License included
 | |
|  * in the LICENSE.TXT file and at www.mariadb.com/bsl11.
 | |
|  *
 | |
|  * Change Date: 2020-01-01
 | |
|  *
 | |
|  * On the date above, in accordance with the Business Source License, use
 | |
|  * of this software will be governed by version 2 or later of the General
 | |
|  * Public License.
 | |
|  */
 | |
| 
 | |
| #include <maxscale/cppdefs.hh>
 | |
| #include <stdio.h>
 | |
| #include <functional>
 | |
| #include <tr1/unordered_map>
 | |
| 
 | |
| namespace maxscale
 | |
| {
 | |
| 
 | |
| /**
 | |
|  * @class CloserTraits utils.hh <maxscale/utils.hh>
 | |
|  *
 | |
|  * A traits class used by Closer. To be specialized for all types that are
 | |
|  * used with Closer.
 | |
|  */
 | |
| template<class T>
 | |
| struct CloserTraits
 | |
| {
 | |
|     /**
 | |
|      * Closes/frees/destroys a resource.
 | |
|      *
 | |
|      * @param t  Close the resource *if* it has not been closed already.
 | |
|      */
 | |
|     static void close_if(T t);
 | |
| 
 | |
|     /**
 | |
|      * Resets a reference to a resource. After the call, the value of t should
 | |
|      * be such that @c close_if can recognize that the reference already has
 | |
|      * been closed.
 | |
|      *
 | |
|      * @param t  Reference to a resource.
 | |
|      */
 | |
|     static void reset(T& t);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * @class Closer utils.hh <maxscale/utils.hh>
 | |
|  *
 | |
|  * The template class Closer is a class that is intended to be used
 | |
|  * for ensuring that a C style resource is released at the end of a
 | |
|  * scoped block, irrespective of how that block is exited (by reaching
 | |
|  * the end of it, or by a return or exception in the middle of it).
 | |
|  *
 | |
|  * Closer performs the actual resource releasing using CloserTraits
 | |
|  * that need to be specialized for every type of resource to be managed.
 | |
|  *
 | |
|  * Example:
 | |
|  * @code
 | |
|  * void f()
 | |
|  * {
 | |
|  *     FILE* pFile = fopen(...);
 | |
|  *
 | |
|  *     if (pFile)
 | |
|  *     {
 | |
|  *         Closer<FILE*> file(pFile);
 | |
|  *
 | |
|  *         // Use pFile, call functions that potentually may throw
 | |
|  *     }
 | |
|  * }
 | |
|  * @endcode
 | |
|  *
 | |
|  * Without @c Closer all code would have to be placed within try/catch
 | |
|  * blocks, which quickly becomes unwieldy as the number of managed
 | |
|  * resources grows.
 | |
|  */
 | |
| template<class T>
 | |
| class Closer
 | |
| {
 | |
| public:
 | |
|     /**
 | |
|      * Creates the closer and stores the provided resourece. Note that
 | |
|      * the constructor assumes that the resource exists already.
 | |
|      *
 | |
|      * @param resource  The resource whose closing is to be ensured.
 | |
|      */
 | |
|     Closer(T resource)
 | |
|         : m_resource(resource)
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Destroys the closer and releases the resource.
 | |
|      */
 | |
|     ~Closer()
 | |
|     {
 | |
|         CloserTraits<T>::close_if(m_resource);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the original resource. Note that the ownership of the
 | |
|      * resource remains with the closer.
 | |
|      *
 | |
|      * @return The resource that was provided in the constructor.
 | |
|      */
 | |
|     T get() const
 | |
|     {
 | |
|         return m_resource;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Resets the closer, that is, releases the resource.
 | |
|      */
 | |
|     void reset()
 | |
|     {
 | |
|         CloserTraits<T>::close_if(m_resource);
 | |
|         CloserTraits<T>::reset(m_resource);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Resets the closer, that is, releases the resource and assigns a
 | |
|      * new resource to it.
 | |
|      */
 | |
|     void reset(T resource)
 | |
|     {
 | |
|         CloserTraits<T>::close_if(m_resource);
 | |
|         m_resource = resource;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the original resource together with its ownership. That is,
 | |
|      * after this call the responsibility for releasing the resource belongs
 | |
|      * to the caller.
 | |
|      *
 | |
|      * @return The resource that was provided in the constructor.
 | |
|      */
 | |
|     T release()
 | |
|     {
 | |
|         T resource = m_resource;
 | |
|         CloserTraits<T>::reset(m_resource);
 | |
|         return resource;
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     Closer(const Closer&);
 | |
|     Closer& operator = (const Closer&);
 | |
| 
 | |
| private:
 | |
|     T m_resource;
 | |
| };
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| namespace maxscale
 | |
| {
 | |
| 
 | |
| /**
 | |
|  * @class CloserTraits<FILE*> utils.hh <maxscale/utils.hh>
 | |
|  *
 | |
|  * Specialization of @c CloserTraits for @c FILE*.
 | |
|  */
 | |
| template<>
 | |
| struct CloserTraits<FILE*>
 | |
| {
 | |
|     static void close_if(FILE* pFile)
 | |
|     {
 | |
|         if (pFile)
 | |
|         {
 | |
|             fclose(pFile);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     static void reset(FILE*& pFile)
 | |
|     {
 | |
|         pFile = NULL;
 | |
|     }
 | |
| };
 | |
| 
 | |
| /* Helper type for Registry. Must be specialized for each EntryType. The types
 | |
|  * listed below are just examples and will not compile. */
 | |
| template<typename EntryType>
 | |
| struct RegistryTraits
 | |
| {
 | |
|     typedef int id_type;
 | |
|     typedef EntryType* entry_type;
 | |
| 
 | |
|     static id_type get_id(entry_type entry)
 | |
|     {
 | |
|         static_assert(sizeof(EntryType) != sizeof(EntryType), "get_id() and the"
 | |
|                 " surrounding struct must be specialized for every EntryType!");
 | |
|         return 0;
 | |
|     }
 | |
|     static entry_type null_entry()
 | |
|     {
 | |
|         return NULL;
 | |
|     }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Class Registy wraps a map, allowing only a few operations on it. The intended
 | |
|  * use is simple registries, such as the session registry in Worker. The owner
 | |
|  * can expose a reference to this class without exposing all the methods the
 | |
|  * underlying container implements. When instantiating with a new EntryType, the
 | |
|  * traits-class RegistryTraits should be specialized for the new type as well.
 | |
|  */
 | |
| template <typename EntryType>
 | |
| class Registry
 | |
| {
 | |
|     Registry(const Registry&);
 | |
|     Registry& operator = (const Registry&);
 | |
| public:
 | |
|     typedef typename RegistryTraits<EntryType>::id_type id_type;
 | |
|     typedef typename RegistryTraits<EntryType>::entry_type entry_type;
 | |
| 
 | |
|     Registry()
 | |
|     {
 | |
|     }
 | |
|     /**
 | |
|      * Find an entry in the registry.
 | |
|      *
 | |
|      * @param id Entry key
 | |
|      * @return The found entry, or NULL if not found
 | |
|      */
 | |
|     entry_type lookup(id_type id) const
 | |
|     {
 | |
|         entry_type rval = RegistryTraits<EntryType>::null_entry();
 | |
|         typename ContainerType::const_iterator iter = m_registry.find(id);
 | |
|         if (iter != m_registry.end())
 | |
|         {
 | |
|             rval = iter->second;
 | |
|         }
 | |
|         return rval;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Add an entry to the registry.
 | |
|      *
 | |
|      * @param entry The entry to add
 | |
|      * @return True if successful, false if id was already in
 | |
|      */
 | |
|     bool add(entry_type entry)
 | |
|     {
 | |
|         id_type id = RegistryTraits<EntryType>::get_id(entry);
 | |
|         typename ContainerType::value_type new_value(id, entry);
 | |
|         return m_registry.insert(new_value).second;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Remove an entry from the registry.
 | |
|      *
 | |
|      * @param id Entry id
 | |
|      * @return True if an entry was removed, false if not found
 | |
|      */
 | |
|     bool remove(id_type id)
 | |
|     {
 | |
|         entry_type rval = lookup(id);
 | |
|         if (rval)
 | |
|         {
 | |
|             m_registry.erase(id);
 | |
|         }
 | |
|         return rval;
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     typedef typename std::tr1::unordered_map<id_type, entry_type> ContainerType;
 | |
|     ContainerType m_registry;
 | |
| };
 | |
| 
 | |
| // binary compare of pointed-to objects
 | |
| template<typename Ptr>
 | |
| bool equal_pointees(const Ptr& lhs, const Ptr& rhs)
 | |
| {
 | |
|    return *lhs == *rhs;
 | |
| }
 | |
| 
 | |
| // Unary predicate for equality of pointed-to objects
 | |
| template<typename T>
 | |
| class EqualPointees : public std::unary_function<T, bool>
 | |
| {
 | |
| public:
 | |
|    EqualPointees(const T& lhs) : m_ppLhs(&lhs) {}
 | |
|    bool operator()(const T& pRhs)
 | |
|    {
 | |
|        return **m_ppLhs == *pRhs;
 | |
|    }
 | |
| private:
 | |
|    const T* m_ppLhs;
 | |
| };
 | |
| 
 | |
| template<typename T>
 | |
| EqualPointees<T> equal_pointees(const T& t)
 | |
| {
 | |
|    return EqualPointees<T>(t);
 | |
| }
 | |
| 
 | |
| }
 |