Russian Qt Forum

Программирование => С/C++ => Тема начата: Igors от Апрель 04, 2015, 13:19



Название: mutable метод?
Отправлено: Igors от Апрель 04, 2015, 13:19
Добрый день

Есть ли mutable методы - аналогично mutable членам? Сегодня опять столкнулся с этим неудобством, псевдокод
Код
C++ (Qt)
int MyClass::GetNumFiles( void ) const  // ну явно const, возвращает инфу
{
 if (mData.size() == 0)    // данные были сброшены?
  UpdateData();              // пересоздать данные (никак не const)      
 
return mData.size();
}
 
Спасибо


Название: Re: mutable метод?
Отправлено: Bepec от Апрель 04, 2015, 16:51
Вроде бы нет. Но мутабл члены решат эту проблему.

PS но это явный и плохой костыль. От него надо избавляться.


Название: Re: mutable метод?
Отправлено: Igors от Апрель 04, 2015, 17:15
Вроде бы нет. Но мутабл члены решат эту проблему.
Не вижу каким образом  :)

PS но это явный и плохой костыль. От него надо избавляться.
Ну так, добросил фразу - авось "в масть", угадаю.


Название: Re: mutable метод?
Отправлено: Авварон от Апрель 04, 2015, 17:51
снимите const с this


Название: Re: mutable метод?
Отправлено: _Bers от Апрель 04, 2015, 18:13
Igors, одна функция - одной действие.

функция, которая занимается тем,
что возвращает количество файлов не должна заниматься их обновлением.

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

не нужно пудрить мозги выдавая одно за другое, и искать пути, как можно обмануть контракты.



Название: Re: mutable метод?
Отправлено: Bepec от Апрель 04, 2015, 22:24
Ммм... если все изменяемые члены будут мутаблами, конст метод должен скомпилиться, нет?

Нет, не добросил фразу, а прямо сказал что это костыль. Как и мутабл переменные - это костыль, которого в идеале быть не должно.


Название: Re: mutable метод?
Отправлено: Igors от Апрель 05, 2015, 04:57
одна функция - одной действие.

функция, которая занимается тем,
что возвращает количество файлов не должна заниматься их обновлением.
Такая логика несколько прямолинейна :)  Сделать 2 метода несложно, но тогда второй (напр UpdateFiles) быстро проникнет во все щели, его придется вызывать всем кто пользуется MyClass::GeetNumFiles и др такими геттерами (их там с десяток). И будет совсем непросто отследить был ли UpdateFiles перед вызовом каждого геттера. Замкнуть UpdateFiles в своем классе гораздо лучше, как бы "отложенное действие", что-там "lazy" - вещь типовая.

если по каким то соображениям вы решили все таки навесить на одну функцию несколько задач,
тогда будьте честны и убирайте квалификатор const с метода.
Да, формально, с точки зрения языка, GetNumFiles никакой не const. Но убрать const вызывает эффект домино, от константности остаются рожки да ножки. Теперь уже MyClass практически нигде не подать по константной ссылке - не даст первый геттер. И по смыслу неконстантность здесь не лепится, напр
Код
C++ (Qt)
void AnotherClass::DoSomething( MyClass & data )
{
...
data.UpdateFiles();
int count = data.GetNumFiles();
..
}
Выглядит как DoSomething предназначен чтобы сделать что-то с экземпляром MyClass - но в действительности он им просто пользуется

Придется наверное смириться с const_cast внутрях


Название: Re: mutable метод?
Отправлено: _Bers от Апрель 05, 2015, 06:35
И по смыслу неконстантность здесь не лепится, напр
Код
C++ (Qt)
void AnotherClass::DoSomething( MyClass & data )
{
...
data.UpdateFiles();
int count = data.GetNumFiles();
..
}
Выглядит как DoSomething предназначен чтобы сделать что-то с экземпляром MyClass - но в действительности он им просто пользуется

напротив. выглядит так, словно DoSomething хочет изменить состояние data
и по факту именно это и происходит.

