"class const - метод" - можно ли организовать?
От: _hum_ Беларусь  
Дата: 23.09.15 10:20
Оценка:
Доброго дня.
Раньше как-то сильно не задумывался и негласно для себя считал, что const-спецификатор метода гарантирует неизменность полей объекта, из которого этот метод вызывается. А вот когда начал работать с сигнал-слот-архитектурой, увидел, что это, вообще говоря, не так:

class C_B;

////////////////////////////
////       C_A          ////
////////////////////////////
class C_A
{
    C_B* m_pB;

    int  m_x;
public:
    //------------------------------------------
    void set_pB(C_B* pB){m_pB = pB;}
    //------------------------------------------
    void set_x(int x)   {m_x = x;}
    //------------------------------------------
    void foo_A(void)const;// несмотря на const, вызов этой функции может изменить значение поля m_x
    //------------------------------------------
};
////////////////////////////

////////////////////////////
////       C_B          ////
////////////////////////////
class C_B
{
    C_A* m_pA;
public:
    //------------------------------------------
    C_B(C_A* pA):m_pA(pA){}
    //------------------------------------------
    void foo_B(void){ m_pA->set_x(10);}
    //------------------------------------------
};
////////////////////////////

//------------------------------------------
void C_A::foo_A(void)const
{
    m_pB->foo_B();
}
//------------------------------------------



void main(void) 
{
    C_A A;
    C_B B(&A);

    A.set_pB(&B);

    A.set_x(0);
    A.foo_A();
}


Потому встал вопрос, есть ли в других языках или в новом стандарте с++ что-нибудь наподобие "class const — метода", который делает
константными ЛЮБЫЕ this данного класса в своей области видимости, и тем самым предотвращает всякое изменение любого (а значит и заданного)объекта данного класса?

Это понадобилось, в частности, для того, чтобы на этапе компиляции отлавливать зацикливания. Например,
если foo_A воспринимаеть как сигнал о модификации объекта, к которому приконнекчен слот foo_B, то хотелось бы иметь гарантиии, что вызов этого слота ен приведет к повторному изменению объекта и повторному вызову сигнала (тем самым входя в цикл).
Re: "class const - метод" - можно ли организовать?
От: VTT http://vtt.to
Дата: 23.09.15 10:30
Оценка:
У вас тут const-ность нарушается из-за наличия в классе не const ссылки на самого себя. Этот пример можно упростить до
class
t_Something
{
  protected: t_Something * m_p_non_const;
  protected: int           m_x = 0;
    
  public:
  t_Something(void)
  :  m_p_non_const(this)
  {
    // do nothing;
  }
    
  public: void
  Set_X(int const x) const
  {
    m_p_non_const->m_x = x;
  }
};

int
main(void)
{
  t_Something const something;
  something.Set_X(42);
}


Если хочется гарантий, то можно делать immutable объекты.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Отредактировано 23.09.2015 10:37 VTT . Предыдущая версия . Еще …
Отредактировано 23.09.2015 10:36 VTT . Предыдущая версия .
Re: "class const - метод" - можно ли организовать?
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 23.09.15 10:56
Оценка:
Здравствуйте, _hum_, Вы писали:

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


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

Реализовать твоё поведение вряд ли возможно в принципе. Потому как на каждую операцию записи нам необходимо доказать, что невозможно изменение содержимого любого константного указателя. Что нетривиально.
Re[2]: "class const - метод" - можно ли организовать?
От: _hum_ Беларусь  
Дата: 23.09.15 11:04
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>У вас тут const-ность нарушается из-за наличия в классе не const ссылки на самого себя. Этот пример можно упростить до

VTT>
VTT>class
VTT>t_Something
VTT>{
VTT>  protected: t_Something * m_p_non_const;
VTT>  protected: int           m_x = 0;
    
VTT>  public:
VTT>  t_Something(void)
VTT>  :  m_p_non_const(this)
VTT>  {
VTT>    // do nothing;
VTT>  }
    
VTT>  public: void
VTT>  Set_X(int const x) const
VTT>  {
VTT>    m_p_non_const->m_x = x;
VTT>  }
VTT>};

VTT>int
VTT>main(void)
VTT>{
VTT>  t_Something const something;
VTT>  something.Set_X(42);
VTT>}
VTT>


да, спасибо, так нагляднее.

VTT>Если хочется гарантий, то можно делать immutable объекты.


