Группы
Главная, Основные концепции, АОП, Адвайсы
Мощный механизм для работы с группами адвайсов объединенных по определенным критериям. В группу можно поместить один или несколько адвайсов с помощью конструкции fas::group:
typedef fas::group<_group_, _foo_> group_advice1;
typedef fas::group<_group_, fas::type_list_n<_bar_, _baz_>::type > group_advice2;
Адвайсы определяются в группу одним или несколькими конструкциями fas::group с общим тегом. В отличие от прочих типов адвайсов, в аспекте может быть несколько таких конструкций с общим тегом. В аспекте может быть определено неограниченное количество групп.
Если все адвайсы группы поддерживают семантику operator()(T&[, arg1, [arg2, [arg3, [arg4, [arg5]]]])
, то вызов может быть осуществлен прозрачно, например:
t.get_aspect().template get<_group_>()(t, arg1);
Т.е. в этом месте нет возможности узнать, что скрыто тегом _group_
, группа адвайсов, или отдельный адвайс. Можно указать явно, что предполагается обращение к группе:
t.get_aspect().template getg<_group_>()(t, arg1);
Однако и в этом случае, под тегом _group_
может быть не группа, а конкретный адвайс. Разница в том, что в первом случае, если в аспекте отсутствует сущность с тегом _group_
, компилятор выдаст ошибку fas::advice_not_found
. Во втором случае вызов будет проигнорирован. Избежать ошибку компиляции можно путем внедрения в аспект заглушки. Оба варианта, get
с заглушкой и getg
, взаимозаменяемы. В обоих случаях, как правило, предполагается, что разработчик аспектного класса, предлагает пользователю внедрить некоторый свой функционал. В первом случае это может быть некоторый функционал расширяющий существующий, а во втором - реакция на некоторые события.
Адвайсы группы обрабатываются строго в том порядке, в котором они определены в аспекте, т.е. первым будет вызван последний внедренный в аспект адвайс. Если необходимо извинить порядок на обратный - сымитировать реакцию на событие, то используется специальный метод gete
:
t.get_aspect().template gete<_group_>()(t, arg1);
Шаблонными параметрами getg
и gete
, помимо тегов групп и адвайсов можно передавать списки тегов, в который включены как теги групп, так и обычных адвайсов. В примере ниже показано как это можно сделать:
#include <fas/aop.hpp>
#include <fas/algorithm.hpp>
#include <iostream>
struct _show1_;
struct _show2_;
struct _show3_;
struct _call_group_;
template<int I>
struct ad_show
{
template<typename T>
void operator()(T&)
{
std::cout << "show " << I << std::endl;
}
};
struct aspect_foo: fas::aspect< fas::type_list_n<
fas::advice<_show1_, ad_show<1> >,
fas::advice<_show2_, ad_show<2> >,
fas::advice<_show3_, ad_show<3> >,
fas::group<_call_group_, fas::type_list_n<_show1_, _show2_>::type >,
fas::group<_call_group_, _show3_>
>::type>{};
template< typename A = fas::aspect<> >
class foo
: public fas::aspect_class< typename fas::merge_aspect<A, aspect_foo>::type >
{
typedef fas::aspect_class< typename fas::merge_aspect<A, aspect_foo>::type > super;
public:
void method1()
{
this->get_aspect().template get<_call_group_>()(*this);
}
void method2()
{
this->get_aspect().template getg<_call_group_>()(*this);
}
void method3()
{
this->get_aspect().template gete<_call_group_>()(*this);
}
void method4()
{
typedef typename super::aspect::template select_group<_call_group_>::type group_list_origin;
typedef typename fas::random_shuffle< fas::int_<42>, group_list_origin >::type shuffle_list;
typedef typename fas::type_list_n< _show3_, shuffle_list>::type group_list;
this->get_aspect().template getg<group_list>()(*this);
}
};
struct aspect_bar: fas::aspect< fas::type_list_n<
fas::advice<_show3_, ad_show<33> >,
fas::alias<_call_group_, _show3_ >
>::type>{};
int main()
{
foo<> f;
std::cout << "foo<>::method1()" << std::endl;
f.method1();
std::cout << "foo<>::method2()" << std::endl;
f.method2();
std::cout << "foo<>::method3()" << std::endl;
f.method3();
std::cout << "foo<>::method4()" << std::endl;
f.method4();
std::cout << "-------" << std::endl;
foo<aspect_bar> b;
std::cout << "foo<aspect_bar>::method1()" << std::endl;
b.method1();
std::cout << "foo<aspect_bar>::method2()" << std::endl;
b.method2();
std::cout << "foo<aspect_bar>::method3()" << std::endl;
b.method3();
std::cout << "foo<aspect_bar>::method4()" << std::endl;
b.method4();
std::cout << "-------" << std::endl;
foo< fas::remover<_call_group_> > r;
std::cout << "foo<fas::remover<_call_group_>>::method1()" << std::endl;
// r.method1();
std::cout<<"error: incomplete type ‘fas::advice_has_been_removed<_call_group_>’ used in nested name specifier" << std::endl;
std::cout << "foo<fas::remover<_call_group_>>::method2()" << std::endl;
r.method2();
std::cout << "foo<fas::remover<_call_group_>>::method3()" << std::endl;
r.method3();
std::cout << "foo<fas::remover<_call_group_>>::method4()" << std::endl;
r.method4();
}
В этом примере был сформирован аспект aspect_foo
для класса foo
который включает три адвайса, которые выводят некоторое число и группа, в которую включены все три адвайса. Группа определена двумя конструкциями fas::group
, но ничто не мешает определить одной такой конструкцией, например так:
fas::group<_call_group_, fas::type_list_n<_show1_, _show2_, _show3_>::type >,
или тремя:
fas::group<_call_group_, _show1_>,
fas::group<_call_group_, _show2_>,
fas::group<_call_group_, _show3_>
В классе foo определены четыре метода:
- Метод method1 обращается к
_call_group_
как к обычному адвайсу. Если адвайс с тегом_call_group_
в аспекте отсутствует, то будет ошибка компиляции - Метод method2 обращается к
_call_group_
как к группе. Если адвайс с тегом_call_group_
в аспекте отсутствует, то ошибки не будет, вызов будет проигнорирован - Метод method3 эмулирует событие
_call_group_
- адвайсы группы будут вызваны в обратном порядке - Метод method4 формирует собственную группу
Формирование списка вызова в method4 происходит следующим образом:
- извлекаются из аспекта все теги группы
_call_group_
- псевдо-случайно перемешиваются с зерном 42
- дополнительно добавляется тег
_show3_
, после чего осуществляется вызов.
Следует отметить, что при повторном добавлении тега в группу в аспекте, происходит только изменение порядка вызовов адвайсов группы, т.е. повторного вызова не происходит. Дважды вызвать один и тот же адвайс возможно только при ручном формировании списка, как это продемонстрировано в этом методе.
Результатом вызова всех четырех методов с аспектом foo_aspect
будет:
foo<>::method1()
show 1
show 2
show 3
foo<>::method2()
show 1
show 2
show 3
foo<>::method3()
show 3
show 2
show 1
foo<>::method4()
show 3
show 1
show 3
show 2
В аспекте aspect_bar
под тегом _show3_
внедряется адвайс выводящий на консоль число 33, а под _call_group_
алиас на этот адвайс. После внедрения aspect_bar
в foo
изначальная группа _call_group_
будет заменена этим алиасом. Результатом внедрения будет следующий вывод на консоль:
foo<aspect_bar>::method1()
show 33
foo<aspect_bar>::method2()
show 33
foo<aspect_bar>::method3()
show 33
foo<aspect_bar>::method4()
show 33
show 33
Ну и наконец, в примере продемонстрировано удаление группы. После удаления, вызов method1
, приведет к ошибке компиляции:
foo<fas::remover<_call_group_>>::method1()
error: incomplete type ‘fas::advice_has_been_removed<_call_group_>’ used in nested name specifier
foo<fas::remover<_call_group_>>::method2()
foo<fas::remover<_call_group_>>::method3()
foo<fas::remover<_call_group_>>::method4()
show 3
Несмотря на то, что мы используем термин "вызов", в случае с включенной оптимизацией, как правило, будет произведена inline
подстановка кода всех адвайсов группы, также как и при обращению к обычному адвайсу.