patterncppMinor
C++ Template for one to many Registration (pre Gang of Four)
Viewed 0 times
templateregistrationfourpreoneformanygang
Problem
I use this template to lookup clients registered for data. The data is associated by name(key) and clients are shared pointers (value) to a class which will consume the data.
```
////////////////////////////////////////////////////////////////////
//
// Registrar Template to help manage Key to Value Registrations
//
// T1 - Key Object
// T2 - Value Object
//
// For Example: Register clients (T2) for Data (T1)
////////////////////////////////////////////////////////////////////
#ifndef _RegistrarT_hpp_
#define _RegistrarT_hpp_
#include
#include
#include
template >
class RegistrarT
{
public:
typedef std::multimap RegistrationMultiMap;
typedef std::vector RegistrationVector;
typedef std::set KeySet;
public:
RegistrarT(){}
~RegistrarT(){}
//
// Register a value; Do not allow duplicate registrations
void Register(T1 const & key, T2 const & value)
{
Unregister(key, value); // Remove if it exists in the multimap
registrations_.insert(std::make_pair(key,value));
}
// Lookup all Registered for Key then find and remove value
void Unregister(T1 const & key, T2 const & value)
{
bool found=false;
typename RegistrationMultiMap::iterator itr =
registrations_.lower_bound(key);
while (!found && itr != registrations_.upper_bound(key))
{
if (itr->second == value)
found = true;
else
++itr;
}
if (found)
registrations_.erase(itr);
}
// Remove all values registered for key
void UnregisterByKey(T1 const & key)
{
registrations_.erase(registrations_.lower_bound(key),
registrations_.upper_bound(key));
}
// Find all values and remove registrations for all keys
void UnregisterAll(T2 const & value)
{
typename RegistrationMultiMap::iterator itr =
registrations_.begin();
while (itr != registrations_.end())
{
if (itr->second == value)
registrations_.erase(itr++);
```
////////////////////////////////////////////////////////////////////
//
// Registrar Template to help manage Key to Value Registrations
//
// T1 - Key Object
// T2 - Value Object
//
// For Example: Register clients (T2) for Data (T1)
////////////////////////////////////////////////////////////////////
#ifndef _RegistrarT_hpp_
#define _RegistrarT_hpp_
#include
#include
#include
template >
class RegistrarT
{
public:
typedef std::multimap RegistrationMultiMap;
typedef std::vector RegistrationVector;
typedef std::set KeySet;
public:
RegistrarT(){}
~RegistrarT(){}
//
// Register a value; Do not allow duplicate registrations
void Register(T1 const & key, T2 const & value)
{
Unregister(key, value); // Remove if it exists in the multimap
registrations_.insert(std::make_pair(key,value));
}
// Lookup all Registered for Key then find and remove value
void Unregister(T1 const & key, T2 const & value)
{
bool found=false;
typename RegistrationMultiMap::iterator itr =
registrations_.lower_bound(key);
while (!found && itr != registrations_.upper_bound(key))
{
if (itr->second == value)
found = true;
else
++itr;
}
if (found)
registrations_.erase(itr);
}
// Remove all values registered for key
void UnregisterByKey(T1 const & key)
{
registrations_.erase(registrations_.lower_bound(key),
registrations_.upper_bound(key));
}
// Find all values and remove registrations for all keys
void UnregisterAll(T2 const & value)
{
typename RegistrationMultiMap::iterator itr =
registrations_.begin();
while (itr != registrations_.end())
{
if (itr->second == value)
registrations_.erase(itr++);
Solution
My immediate tendency would be to consider changing data structures. Right now you have an
This simplifies quite a bit of the code considerably. For example,
Likewise,
...and
Along with simplifying the code, this at least has some chance of improving speed. All your current operations on the values associated with a given key are linear in nature. Since this uses a set, most of those become logarithmic instead.
About the only operations that look like they're at all likely to become more complex/difficult are things like the no-parameter version of
If you decide you need to keep the same data structure, I think you could make better use of the library in a number of places. For example
One other change to consider would be to get rid of
std::multimap. Given that you want set-like behavior for the values associated with a particular key, my first inclination would be to change the basic data structure to something like: std::map >.This simplifies quite a bit of the code considerably. For example,
Register turns into something like: registrations_[key].insert(value);Likewise,
Unregister turns into something like this:auto pos = registrations_.find(key);
if (pos != registrations.end)
pos->second.erase(value);...and
GetRegistrationKeys turns into something like this:auto pos = registrations_.find(key);
if (pos != registrations_.end()) {
auto const &s = pos->second;
std::copy(s.begin(), s.end(), ks);
}Along with simplifying the code, this at least has some chance of improving speed. All your current operations on the values associated with a given key are linear in nature. Since this uses a set, most of those become logarithmic instead.
About the only operations that look like they're at all likely to become more complex/difficult are things like the no-parameter version of
RegistrationsCount. If you really need this a great deal, it can be done with constant complexity by keeping track of the overall size as you insert and delete registrations, but I can't guess whether it's used often enough to justify the bother (even though the bother is pretty slight).If you decide you need to keep the same data structure, I think you could make better use of the library in a number of places. For example
Unregister can turn into something like this:// find range with specified key:
auto range = registrations_.equal_range(key);
// find specified value within that range:
auto pos = std::find_if(range.first, range.second,
[&](auto v) { return v.second == value; });
// If we found the value, remove it:
if (pos != range.second)
registrations_.erase(pos);One other change to consider would be to get rid of
RegistrationVector and KeySet. Instead of using them, I'd turn the functions that currently use them into template member functions taking a templated iterator type to which they'd write their result.Code Snippets
auto pos = registrations_.find(key);
if (pos != registrations.end)
pos->second.erase(value);auto pos = registrations_.find(key);
if (pos != registrations_.end()) {
auto const &s = pos->second;
std::copy(s.begin(), s.end(), ks);
}// find range with specified key:
auto range = registrations_.equal_range(key);
// find specified value within that range:
auto pos = std::find_if(range.first, range.second,
[&](auto v) { return v.second == value; });
// If we found the value, remove it:
if (pos != range.second)
registrations_.erase(pos);Context
StackExchange Code Review Q#116075, answer score: 2
Revisions (0)
No revisions yet.