2017-06-01 10:24:20 +03:00

272 lines
6.5 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 <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;
};
}