есть логическое разделение: "только для чтения", и "для изменения".
ну так вот, и внешний дизайн DoSomething,
и её реализация указывают,
что она будет изменять объект.

логическое "только для чтения" - например, мы лочим мутекс, что бы прочитать данные.
для этого приходится делать мутекс mutable,
потому что он свое состояние меняет,
несмотря на то, что все что нам нужно - просто прочитать состояние агрегата.
однако после окончания операции чтения, мы мутекс разлочиваем, и он тоже принимает первоначальное состояние.

таким образом, логическое состояние не изменялось.
а бинарное состояние "до" и "после" осталось прежнем.






но в вашей ситуации - не просто чтение. вы реально обновляете компонент.
он у вас не только бинарно, но и логически тоже изменяется.
меняет состояние со "старого" на "обновленное".

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



а вот если бы она выглядела так, словно только для чтения,
а сама по факту меняла бы данные,
то это был бы сюрприз для стороннего программиста.


Придется наверное смириться с const_cast внутрях

ага. потом программер будет чесать репу:
почему так? объект не должен был измениться,
но мало того, что изменился, так ещё и покрашел весь процесс.

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

он же не знает, что там под капотом конст_каст снимает константность,
и затем состояние объекта изменяется.

он думает: ну раз функция якобы только для чтения,
и раз она принимает объект по константной ссылке, то значит не будет его изменять.

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


модификация объекта, рожденного константой - UB.
оптимизирующий компилятор запросто может поместить такой объект в область памяти только для чтения.

const_cast + modification = upppssss ...

-----------------------------------------------------

вы боретесь с последствиями ущербного дизайна.
и в попытке заставить его через палку работать,
готовы наставить таких костылей,
которые могут привести к трагичным последствиям.


Название: Re: mutable метод?
Отправлено: Igors от Апрель 05, 2015, 07:18
напротив. выглядит так, словно DoSomething хочет изменить состояние data
и по факту именно это и происходит.

есть логическое разделение: "только для чтения", и "для изменения".
ну так вот, и внешний дизайн DoSomething,
и её реализация указывают,
что она будет изменять объект.
Опять-таки формально - да. Но посмотрим на это с точки зрения AnotherClass. Сколько раз он бы ни звал GetNumFiles (или любой др геттер) - рез-т тот же самый, для клиента ничего не меняется. А что там происходит внутри  MyClass - его личное дело, знание этих подробностей только вредит AnotherClass'у

вы боретесь с последствиями ущербного дизайна.
и в попытке заставить его через палку работать,
готовы наставить таких костылей,
которые могут привести к трагичным последствиям.
Сейчас сделано так: геттеры объявлены const, но практически все члены mutable. Этот дизайн мне кажется столь же ущербным как и const_cast. Но снимать const еще менее выгодно. Разумеется GetNumFiles virtual, а MyClass - только один из наследников большого базового класса. Если делать как Вы предложили, (строго следовать канонам), то вся эта иерархия классов практически полностью теряет константность.

Немного о предметной части. Есть "просто картинка" (файл). Есть "анимированная картинка" т.е. один файл хранит набор кадров. И есть набор файлов-картинок (напр 1.png, 2.png и.т.д) который тоже должен трактоваться как "картинка с кадрами".  Вот для клиента все это представляется одним классом


Название: Re: mutable метод?
Отправлено: Old от Апрель 05, 2015, 07:29
А кто, как и почему может "сбросит" данные, после чего их приходится обновлять?


Название: Re: mutable метод?
Отправлено: Igors от Апрель 05, 2015, 11:16
А кто, как и почему может "сбросит" данные, после чего их приходится обновлять?
Особо не вникал, один из вариантов - перегрузить текстуру (или все). Метод Reset просто зачищает контейнер, когда кто-то полезет за картинкой - перечитается. Возможно есть и др варианты. Изменить это в большом проекте непросто, да и работало без нареканий.


