blob: 9ba92634f22ef378cdeddceadfab2c570e7403bd [file] [log] [blame]
#pragma once
#include <stdint.h>
#include <algorithm>
#include <memory>
#include <vector>
namespace libhei
{
/** @brief A generic flyweight factory for objects of type T. */
template <class T>
class Flyweight
{
private: // This class cannot be instantiated. Use getSingleton() instead.
/** @brief Default constructor. */
Flyweight() = default;
/** @brief Destructor. */
~Flyweight()
{
clear();
}
/** @brief Default copy constructor. */
Flyweight(const Flyweight&) = delete;
/** @brief Default assignment operator. */
Flyweight& operator=(const Flyweight&) = delete;
public:
/** @brief Provides access to a singleton instance of this object. */
static Flyweight& getSingleton()
{
static Flyweight theFlyweight;
return theFlyweight;
}
/**
* @brief Does an emplace add of a new entry to the factory, if the entry
* does not already exist, and returns a pointer to the entry in the
* factory.
* @param An variable argument list that would be passed to a contructor of
* class T.
* @return A pointer to this entry in the factory.
*/
template <class... Args>
std::shared_ptr<T> get(Args&&... i_args)
{
// Create a new instance with the given arguments.
std::shared_ptr<T> newEntry = std::make_shared<T>(i_args...);
// The index is sorted by the value of the T objects. Check to see if
// newEntry already exists in the factory.
auto itr = std::lower_bound(
iv_index.begin(), iv_index.end(), newEntry,
[](const std::shared_ptr<T> a, const std::shared_ptr<T> b) {
return *a < *b;
});
// std::lower_bound() will return the first element that does not
// compare less than newEntry. So if an element is found, we must make
// sure it does not have the same value as newEntry.
if (iv_index.end() == itr || !(*newEntry == *(*itr)))
{
// Store the new enty in the sorted index.
itr = iv_index.insert(itr, newEntry);
}
// It is important to note that if newEntry was not inserted into the
// index above because a duplicate already existed, it will be deleted
// as soon as it goes out of scope.
// Return a pointer to the this entry in the factory.
return *itr;
}
/**
* @brief Deletes all entries in the factory.
*
* This is called in the destructor. So it cannot throw an exception.
*/
void clear()
{
iv_index.clear();
}
/**
* @brief Shrinks the index capacity to eliminate unused memory space.
*
* The index is a vector where the capacity will grow as items are added to
* it. Each time more capacity is needed, the vector will double the current
* capacity to make room for new entries. The general use of this class is
* expected that all needed entries will be added during the isolator
* initialization. Afterwards, the extra capacity is not needed. So this
* function will shrink the capacity to the size of the vector.
*/
void compact()
{
iv_index.shrink_to_fit();
}
/** @return The number of entries in this flyweight factory. */
size_t size()
{
return iv_index.size();
}
private:
/**
* Each new T is allocated on the memory heap and a pointer to each of those
* objects is stored in this vector. The vector will not contain duplicates
* and is kept sorted by the value of the objects. Inserting a new element
* is O(n). However, since the vector is sorted, searching for existing
* entries is O(log n). Considering the expected use of this class,
* insertion should only exist during initialization of the isolator, where
* searching will be done at the time of isolation when performance is more
* important.
*
* An alternative to this approach is to use std::set, where insertion and
* searching are both O(log n). However, std::set is typically designed with
* a red-black tree which carries a lot of extra memory overhead to maintain
* the structure. Also, the Hostboot user application does not support
* std::set at this time.
*/
std::vector<std::shared_ptr<T>> iv_index;
};
} // end namespace libhei