про рефакторинг
От: glmn  
Дата: 05.09.13 16:32
Оценка:
Добрый день,

Есть класс представляющий коллекцию векторов разных типов

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);   

}


Таким образом в каждой из функций приходиться выписывать по одному вызову на каждый тип векторов, что в итоге выливается в большую некрасивую кучу кода.

Подскажите пожалуйста подход помимо вложенных макросов, позволяющий сократить объем кода?

Спасибо.
Re: про рефакторинг
От: jazzer Россия Skype: enerjazzer
Дата: 05.09.13 17:34
Оценка:
Здравствуйте, 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 в каком-то виде.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re: про рефакторинг
От: Кодт Россия  
Дата: 05.09.13 18:58
Оценка:
Здравствуйте, 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);
}


Если очень-очень хочется, то и вариадики припахать можно. Но массивы, мне кажется, экономнее.
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.