Название: Re: mutable метод?
Отправлено: Old от Апрель 05, 2015, 11:33
Особо не вникал, один из вариантов - перегрузить текстуру (или все). Метод Reset просто зачищает контейнер, когда кто-то полезет за картинкой - перечитается. Возможно есть и др варианты.
Ну так метод reset и должен обновлять, он точно не const.

Изменить это в большом проекте непросто, да и работало без нареканий.
Если работало без нареканий, зачем туда лезть. Если что-то добавляете, то сделайте по аналогии с тем, как сделано там.
Это одним движением исправить все равно не получиться.


Название: Re: mutable метод?
Отправлено: Igors от Апрель 05, 2015, 12:05
Ну так метод reset и должен обновлять, он точно не const.
Ну а чего ему всякий раз лезть сканировать файлы если это никому (пока) не нужно? В принципе у меня нет возражений против "lazy calculations"

Если работало без нареканий, зачем туда лезть. Если что-то добавляете, то сделайте по аналогии с тем, как сделано там. Это одним движением исправить все равно не получиться.
А я и не лезу - просто обдумываю/советуюсь как лучше сделать, ситуация-то типовая, наверняка еще не раз вылезет.


Название: Re: mutable метод?
Отправлено: Old от Апрель 05, 2015, 12:15
А я и не лезу - просто обдумываю/советуюсь как лучше сделать, ситуация-то типовая, наверняка еще не раз вылезет.
Код
C++ (Qt)
int MyClass::GetNumFiles( void ) const  // ну явно const, возвращает инфу
{
 if (mData->size() == 0)    // данные были сброшены?
   mData->updateData();              // пересоздать данные (никак не const)
 return mData->size();
}
 


Название: Re: mutable метод?
Отправлено: Igors от Апрель 05, 2015, 13:36
Ну чего там скромничать с одним членом, тогда уж так
Код
C++ (Qt)
struct CTest {
CTest( void ) : mHook(this)
{
}
 
void Modify( void ) const
{
mHook->mA = 0;
}
 
CTest * mHook;
int mA;
};
 
:)


Название: Re: mutable метод?
Отправлено: Old от Апрель 05, 2015, 13:39
А что это и к чему?


Название: Re: mutable метод?
Отправлено: andrew.k от Апрель 08, 2015, 21:20
А что это и к чему?
Это типа, если совесть мучает писать везде mutable (и поделом), то сохраняешь не константный this и творишь чудеса в решете)

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

Может добавить "dirty state" и пусть клиент сам проверяет.
Код
C++ (Qt)
if(obj->isDirty() /*|| obj->isReady()*/)
  obj->update();
А при этому GetNumFiles() const должен возвращать значение, которое даже в состоянии "dirty" должно иметь смысл, потому что и до и после обновления оно не должно меняться, у нас ведь логическая константность требуется?


Название: Re: mutable метод?
Отправлено: Igors от Апрель 09, 2015, 08:08
А по сабжу: очень некрасиво как-то выходит.
Вот захотел я как клиент класса узнать количество файлов, а там целая жизнь происходит, что-то обновляется, что-то загружается, непрозрачно, нелогично, некрасиво. А я просто хотел число, чтобы на форме показать.
Пример
Код
C++ (Qt)
virtual QSize sizeHint() const
Просто хотел узнать размер, а там целая жизнь произошла! Все позиции чайлд виджетов изменились, а объявлен const

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

Может добавить "dirty state" и пусть клиент сам проверяет.
Код
C++ (Qt)
if(obj->isDirty() /*|| obj->isReady()*/)
  obj->update();
Это уже звучало, клиент использует геттеры очень массово. Требование окружать каждый геттер проверкой - это возложить на клиента массу забот которые ему совершенно не нужны.

Также заметим что ситуация эта возникает только в одном из унаследованных классов, в остальных все прекрасно ложится "по книжке"


