Files
MaxScale/include/maxscale/utils.hh
Markus Mäkelä 98fb2cf5ed MXS-1503: Add EqualPointees<T> template
The templated provides an unary predicate for (shared) pointers that
allows containers containing pointers to objects to be searched, not by
pointer comparisons but by comparisons of the values that they point
to. This allows std::find_if to be used with shared pointers in a similar
manner that std::find is used with non-pointer objects.
2018-04-03 13:31:40 +03:00

299 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 <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:
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);
}
}