вы не совсем тогда поняли — я хочу гарантию, что ВЫЗОВ ДАННОГО МЕТОДА не приведет к изменению данных, а не что эти данные ВОВСЕ не должны меняться.
и реализация этого вроде бы не сложная — компилятор должен все this-указатели данного класса, которые попадают в область видимости данного метода, считать const this. тогда бы он смог выдать ошибку в вашем примере, типа:
"m_p_non_const->m_x = x : использование неконстантного указателя на класс t_Something внутри class const метода"
Re[2]: "class const - метод" - можно ли организовать?
От: _hum_ Беларусь  
Дата: 23.09.15 11:08
Оценка:
Здравствуйте, Mystic, Вы писали:

M>Здравствуйте, _hum_, Вы писали:


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


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


так одно другого не исключает мы говорим компилятору (который включает в себя и синтаксический анализатор, и оптимизатор), как мы считаем, должен себя вести объект, а уже компилятор использует эту инфу для многих целей — и для отлавливания семантических ошибок (сказали, что будем использовать без изменения, и почему-то пишем изменяющий код) и для оптимизации.

M>Реализовать твоё поведение вряд ли возможно в принципе. Потому как на каждую операцию записи нам необходимо доказать, что невозможно изменение содержимого любого константного указателя. Что нетривиально.


вроде бы несложно (см. пост выше для VTT)
Re[3]: "class const - метод" - можно ли организовать?
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 23.09.15 11:10
Оценка:
Здравствуйте, _hum_, Вы писали:


__>вы не совсем тогда поняли — я хочу гарантию, что ВЫЗОВ ДАННОГО МЕТОДА не приведет к изменению данных, а не что эти данные ВОВСЕ не должны меняться.

__>и реализация этого вроде бы не сложная — компилятор должен все this-указатели данного класса, которые попадают в область видимости данного метода, считать const this. тогда бы он смог выдать ошибку в вашем примере, типа:
__>"m_p_non_const->m_x = x : использование неконстантного указателя на класс t_Something внутри class const метода"

Это может быть и некоторый другой класс, откуда компилятору знать, куда будет указывать m_p_non_const в момент вызова метода?
Re[4]: "class const - метод" - можно ли организовать?
От: _hum_ Беларусь  
Дата: 23.09.15 11:18
Оценка:
Здравствуйте, Mystic, Вы писали:

M>Здравствуйте, _hum_, Вы писали:



__>>вы не совсем тогда поняли — я хочу гарантию, что ВЫЗОВ ДАННОГО МЕТОДА не приведет к изменению данных, а не что эти данные ВОВСЕ не должны меняться.

__>>и реализация этого вроде бы не сложная — компилятор должен все this-указатели данного класса, которые попадают в область видимости данного метода, считать const this. тогда бы он смог выдать ошибку в вашем примере, типа:
__>>"m_p_non_const->m_x = x : использование неконстантного указателя на класс t_Something внутри class const метода"

M>Это может быть и некоторый другой класс, откуда компилятору знать, куда будет указывать m_p_non_const в момент вызова метода?


неважно, куда будет указывать. главное, какого он типа. а это определяется на этапе компиляции.
(речь не о динамических отлавливаниях изменений, когда, действительно, требуется рантайм-анализ указателей, а о статическом — мы просто тупо запрещаем в коде, попадающем в область видимости данного метода, менять что-то через указатели данного класса)
Re[3]: "class const - метод" - можно ли организовать?
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 23.09.15 11:21
Оценка:
Здравствуйте, _hum_, Вы писали:

__>вроде бы несложно (см. пост выше для VTT)


Например?


struct X 
{ 
    X * next; 
    void f() const { next->next = nullptr; }
};

void something(X * x);

int main()
{
    X x;
    something(&x); // Какими будут поля структуры X после выхода из метода?
    x.f(); // Как понять, есть нарушение или нет?
    return 0;
}

#ifdef UB
    void something(X * x)
    {
        x->next = x;
    }
#else
    void something(X * x)
    {
        x->next = new X();
    }
#endif
Re[5]: "class const - метод" - можно ли организовать?
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 23.09.15 11:23
Оценка:
Здравствуйте, _hum_, Вы писали:

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

__>(речь не о динамических отлавливаниях изменений, когда, действительно, требуется рантайм-анализ указателей, а о статическом — мы просто тупо запрещаем в коде, попадающем в область видимости данного метода, менять что-то через указатели данного класса)

Т. е. ты хочешь, чтобы некоторый superconst влиял на все экземпляры данного класса?
Re[5]: "class const - метод" - можно ли организовать?
От: VTT http://vtt.to
Дата: 23.09.15 11:23
Оценка:
Здравствуйте, _hum_, Вы писали:

__>(речь не о динамических отлавливаниях изменений, когда, действительно, требуется рантайм-анализ указателей, а о статическом — мы просто тупо запрещаем в коде, попадающем в область видимости данного метода, менять что-то через указатели данного класса)