Название: Re: mutable метод?
Отправлено: Old от Апрель 09, 2015, 09:00
Пример
Код
C++ (Qt)
virtual QSize sizeHint() const
Просто хотел узнать размер, а там целая жизнь произошла! Все позиции чайлд виджетов изменились, а объявлен const
А почему в sizeHint все позиции детей меняются? На каком основании?


Название: Re: mutable метод?
Отправлено: Igors от Апрель 09, 2015, 09:09
А почему в sizeHint все позиции детей меняются? На каком основании?
Не суть. Главное - что выполняется "отложенное действие", лайаут не бросается все пересчитывать при каждом изменении, а взводит флажок dirty. Когда же клиент вызывает пересчет - для него это const операция, и это правильно. 


Название: Re: mutable метод?
Отправлено: Old от Апрель 09, 2015, 09:16
Не суть. Главное - что выполняется "отложенное действие", лайаут не бросается все пересчитывать при каждом изменении, а взводит флажок dirty. Когда же клиент вызывает пересчет - для него это const операция, и это правильно.  
Значит sizeHint ничего не пересчитывает и спокойно может быть const. В чем проблема?
Тема началась с того, что вы хотели менять состояние объекта из const методов - так нельзя. Но вы можете из const методов менять состояние других объектов, которыми объект может владеть. Что и показывал мой пример.

В контексте вашей проблемы:
Код
C++ (Qt)
class ImageMovie
{
public:
   int getFileNum() const
   {
       if( !m_cache->size() )
           m_cache->preloadFiles();    // вызываем не const-метод кеша. Меняем состояние объекта кеша, а не нашего
       return m_cache->size();
   }
 
private:
   ImageCachePtr m_cache;
};
 


Название: Re: mutable метод?
Отправлено: andrew.k от Апрель 09, 2015, 15:13
А почему в sizeHint все позиции детей меняются? На каком основании?
Не суть. Главное - что выполняется "отложенное действие", лайаут не бросается все пересчитывать при каждом изменении, а взводит флажок dirty. Когда же клиент вызывает пересчет - для него это const операция, и это правильно.  
Ничего он не выставляет и не двигает никуда детей.
Просто возвращает текущее значение.


Название: Re: mutable метод?
Отправлено: Igors от Апрель 10, 2015, 06:07
Ничего он не выставляет и не двигает никуда детей.
Просто возвращает текущее значение.
Я тоже так думал (на основании 1-2 экспериментов). Кстати вот как решают это в Qt
Код
C++ (Qt)
QSize QBoxLayout::sizeHint() const
{
   Q_D(const QBoxLayout);
   if (d->dirty)
       const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
   return d->sizeHint;
}


Название: Re: mutable метод?
Отправлено: _Bers от Апрель 11, 2015, 10:40
Кстати вот как решают это в Qt
Код
C++ (Qt)
QSize QBoxLayout::sizeHint() const
{
   Q_D(const QBoxLayout);
   if (d->dirty)
       const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
   return d->sizeHint;
}

Код:
const QBoxLayout trololo;
trololo.sizeHint(); // <--- UB

Изменение состояния объекта, рожденного константой есть UB.

На практике сталкивался с случаями,
когда компилятор размещал такие объекты в памяти "только для чтения".

В результате, функции которые пытались изменить состояние объекта
отваливались с аккцесс-виолейшеном.

const_cast можно применять только в 2х случаях:
1. Когда вы точно знаете, что состояние объекта изменено не будет.
Например, студенты медицинского факультета написали библиотеку,
в которой вообще нигде нет ни одного const.

Но тем не менее, вы знаете что часть методов хоть и не const,
но на самом деле состояние объекта не изменяют

В этом случае можно скастить аргумент,
что бы компилятор позволил вызовы функций.

2. Когда вы точно знаете, что объект,
над которым творят такие бесчеловеченые издевательства,
был рожден не как константа.





Название: Re: mutable метод?
Отправлено: Авварон от Апрель 11, 2015, 19:58

Код:
const QBoxLayout trololo;
trololo.sizeHint(); // <--- UB

