Russian Qt Forum

Программирование => С/C++ => Тема начата: Igors от Июнь 03, 2016, 08:19



Название: Контейнер из подручных средств
Отправлено: Igors от Июнь 03, 2016, 08:19
Добрый день

Пример: есть такой темплейт
Код
C++ (Qt)
template <class T>
int GetMaxNegative( const T & vec )
{
int maxV = 0;
for (size_t i = 0; i < vec.size(); ++i) {
int val = vec[i];
if (val >= 0) continue;
if (!maxV || val > maxV)
maxV = val;
}
 
return maxV;
}
Хорошо, теперь я могу его использовать для любых контейнеров с прямым доступом (std::vector, QVector,  QList). Но вот мне понадобилось напр так
Код
C++ (Qt)
int GetMaxNegative( const QWidgetList & vec )
{
int maxV = 0;
for (size_t i = 0; i < vec.size(); ++i) {
int val = vec[i]->x();
if (val >= 0) continue;
if (!maxV || val > maxV)
maxV = val;
}
 
return maxV;
}
Из-за разницы в обращении к эл-ту приходится переписывать. Ну или чуть более сложный случай - напр "x" в координатах заданного виджета (а не парента). Основания для обобщения вроде имеются, но как это лучше сделать?

Спасибо


Название: Re: Контейнер из подручных средств
Отправлено: kuzulis от Июнь 03, 2016, 08:28
http://ru.cppreference.com/w/cpp/algorithm/max_element не?


Название: Re: Контейнер из подручных средств
Отправлено: Igors от Июнь 03, 2016, 09:18
http://ru.cppreference.com/w/cpp/algorithm/max_element не?
Ну вот, как говорит мой свояк, "абы фатануть" :) Только ради вычисления максимума нет смысла заморачиваться, это чисто для примера.


Название: Re: Контейнер из подручных средств
Отправлено: Old от Июнь 03, 2016, 10:59
Ну вот, как говорит мой свояк, "абы фатануть" :) Только ради вычисления максимума нет смысла заморачиваться, это чисто для примера.
Ну пример kuzulis еще намекает на функторы. Вы можете вынести код возвращающий значение (эдакий геттер) для сравнения за шаблон. В случае с контейнером чисел, геттер будет просто возвращать число из коллекции, а в случае виджета или QPoint - значение нужной величины (в вашем случае значение x).


Название: Re: Контейнер из подручных средств
Отправлено: Igors от Июнь 03, 2016, 11:07
Ну пример kuzulis еще намекает на функторы. Вы можете вынести код возвращающий значение (эдакий геттер) для сравнения за шаблон. В случае с контейнером чисел, геттер будет просто возвращать число из коллекции, а в случае виджета или QPoint - значение нужной величины (в вашем случае значение x).
Не хотелось бы "эдакий", т.к. его придется обеспечивать и для всех банальных контейнеров.

Да, а где же наш энтузиаст шаблонной магии?  :)


Название: Re: Контейнер из подручных средств
Отправлено: GreatSnake от Июнь 03, 2016, 11:18
Не хотелось бы "эдакий", т.к. его придется обеспечивать и для всех банальных контейнеров.
Вместо функтора для getter-a можно задействовать опциональную std::function.


Название: Re: Контейнер из подручных средств
Отправлено: Igors от Июнь 03, 2016, 11:30
Вместо функтора для getter-a можно задействовать опциональную std::function.
А можно пример как это будет выглядеть в данном случае? (std::function пока в мой арсенал не входит, все не переползем на С++ 11)

И еще могут понадобиться доп данные для чтения/записи эл-та (см пример с "х" в координатах заданного виджета)


Название: Re: Контейнер из подручных средств
Отправлено: Old от Июнь 03, 2016, 11:34
Не хотелось бы "эдакий", т.к. его придется обеспечивать и для всех банальных контейнеров.
Лямбды это упростят:

Код
C++ (Qt)
#include <vector>
#include <iostream>
 
using namespace std;
 
template<typename Ret, typename It, typename Func>
Ret getMaxNegative( It beg, It end, Func get )
{
Ret maxV = 0;
for( It i = beg; i != end; ++i )
{
Ret val = get( *i );
if( val >= 0 )
continue;
 
if( !maxV || val > maxV )
maxV = val;
}
 
return maxV;
}
 
template<typename Ret, typename It>
Ret getMaxNegative( It beg, It end )
{
return getMaxNegative<Ret, It>( beg, end, []( Ret v ) { return v; } );
}
 
struct Point
{
int x;
int y;
};
 
int main( int, char ** )
{
cout << "Hello World!" << endl;
 
std::vector<int> v{ 3, -1, -14, 1, 5, 9 };
cout << "Result = " << getMaxNegative<int>( v.begin(), v.end() ) << endl;
 
std::vector<Point> p{ { 10, 10 }, { 20, 20 }, { -30, 30 }, { -10, 40 } };
cout << "Result = " << getMaxNegative<double>( p.begin(), p.end(), []( const Point &v ){ return v.x; } ) << endl;
 
return 0;
}
 


Название: Re: Контейнер из подручных средств
Отправлено: Igors от Июнь 03, 2016, 13:20
Лямбды это упростят:
Так-то оно так, но вот есть десяток ф-ций наподобие GetMaxNegative - и каждую придется "одевать", непрахтишно. Да и передача хвунктора как-то не очень. Нельзя ли сосредоточить весь новый код в том что "подается на вход GetMaxNegative" ?


Название: Re: Контейнер из подручных средств
Отправлено: Old от Июнь 03, 2016, 13:22
Нельзя ли сосредоточить весь новый код в том что "подается на вход GetMaxNegative" ?
На вход подается коллекция QPoint, по какой оси должен искаться максимум? А если коллекция QColor?


Название: Re: Контейнер из подручных средств
Отправлено: Racheengel от Июнь 03, 2016, 14:40
Стремиться, имхо, надо к следующему:

Код
C++ (Qt)
template <class T>
int GetVectorValue(const T & vec, int index) const
{
   return vec[index];
}
 
template <class T>
int GetVectorValueByX(const T & vec, int index) const
{
   return vec[index]->x();
}
 
template <class T>
int GetVectorValueByY(const T & vec, int index) const
{
   return vec[index]->y();
}
 
// ... blabla
 
template <class T, class F = GetVectorValue>
int GetMaxNegative( const T & vec, F* fvec  )
{
int maxV = 0;
for (size_t i = 0; i < vec.size(); ++i) {
int val = fvec(vec, i);
if (val >= 0) continue;
if (!maxV || val > maxV)
maxV = val;
}
 
return maxV;
}
 

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


Название: Re: Контейнер из подручных средств
Отправлено: kambala от Июнь 03, 2016, 15:19
Лямбды это упростят:
Так-то оно так, но вот есть десяток ф-ций наподобие GetMaxNegative - и каждую придется "одевать", непрахтишно. Да и передача хвунктора как-то не очень. Нельзя ли сосредоточить весь новый код в том что "подается на вход GetMaxNegative" ?
такое вообще бывает в нединамических языках? (ну кроме switch-enum подхода)


Название: Re: Контейнер из подручных средств
Отправлено: m_ax от Июнь 03, 2016, 15:46
Можно специализировать getter_helper под различные пользоваткльские типы и дёргать его из функции:

Код
C++ (Qt)
template <class>
struct getter_helper
{
   template <class R, class U>
   static R get(const U & x)
   {
       return x;
   }
};
 
template <>
struct getter_helper<QPoint>
{
   template <class R, class U>
   static R get(const U & p)
   {
       return p.x();
   }
};
 
