Добрый день,
Есть класс представляющий коллекцию векторов разных типов
class ObjCollection
{
std::vector<ObjType1> objects1;
std::vector<ObjType2> objects2;
...
std::vector<ObjTypeN> objectsN
};
Есть много функций работающих с этими коллекциями, которые сводятся к последовательным вызовам некоторых шаблонных
функций над соответствующими векторами:
SomeRetType someFunc(const ObjCollection& coll_A,
const ObjCollection& coll_B,
...
const ObjCollection& coll_X)
{
prepareSomeActions<ObjType1>(coll_A.objects1,
coll_B.objects1,
...
coll_Y.objects1);
...
prepareSomeActions<ObjTypeN>(coll_A.objectsN,
coll_B.objectsN,
...
coll_Y.objectsN);
}
Таким образом в каждой из функций приходиться выписывать по одному вызову на каждый тип векторов, что в итоге выливается в большую некрасивую кучу кода.
Подскажите пожалуйста подход помимо вложенных макросов, позволяющий сократить объем кода?
Спасибо.
Здравствуйте, glmn, Вы писали:
G>Таким образом в каждой из функций приходиться выписывать по одному вызову на каждый тип векторов, что в итоге выливается в большую некрасивую кучу кода.
G>Подскажите пожалуйста подход помимо вложенных макросов, позволяющий сократить объем кода?
для борьбы с coll_N — variadic templates
для борьбы с ObjTypeN/objectsN — boost::mpl::vector< &ObjCollection::objects1, &ObjCollection::objects2, ... > и потом проход по нему при помощи mpl::for_each.
(из &ObjCollection::objects1 можно достать его тип (std::vector<ObjType1>) и применить к нему ::value_type, чтоб достать ObjType1)
Саму ObjCollection тоже можно сгенерить из списка типов boost::mpl::vector< ObjType1, ObjType2, ...> в boost::fusion::map< pair<ObjType1, std::vector<ObjType1>>,... > с коллекциями внутри, если не критично иметь имена вида objectsN — тогда все еще больше упростится.
Доставать коллекцию тогда нужно будет не через coll.objects1, а через at_key<ObjType1>(coll).
Ссылки на соответствующие доки:
http://www.boost.org/libs/fusion/doc/html/fusion/container/map.html
http://www.boost.org/libs/fusion/doc/html/fusion/container/vector.html
http://www.boost.org/libs/fusion/doc/html/fusion/algorithm/iteration/functions/for_each.html
http://www.boost.org/libs/mpl/doc/refmanual/map.html
http://www.boost.org/libs/mpl/doc/refmanual/vector.html
http://www.boost.org/libs/mpl/doc/refmanual/for-each.html
В принципе, если возьмешься юзать fusion, то mpl уже можешь и не юзать — он считай что включен в fusion в каком-то виде.
Здравствуйте, glmn, Вы писали:
Во-первых, открой для себя вывод типов
template<class T> void someAction(vector<T>& a, vector<T>& b, ..., vector<T>& x);
.....
void someFunc(ObjCollection& ca, ObjCollection& cb, ..., ObjCollection& cx)
{
someAction(ca.objects1, cb.objects1, ..., cx.objects1);
someAction(ca.objects2, cb.objects2, ..., cx.objects2);
.....
someAction(ca.objectsN, cb.objectsN, ..., cx.objectsN);
}
Во-вторых, зафиксируем набор коллекций, и будем вызывать функцию для заданного поля
template<class T, int X> void someAction(vector<T>* (&vecs)[X]);
// всё равно параметры однородные - будем передавать как массив указателей на векторы
// для массива коллекций и указанного поля - сформируем массив указателей на поля и вызовем someAction
template<class T, int X> void applySomeAction(ObjCollection* (&colls)[X], vector<T> (ObjCollection::*member))
{
vector<T>* vecs[X];
for(int i=0; i!=X; ++i) vecs[i] = &colls[i]->*member;
someAction(vecs);
}
void someFunc(ObjCollection& ca, ObjCollection& cb, ..., ObjCollection& cx)
{
ObjCollection* colls[X] = { &ca, &cb, ..., &cx };
applySomeAction(colls, &ObjCollection::objects1);
applySomeAction(colls, &ObjCollection::objects2);
...
applySomeAction(colls, &ObjCollection::objectsN);
}
Если количество коллекций X заранее фиксировано, то можно не выводить его, а сразу написать.
В-третьих, поскольку someAction могут быть разными, сделаем шаблон someFunc
// шаблоны функций удобно завернуть в нешаблонные классы, чтоб не громоздить адский ад в функциях высшего порядка
struct ActionP { template<class T, int X> void operator()(vector<T>* (&vecs)[X]) const { ..... } };
struct ActionQ { template<class T, int X> void operator()(vector<T>* (&vecs)[X]) const { ..... } };
struct ActionR { template<class T, int X> void operator()(vector<T>* (&vecs)[X]) const { ..... } };
template<class A, class T, int X> void applySomeAction(A action, ObjCollection* (&colls)[X], vector<T> (ObjCollection::*member))
{
vector<T>* vecs[X];
for(int i=0; i!=X; ++i) vecs[i] = &colls[i]->*member;
action(vecs);
}
template<class A>
void someFunc(A action, ObjCollection& ca, ObjCollection& cb, ..., ObjCollection& cx)
{
ObjCollection* colls[X] = { &ca, &cb, ..., &cx };
applySomeAction(action, colls, &ObjCollection::objects1);
applySomeAction(action, colls, &ObjCollection::objects2);
...
applySomeAction(action, colls, &ObjCollection::objectsN);
}
Если очень-очень хочется, то и вариадики припахать можно. Но массивы, мне кажется, экономнее.