Oberon space
General Category => Общий раздел => Тема начата: Berserker от Апрель 13, 2011, 06:34:54 pm
-
Столкнулся с забавной проблемой во время написания кода на Delphi. Связана она с тем, что в методах класса все поля и методы класса перекрывают другие идентификаторы с такими же именами. Обратиться к идентификаторам этого же модуля точно можно используя полную нотацию: ModuleName.IdentName. Дело становится хуже, если обратится нужно к одному из модулей в секции USES. И вот почему.
Класс TForm (диалоговое окно) имеет поле Controls - дочерние контролы. С другой стороны, есть и используется одноимённый модуль Controls, из которого, нам, например, нужна пара констант. В результате Controls, Self.Controls указывает на поле, ModuleName.Controls выдаёт ошибку об отсутствии идентификатора Controls. То есть указать полное имя константы становится невоможным.
Решение найдено в обходном путе.
UNIT ModuleName;
USES ..., Controls;
...
CONST mbLeft = Controls.mbLeft;
...
...ModuleName.mbLeft...
то есть в импорте константы, а вернее создании её синонима.
PS. Если же у объекта будет ещё и поле, совпадающее с именем текущего модуля, то результат будет крайне плачевным. Из чего делаю вывод, что автоматический импорт идентификаторов в текущую область видимости есть зло, которого, к счастью, нет в Оберонах.
-
Не понимаю... "ModuleName.Controls" в моем понимании и должно выдавать такую ошибку. а разве Controls.mbleft - не срабатывает?
-
Controls.mbleft обращается к TForm.Controls. Указать, что ты хочешь обратиться именно к модулю Controls, а не полю объекта, невозможно.
-
Controls.mbleft обращается к TForm.Controls. Указать, что ты хочешь обратиться именно к модулю Controls, а не полю объекта, невозможно.
Так и должно быть...Вот такой код работает без танцев:
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Button=mbleft then ShowMessage('mbLeft');
end;
-
Работает, но нарушает принцип указания полных имён идентификаторов, коим я пользуюсь. И вообще, всегда должна быть возможность точно обратиться к любому идентификатору.
-
Работает, но нарушает принцип указания полных имён идентификаторов, коим я пользуюсь. И вообще, всегда должна быть возможность точно обратиться к любому идентификатору.
Серьезно :) , даже в том случае когда одноименная локальная переменная перекрывает глобальную ?
-
Конечно. Когда локальная перекрывает глобальную, к глобальной обращаются как к UnitName.VarName.
-
Конечно. Когда локальная перекрывает глобальную, к глобальной обращаются как к UnitName.VarName.
Cурово, КП например отказывается такое даже компилировать (и меня это почему то не удивляет).
MODULE m;
IMPORT Log := StdLog, In := i21sysIn, Math;
VAR f:INTEGER;
PROCEDURE p*;
VAR f:INTEGER;
BEGIN
f:=15;
Log.Int(f);
Log.Ln;
Log.Int(m.f);
END p;
BEGIN
f:=10;
END m.
-
Значит в Оберонах существенная проблема. В паскалях традиционно можно обращаться ко всем идентификаторам модуля точно так же, как и к идентификаторам других модулей. Чем это вас смущает?
UNIT A;
USES B, C;
CONST
q = 7;
v1 = A.q;
v2 = B.q;
v3 = C.q;
-
Значит в Оберонах существенная проблема. В паскалях традиционно можно обращаться ко всем идентификаторам модуля точно так же, как и к идентификаторам других модулей. Чем это вас смущает?
UNIT A;
USES B, C;
CONST
q = 7;
v1 = A.q;
v2 = B.q;
v3 = C.q;
Меня ничего не смущает - наоборот , то что Вас смущает представляется мне естественным. Тот эффект про который вы говорите не описан в ЯП - и я его последние 20 лет трактую (для себя) с позиции затенения локальной переменной... и до сих пор проблем не было..
-
Значит в Оберонах существенная проблема. В паскалях традиционно можно обращаться ко всем идентификаторам модуля точно так же, как и к идентификаторам других модулей. Чем это вас смущает?
В Борланд-Паскалях. В Аде - нет (насколько помню).
И это правильно, т.к. общий механизм "прорыва через перекрытие" сделать всё равно нельзя. Если у Вас процедура второго уровня, как Вы доберётесь до перекрытой переменной в объемлющей процедуре?
-
Не могу проверить в Аде, но сомневаюсь, что там по-другому.
И это правильно
Отнюдь. Сперва ради упрощения жизни вводится ошибковлекующее правило автоматического импорта идентификаторов в текущую область видимости для модулей и подпрограмм, а потом оказывается, что явно прямо обратиться к элементу родителя нельзя.
Посудите сами. Модули ко всем своим основополагающим функциям выполняют также и роль пространства имён. Это позволяет в любом модуле иметь на верхнем уровне столько непереопределяемых идентификаторов, сколько импортируется других модулей + 1.
MODULE A;
IMPORT B, C, D;
(* Область видимости: A, B, C, D *)
Тогда на уровне верхнем (пока без учёта подпрограмм) можем явно и безошибочно обращаться к любому идентификатору из 4-х указанных модулей. При этом они для нас равнозначны. Вероятность ошибки - 0.
A.Const
B.Const
C.Const
D.Const
Вернёмся к подпрограммам. В них создаётся новая область видимости и туда записываются лишь идентификаторы модулей. И снова всё чётко и явно.
PROCEDURE Proc;
CONST
Const = B.Const;
Проблема возникает во вложенных подпрограммах по одной простой причине. Потому что отношение "подпрограмма - дочерняя подпрограмма" само по себе образует псевдомодуль со своей областью видимости, типами, константами и т.д. Только это уже подлиннее цепочка: A -> Proc -> ChildProc. Решение просто - позволить работать синтаксису Proc.Const для родительских подпрограмм и запретить переопределять ключевые идентификаторы пространств имён. То есть в цепочке A -> Proc -> ChildProc на уровне ChildProc не должно быть локальных имён A, B, C, D, Proc.
-
В доказательство того, что цепочки подпрограммы сами есть модули, приведу пример из недавнего кода. Модуль из 366 строк, 1 процедура, никаких глобальных переменных. Зато сама это процедура является модулем по отношению к цепочке (10) дочерних, что объявлены в ней.
-
В доказательство того, что цепочки подпрограммы сами есть модули, приведу пример из недавнего кода. Модуль из 366 строк, 1 процедура, никаких глобальных переменных. Зато сама это процедура является модулем по отношению к цепочке (10) дочерних, что объявлены в ней.
Да причем тут это - в примере ,который вы привели выше (работающем) переменные имели один уровень глобальности. при понижении его (переходе к локальной области видимости) действует правило затенения.. в стандартном Паскале оно постулируется, когда борланды расширяли язык - они просто следовали этому принципу.
-
Правило затемнения - отлично. Но при чём тут оно к возможности при этом обращаться к любому идентификатору по полному имени? Вы вспомните файловую систему. Краткое имя обращается к файлу в текущем каталоге, но кто мешает явно указать путь до родительского? В чём концептуальная проблема?
-
По полному имени обращаемся к внешнему идентификатору. Внутри модуля его имя неизвестно. Его самого ещё как бы нет. Даже технически - модуль только компилируется, откуда ему быть.
Ваша логика тоже понятна, и не менее логична, чем моя :)
Но к чему огород городить (в случае, если полная квалификация в языке обязательна). Тупее - так, как есть в Обероне. Тупее = меньше мест для каких-то потенциальных проблем.
-
Правило затемнения - отлично. Но при чём тут оно к возможности при этом обращаться к любому идентификатору по полному имени? Вы вспомните файловую систему. Краткое имя обращается к файлу в текущем каталоге, но кто мешает явно указать путь до родительского? В чём концептуальная проблема?
Вы про что вообще говорите: 1. Про свое ЛИЧНОЕ мироощущение 2. Общепринятые нормы 3. Ошибку в компиляторе Делфи(его несоответствие нормам Объектного Паскаля),
4. Несоответствие Объектного Паскаля общепринятым нормам.?
Я так понял (из исходного сообщения), что о п.3 и п.4 и ответил вам -т.е. не обнаружил ни ошибки ни разрыва в моем понимании Делфи и общих принципов (слава богу).
-
По полному имени обращаемся к внешнему идентификатору. Внутри модуля его имя неизвестно. Его самого ещё как бы нет. Даже технически - модуль только компилируется, откуда ему быть.
Ваша логика тоже понятна, и не менее логична, чем моя :)
Но к чему огород городить (в случае, если полная квалификация в языке обязательна). Тупее - так, как есть в Обероне. Тупее = меньше мест для каких-то потенциальных проблем.
Просто, когда есть возможность явно указывать имена, то не нужно изощраться с тем, чтобы имена нижнего уровня ни в коем случае не перекрывали имена верхнего. Это то, что я по наивности всегда считал ключевой фишкой паскаля.
Правило затемнения - отлично. Но при чём тут оно к возможности при этом обращаться к любому идентификатору по полному имени? Вы вспомните файловую систему. Краткое имя обращается к файлу в текущем каталоге, но кто мешает явно указать путь до родительского? В чём концептуальная проблема?
Вы про что вообще говорите: 1. Про свое ЛИЧНОЕ мироощущение 2. Общепринятые нормы 3. Ошибку в компиляторе Делфи(его несоответствие нормам Объектного Паскаля),
4. Несоответствие Объектного Паскаля общепринятым нормам.?
Я так понял (из исходного сообщения), что о п.3 и п.4 и ответил вам -т.е. не обнаружил ни ошибки ни разрыва в моем понимании Делфи и общих принципов (слава богу).
Я говорю про практику. Делфи, Free Pascal, Virtual Pascal как минимум поддерживают нотацию обращения к своему модулю. Но при этом они не поддерживают чуть более длинную нотацию, чтобы от имени своего модуля перейти к другим модулям: A.B.Const. Вот эта длинная нотация помогла бы решить проблему. Ну и в целом высказал пожелание, чтобы и к подпрограммам применить это правило и дать возможность ссылаться на Proc.Consts, указав, что отношение между подпрограммой и дочерней подпрограммой точно такие же, как между модулем и подпрограммами верхнего уровня.