template <class T>
int GetMaxNegative( const T & vec)
{
   int maxV = 0;
   for (size_t i = 0; i < vec.size(); ++i) {
       int val = getter_helper<typename T::value_type>::get(vec[i]);
       if (val >= 0) continue;
       if (!maxV || val > maxV)
           maxV = val;
   }
 
   return maxV;
}
 


Название: Re: Контейнер из подручных средств
Отправлено: Igors от Июнь 03, 2016, 15:57
такое вообще бывает в нединамических языках? (ну кроме switch-enum подхода)
Не понял, что "бывает"? В смысле десятки ф-ций работающих с одним контейнером? Да, конечно


Название: Re: Контейнер из подручных средств
Отправлено: Igors от Июнь 03, 2016, 16:11
Можно специализировать getter_helper под различные пользоваткльские типы и дёргать его из функции:
Так мне кажется логичнее, но зачем нам засорять код рабочих ф-ций каким-то getter_helper'ом ? Почему не так
Код
C++ (Qt)
struct CWxContainer {
CWxContainer( const QWdgetList & lst, QWidget * coord ) :
  mLst(lst),
  mCoord(coord)
 {
 }
 
 size_t size( void ) const { return mLst.size(); }
 int operator[] ( size_t index ) const { return mCoord->mapFromGlobal(mLst[index]->mapToGlobal(QPoint(0, 0))).x(); }
 
// data
const QWdgetList & mLst;
QWidget * mCoord;
};
Вот правда не знаю как сделать запись, чтобы [] возвращал ссылку на int (ее ведь "в оригинале" может не быть)


Название: Re: Контейнер из подручных средств
Отправлено: Old от Июнь 03, 2016, 16:25
Можно специализировать getter_helper под различные пользоваткльские типы и дёргать его из функции:
Знаете что не нравиться: для каждого типа может быть только один хелпер. Например, для QPoint нельзя в одном случае считать по x, а в другом по y.


Название: Re: Контейнер из подручных средств
Отправлено: kambala от Июнь 03, 2016, 16:42
такое вообще бывает в нединамических языках? (ну кроме switch-enum подхода)
Не понял, что "бывает"? В смысле десятки ф-ций работающих с одним контейнером? Да, конечно
нет, чтобы
Цитировать
сосредоточить весь новый код в том что "подается на вход GetMaxNegative"
в objc с его «магией рантайма» я еще могу представить как это сделать, но в ++... вариант с лямбдами/функторами кажется вполне естественным.


Название: Re: Контейнер из подручных средств
Отправлено: Igors от Июнь 03, 2016, 17:31
... вариант с лямбдами/функторами кажется вполне естественным.
Пока одна содержательная/рабочая ф-ция - все хорошо.
Код
C++ (Qt)
getMaxNegative<double>( p.begin(), p.end(), []( const Point &v ){ return v.x; } )
Но беда в том что так придется писать для КАЖДОЙ рабочей ф-ции, напр
Код
C++ (Qt)
getMinPositive<double>( p.begin(), p.end(), []( const Point &v ){ return v.x; } )
А как только объявится еще "виртуальный контейнер" (хотя бы "y" вместо "x") - опять все для КАЖДОЙ

в objc с его «магией рантайма» я еще могу представить как это сделать, но в ++...
Шо за пессимизм?  :)

Стремиться, имхо, надо к следующему:

Код
C++ (Qt)
template <class T>
int GetVectorValue(const T & vec, int index) const
{
   return vec[index];
}
 
...
 

Таким образом через параметры можно передать и вектор, и функтор для доступа к нему (если базовый не подходит).
Не вкурил, можно подробнее? Как (или куда) пристраивать написанное?


Название: Re: Контейнер из подручных средств
Отправлено: kambala от Июнь 03, 2016, 18:38
ну так идентичные лямбды делаются обычными функциями.

а если передавать в качестве параметра GetMaxNegative() еще и указатель на метод элемента контейнера?