Ну можно попробовать использовать не указатели, а специальный reference_wrapper класс, который бы давал доступ к изменяемому объекту только если он сам не const.
т.е. имел бы два метода (в отличие от стандартного)
T & get();
T const & get() const;
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[6]: "class const - метод" - можно ли организовать?
От: _hum_ Беларусь  
Дата: 23.09.15 11:26
Оценка:
Здравствуйте, Mystic, Вы писали:

M>Здравствуйте, _hum_, Вы писали:


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

__>>(речь не о динамических отлавливаниях изменений, когда, действительно, требуется рантайм-анализ указателей, а о статическом — мы просто тупо запрещаем в коде, попадающем в область видимости данного метода, менять что-то через указатели данного класса)

M>Т. е. ты хочешь, чтобы некоторый superconst влиял на все экземпляры данного класса?


ну, можно и так сказать (только не на сами экземпляры, а на указатели на эти экземпляры, и не на все указатели, а на те, что по коду попадают в область видимости данного superconst- метода)
Re[4]: "class const - метод" - можно ли организовать?
От: _hum_ Беларусь  
Дата: 23.09.15 11:32
Оценка:
Здравствуйте, Mystic, Вы писали:

M>Здравствуйте, _hum_, Вы писали:


__>>вроде бы несложно (см. пост выше для VTT)


M>Например?


M>

M>struct X 
M>{ 
M>    X * next; 
M>    void f() const { next->next = nullptr; }
M>};

M>void something(X * x);

M>int main()
M>{
M>    X x;
M>    something(&x); // Какими будут поля структуры X после выхода из метода?
M>    x.f(); // Как понять, есть нарушение или нет?
M>    return 0;
M>}

M>#ifdef UB
M>    void something(X * x)
M>    {
        x->>next = x;
M>    }
M>#else
M>    void something(X * x)
M>    {
        x->>next = new X();
M>    }
M>#endif

M>

если под
void f() const { next->next = nullptr; }

понималось
void f() class_const { next->next = nullptr; }

то компилятор уже в этом месте не пропустит, ибо у вас в области видимости class_const-метода осуществляется изменение через указатель на данный класс.
иными словами, компилятор трактует это как:
void f() class_const { (const X*)next->next = nullptr; }

и резонно выдает ошибку.
Re: "class const - метод" - можно ли организовать?
От: Mr.Delphist  
Дата: 23.09.15 11:38
Оценка:
Здравствуйте, _hum_, Вы писали:

__> Доброго дня.

__> Раньше как-то сильно не задумывался и негласно для себя считал, что const-спецификатор метода гарантирует неизменность полей объекта, из которого этот метод вызывается. А вот когда начал работать с сигнал-слот-архитектурой, увидел, что это, вообще говоря, не так:

Авторы не захотели прописать mutable напротив поля m_x (или закладывались на какие-то компиляторы без поддержки оного), вот и делается такой "хак себя", когда весь класс неизменный, но определённое поле — эдакая temp-переменная, вроде как захваченная в контекст.

Собственно, ничего нового в явлении нет, идиома известна ещё со времён plain C, равно как и грабли ей сопутствующие http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule
Re[6]: "class const - метод" - можно ли организовать?
От: _hum_ Беларусь  
Дата: 23.09.15 11:41
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>Здравствуйте, _hum_, Вы писали:


__>>(речь не о динамических отлавливаниях изменений, когда, действительно, требуется рантайм-анализ указателей, а о статическом — мы просто тупо запрещаем в коде, попадающем в область видимости данного метода, менять что-то через указатели данного класса)


VTT>Ну можно попробовать использовать не указатели, а специальный reference_wrapper класс, который бы давал доступ к изменяемому объекту только если он сам не const.

VTT>т.е. имел бы два метода (в отличие от стандартного)
VTT>
VTT>T & get();
VTT>T const & get() const;
VTT>

а разве это может спасти от ситуации с моим первым примером, когда C_B не использует (ибо его никто не может обязать это делать) такой враппер для хранения ссылки на объект C_A?
Re[2]: "class const - метод" - можно ли организовать?
От: _hum_ Беларусь  
Дата: 23.09.15 11:45
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>Здравствуйте, _hum_, Вы писали:


__>> Доброго дня.

__>> Раньше как-то сильно не задумывался и негласно для себя считал, что const-спецификатор метода гарантирует неизменность полей объекта, из которого этот метод вызывается. А вот когда начал работать с сигнал-слот-архитектурой, увидел, что это, вообще говоря, не так:

MD>Авторы не захотели прописать mutable напротив поля m_x (или закладывались на какие-то компиляторы без поддержки оного), вот и делается такой "хак себя", когда весь класс неизменный, но определённое поле — эдакая temp-переменная, вроде как захваченная в контекст.


