Теперь в функцию распределителя, помимо данных, будет передаваться адрес. Источник должен найти аргумент, которому соответствует полученный адрес, и выполнить для него вызов. Для поиска необходимо сравнивать адреса, но ведь мы не знаем их типы: теперь это параметр шаблона, и тип используемого адреса станет известен только после инстанциирования. По этой причине мы не можем производить сравнение адресов напрямую, для этого необходимо использовать предикаты (см. п. 4.3.3).
Какой выбрать контейнер? На эту роль лучше других подойдет std::map. Во-первых, не нужно вводить новую структуру для хранения адреса и аргумента, контейнер реализует ее естественным образом в виде пары «ключ-значение». И, во-вторых, std::mapосуществляет быстрый поиск по ключу, в качестве которого выступает адрес. Структурная схема изображена на Рис. 25.
Рис. 25. Структурная схема адресного распределения
5.7.2. Адресный распределитель
Реализация адресного распределителя приведена в Листинг 84.
Листинг 84. Распределитель для адресного набора получателей
template class AddressDistributor; // (1)
template // (2)
class AddressDistributor
// (3)
{
public:
template // (4)
void addReceiver(Address address, CallObject object)
{
callObjects.insert({ address,object } );
}
void deleteReceiver(Address address) // (5)
{
callObjects.erase(address);
}
Return operator()(Address address, ArgumentList… arguments) // (6)
{
auto iterator = callObjects.find(address); // (7)
if (iterator != callObjects.end())
{
return iterator->second(arguments…); // (8)
}
else
{
throw std::invalid_argument("Invalid receiver address"); // (9)
}
}
private:
std::map< Address, std::function, AddressCompare > callObjects; // (10)
};
В строке 1 объявлена общая специализация шаблона, параметрами выступают адрес получателя Address, предикат для сравнения AddressCompareи сигнатура распределяющей функции Function. Реализация здесь отсутствует, поскольку для каждой сигнатуры требуется отдельная специализация – аналогично настройке сигнатуры для универсального аргумента (п. 4.5.2).
В строке 2 объявлена частичная специализация, в которой дополнительно представлены параметр для возвращаемого значения Returnи пакет параметров ArgumentListдля аргументов функции. В строке 3 объявлен класс, который специализируется сигнатурой из указанных параметров.
В строке 4 объявлен шаблон метода для добавления получателя, который принимает адрес address, вызываемый объект objectи добавляет их в контейнер. В строке 5 объявлен метод для удаления получателя. Оба метода работают с контейнером, который объявлен в строке 10. Контейнер объявлен как std::map, ключом является адрес, а значением – объект std::functionс заданной сигнатурой.
В строке 6 объявлен перегруженный оператор, который осуществляет распределение вызовов, т. е. является распределяющей функцией. Он пробегает по всем элементам контейнера и осуществляет вызов в соответствии с списком аргументов, типы которых задаются в пакете параметров шаблона класса. Поскольку мы используем адресное распределение, т. е. предполагается, что вызов попадает только одному получателю, то мы операторе можем вернуть результат вызова.
В строке 7 происходит поиск получателя по адресу. Если получатель найден, то происходит вызов объекта (строка 8). Если получатель не найден, то генерируется исключение (строка 9), иначе какой результат нам возвратить?
5.7.3. Использование адресного распределения
Пример использования адресного распределения приведен в Листинг 85.
Листинг 85. Использование адресного распределения
struct FO
{
int operator() (int eventID)
{
return 10;
}
};
int ExternalHandler(int eventID)
{
return 0;
}
struct ReceiverAddress // (1)
{
ReceiverAddress(int idGroup = 0, int idNumber = 0)
{
group = idGroup; number = idNumber;
}
int group;
int number;
};
template<>
struct std::less // (2)
{
bool operator() (const ReceiverAddress& addr1, const ReceiverAddress& addr2) const
{
if (addr1.group < addr2.group)
{
return true;
}
else
{
if (addr1.group == addr2.group)
Читать дальше