Название: Re: Контейнер из подручных средств
Отправлено: m_ax от Июнь 03, 2016, 19:24
Цитировать
Так мне кажется логичнее, но зачем нам засорять код рабочих ф-ций каким-то getter_helper'ом ? Почему не так
Ну так Вы же сами в первом посте потавили так вопрос. В любом случае где-то придётся руками прописывать поведение для конкретного типа. Здесь мы функцию зато не трогаем больше.

Цитировать
Знаете что не нравиться: для каждого типа может быть только один хелпер. Например, для QPoint нельзя в одном случае считать по x, а в другом по y.
Согласен) Можно ещё один шаблон добавить, который уже ракзличные стратегии определяет для данного типа..

Хотя я бы с лямбдами сделал и не запаривался)


Название: Re: Контейнер из подручных средств
Отправлено: Igors от Июнь 04, 2016, 11:40
ну так идентичные лямбды делаются обычными функциями.

а если передавать в качестве параметра GetMaxNegative() еще и указатель на метод элемента контейнера?
Конечно можно, но это отягощает рабочие ф-ции. Два темплейт-аргумента вместо одного, плюс "default" реализация, плюс лямбды и/или функторы повсеместно. Когда ф-ций много это становится очень ощутимым.

Ну так Вы же сами в первом посте потавили так вопрос. В любом случае где-то придётся руками прописывать поведение для конкретного типа.
В исходном примере возвращаемое значение int (конкретный тип), т.е. меня не интересует что-то другое, нет планов адаптировать под double или еще что.

Вопрос был в том что исходные (хранимые) значения сами могут и не находиться в классе-контейнере, но для них существует ф-ционал:

- счетчик (число эл-тов)
- доступ к значению по индексу

Ну как бы "виртуальный контейнер" прямого доступа


Название: Re: Контейнер из подручных средств
Отправлено: Racheengel от Июнь 04, 2016, 14:47
Не вкурил, можно подробнее? Как (или куда) пристраивать написанное?

Вторым параметром при необходимости (F* fvec):

template <class T, class F = GetVectorValue>
int GetMaxNegative( const T & vec, F* fvec  )

Примерно как:

GetMaxNegative(myVector, myFVector);

myFVector должен быть, соответвенно, заточен на обработку myVector.
Каждый раз для получения index элемента вектора будет вызвано myFVector ->GetVectorValue(myVector, index);



Название: Re: Контейнер из подручных средств
Отправлено: Igors от Июнь 04, 2016, 15:17
Вторым параметром при необходимости (F* fvec):

template <class T, class F = GetVectorValue>
int GetMaxNegative( const T & vec, F* fvec  )
Понял, спасибо. Это близко к предложенным вариантам, но кажется мне поудобнее. Все же "вживление" специального геттера... Ведь следом за ним неизбежно последует сеттер. Зачем мы таким образом достигаем ф-ционала оператора [], не лучше ли перекрывать его если требуется ?


Название: Re: Контейнер из подручных средств
Отправлено: Racheengel от Июнь 04, 2016, 18:53
Ну а как без геттера? Ведь вектор уже имеет собственный оператор [], речь о том, чтобы специализировать "результат" оператора [].
Например, vec.x(), или vec.y() и т.д. - тут надо ж как-то явно указывать, что именно вам нужно.


Название: Re: Контейнер из подручных средств
Отправлено: m_ax от Июнь 04, 2016, 21:33
Цитировать
Вопрос был в том что исходные (хранимые) значения сами могут и не находиться в классе-контейнере, но для них существует ф-ционал:

- счетчик (число эл-тов)
- доступ к значению по индексу

Ну как бы "виртуальный контейнер" прямого доступа
Т.е. предлагаете свой враппер писать под контейнер? Не совсем понимаю, чем это лучше специализации для геттера? В подходе с геттером гибкости больше: появилась потребность обращаться, например к point.y() - написали отдельную специализацию и т.д.. И от типа контейнера можно абстрогироваться (не обязательно по индексу обращаться к элементу)..