MD>Собственно, ничего нового в явлении нет, идиома известна ещё со времён plain C, равно как и грабли ей сопутствующие http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule


не совсем понял, о чем вы. мютабельность полей тут не при чем.
Re[7]: "class const - метод" - можно ли организовать?
От: VTT http://vtt.to
Дата: 23.09.15 11:50
Оценка:
Ну если уж вы часть программы не контролируете, то никакой superconst тут не поможет.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[8]: "class const - метод" - можно ли организовать?
От: _hum_ Беларусь  
Дата: 23.09.15 12:00
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>Ну если уж вы часть программы не контролируете, то никакой superconst тут не поможет.


нет, речь о другом — в самом классе C_A я могу проконтролировать, чтобы все указатели на себя использовались только через такие врапперы, но за пределами этого класса — нет. и глупо рассчитывать на то, что использовать объекты этого класса будут только через врапперы (ну разве только специально писать инструкцию пользования данным классом, что довольно дико).
а компилятор мог бы это автоматом все отслеживать и говорить, где что не так...
Re[9]: "class const - метод" - можно ли организовать?
От: VTT http://vtt.to
Дата: 23.09.15 12:10
Оценка:
Здравствуйте, _hum_, Вы писали:

__>а компилятор мог бы это автоматом все отслеживать и говорить, где что не так...


И как бы он мог это отслеживать? Информации о том, указывает ли указатель в классе C_B на тот же экземпляр класса C_A, что и this в методе foo_A у него в принципе нет. В приведенном примере это очевидно только из-за крайнего упрощения.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[10]: "class const - метод" - можно ли организовать?
От: _hum_ Беларусь  
Дата: 23.09.15 12:29
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>Здравствуйте, _hum_, Вы писали:


__>>а компилятор мог бы это автоматом все отслеживать и говорить, где что не так...


VTT>И как бы он мог это отслеживать? Информации о том, указывает ли указатель в классе C_B на тот же экземпляр класса C_A, что и this в методе foo_A у него в принципе нет. В приведенном примере это очевидно только из-за крайнего упрощения.


нет. и вы тоже не поняли значит. речь не о том, чтобы не модифицировать ЗАДАННЫЙ ЭКЗЕМПЛЯР, а о том, чтобы запретить модификацию ВСЕ ЭКЗЕМПЛЯРЫ, если код их модификации попадает "в нитку", которая запускается вызовом class_const-метода.

иными словами компилятор должен действовать так:

0) заводит стек функций
1) помещает в стек class_const-метод
2) пока стек не пуст, берет верхний элемент стека и для него анализирует тело функции:
2.1) все указатели типа C_A* трактует как const C_A* и выдает ошибку, если с помощью них производится попытка модификации;
2.2) если встретилась функция, то помещает в стек и переходит к 2);
2.3) делает pop для стека.
3) анализ завершен

(тут еще оговорки, конечно, для рекурсивных вызовов, но это тоже решаемо на этапе анализа — просто не надо добавлять в стек то, что уже раньше встречалось)
Re[11]: "class const - метод" - можно ли организовать?
От: VTT http://vtt.to
Дата: 23.09.15 12:39
Оценка:
Здравствуйте, _hum_, Вы писали:

__>нет. и вы тоже не поняли значит. речь не о том, чтобы не модифицировать ЗАДАННЫЙ ЭКЗЕМПЛЯР, а о том, чтобы запретить модификацию ВСЕ ЭКЗЕМПЛЯРЫ, если код их модификации попадает "в нитку", которая запускается вызовом class_const-метода.


__>иными словами компилятор должен действовать так:


__>0) заводит стек функций

__>1) помещает в стек class_const-метод
__>2) пока стек не пуст, берет верхний элемент стека и для него анализирует тело функции:
__> 2.1) все указатели типа C_A* трактует как const C_A* и выдает ошибку, если с помощью них производится попытка модификации;
__> 2.2) если встретилась функция, то помещает в стек и переходит к 2);
__> 2.3) делает pop для стека.
__>3) анализ завершен

__>(тут еще оговорки, конечно, для рекурсивных вызовов, но это тоже решаемо на этапе анализа — просто не надо добавлять в стек то, что уже раньше встречалось)


Запретить модификацию всех экземпляров можно в runtime — сделать в классе дополнительное статическое поле со счетчиком const обращений и кидать исключение, если при вызове не-const метода этот счетчик не 0.
А на этапе компиляции это сделать не получится, так как у компилятора может не быть реализации класса C_B (или это вообще голый интерфейс), и, соответственно, он не сможет определить наличие обращение к не-const экземплярам C_A в нем.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.