Очевидно, что это не так. Иначе бы кутешники не пользовались этим повсеместно (уж кто-кто, а они поддерживают тучу компиляторов и платформ)


Название: Re: mutable метод?
Отправлено: Old от Апрель 11, 2015, 21:25
Очевидно, что это не так. Иначе бы кутешники не пользовались этим повсеместно (уж кто-кто, а они поддерживают тучу компиляторов и платформ)
Разговор идет не про Qt, а про константные объекты.
Например, при компиляции прошивок для микроконтроллеров, компилятор обязан все константные объекты выносить в секции, которые размещаются в read only памяти. ОЗУ в МК ограниченно, и все по максимуму стараются перенести в EEPROM.


Название: Re: mutable метод?
Отправлено: Igors от Апрель 12, 2015, 09:16
Но тем не менее, вы знаете что часть методов хоть и не const,
но на самом деле состояние объекта не изменяют
Спасибо за подробные объяснения, я понял, просто интересует др аспект. Разумеется "изменение состояния" истинное, с этим никто не спорит. Но с др стороны все эти изменения замкнуты внутри класса. Очень плохо посвящать др классы в эти внутренние подробности, для них геттеры должны оставаться const. Вариант с "выносом указателя" на мой взгляд еще хуже, const_cast легко найти, их всего 2 (если не 1). А с указателем "концы в воду".

И, повторюсь, интересно посмотреть на это с точки зрения иерархии классов, напр
Код
C++ (Qt)
virtual int CSinglePicture::FramesTotal( void ) const;   // никаких проблем, чистейшей воды const  
virtual int CAnimatedPicture::FramesTotal( void ) const;   // тоже const  
virtual int CMultiFilePicture::FramesTotal( void ) const;   // вот тут (и только тут) нужен GetNumFiles а он не const
Конечно это не фатальная проблема, я спокойно отношусь к любым приведениям, типа "надо так надо, значит лучшего решения пока нет". Просто интересно какие еще возможности.

Спасибо


Название: Re: mutable метод?
Отправлено: _Bers от Апрель 12, 2015, 21:22
Очевидно, что это не так. Иначе бы кутешники не пользовались этим повсеместно (уж кто-кто, а они поддерживают тучу компиляторов и платформ)

у языка с++ есть стандарт.
в котором все прописано.



Название: Re: mutable метод?
Отправлено: Авварон от Апрель 13, 2015, 09:20
Разговор идет не про Qt, а про константные объекты.
Например, при компиляции прошивок для микроконтроллеров, компилятор обязан все константные объекты выносить в секции, которые размещаются в read only памяти. ОЗУ в МК ограниченно, и все по максимуму стараются перенести в EEPROM.

Объекты, созданные на стеке? Ну-ну.
А если вы делаете глобал не-под статики то у меня для вас плохие новости - это один сплошной UB, даже без const.


Название: Re: mutable метод?
Отправлено: andrew.k от Апрель 13, 2015, 16:10
Ничего он не выставляет и не двигает никуда детей.
Просто возвращает текущее значение.
Я тоже так думал (на основании 1-2 экспериментов). Кстати вот как решают это в Qt
Код
C++ (Qt)
QSize QBoxLayout::sizeHint() const
{
   Q_D(const QBoxLayout);
   if (d->dirty)
       const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
   return d->sizeHint;
}
Читеры! Я вообще смотрел QWidget, в лейаут не догадался.

Странно, что мешало  им сделать d->sizeHint mutable? В этом случае это было бы оправдано.
Ведь он по сути хранит кешированное значение. Так был бы более красивый и понятный код.


Название: Re: mutable метод?
Отправлено: Авварон от Апрель 13, 2015, 21:22
Читеры! Я вообще смотрел QWidget, в лейаут не догадался.

Странно, что мешало  им сделать d->sizeHint mutable? В этом случае это было бы оправдано.
Ведь он по сути хранит кешированное значение. Так был бы более красивый и понятный код.

setupGeom() изменяет 7 (!) переменных. Если их все сделать mutable, то проще const не писать