Inline vs CopyPast и способ передачи параметров в процедуры.

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Аватара пользователя
stikriz
энтузиаст
Сообщения: 612
Зарегистрирован: 15.03.2006 08:37:47

Сообщение stikriz »

Ask писал(а):constдля параметров типа string

Использовать var? :-)
Ask писал(а):не использовать constдля параметров [...], интерфейсов и объектов, а также записей, содержащих эти типыв качестве полей.

Это детские трудности непонимания указателей.
Короче, мнение очень спорное. Используйте const, особенно с типами string. Если проблемы с указателями, то есть очень много других интересных профессий.
alexey38
долгожитель
Сообщения: 1627
Зарегистрирован: 27.04.2011 19:42:31

Сообщение alexey38 »

Ask писал(а):Это не так. К сожалению, с ключевым словом "const" в Паскале связан ряд заблуждений. На самом деле:
1) Ключевое слово "const" НЕ влияет непосредственно на способ передачи параметра -- параметр может быть передан как по ссылке,
так и по значению в зависимости от платформы, настроек оптимизации и т.д.
2) Чтобы гарантировать передачу по ссылке, следует использовать модификатор "constref", но он был реализован относительно недавно, и разработчики компилятора не рекомендуют его использовать без веской причины.
3) Ключевое слово "const" интерпретируется как обещание *от программиста компилятору*, что указанная переменная не поменяется *нигде в программе* в период выполнения функции/процедуры. Обратите внимание, что именно от программиста к компилятору, а не наоборот. Компилятор проверяет выполнение этого обещания только в простейших случаях. Если оно всё же будет нарушено, это может привести к очень трудно отлаживаемым ошибкам и падениям, особенно в случае refcounted типов, таких как строки и интерфейсы.
4) Разработчики языка принципиально не будут менять перечисленные выше пункты -- на этот счёт были долгие и бурные дискуссии, их позиция окончательна.

Таким образом, можно порекомендовать использовать префикс const осторожно, относиться к нему как к оптимизации,
и уж во всяком случае не расставлять бездумно где попало -- например, современные версии Lazarus больше не добавляют "const" автоматически к
параметру setter'а свойств объекта.
К модификаторам var и out перечисленные проблемы не относятся -- их можно свободно использовать там, где они нужны по смыслу
(не путая друг с другом, конечно).


Я бы сказал, что Ваше мнение довольно сомнительное по существу.

Во-первых, важно запомнить, что Вы пишите программу на языке высокого уровня не только и не сколько для компилятора, сколько для себя (для программиста). Компилятор - это машина, не для нее стараемся. Если программа не разовая, то программисту код читать еще 100 раз. И вот ради себя любимого, чтобы облегчить себе жизнь мы используем Const, префиксы, суфиксы и прочие вещи делающие код ясным и понятным.

Во-вторых, смысл Const не в ссылке, если нужна гарантированная ссылка, то пишем Var. Опитимизационный смысл Const в несоздании копии для строк и записей. Для числовых полей копия все же создается, т.к. трудоемкость передачи через стек ссылки и числа одинаковая, а внутри кода процедуры косвенная адрессация работает медленее. Так что в части оптимизации кода Const - это правильное решение.

Что касается нового "constref", то если честно, не понял зачем это сделали. Если ты используешь Const, то берешь на себя обязательство не менять поле. А если не менять поле, то зачем нужна гарантированная ссылка? Нужен указатель? Тогда используй Var или Pointer, будет яснее. Тут либо для совместимости с С, либо для запутывания логики, когда вроде бы Const, но можешь все же использовать не по назначению. Для обычного кода очевидно, что constref не нужно использовать, т.к. он скрывает ясность кода.

P.S. Не знаю точно, как делает FPC (для разных опций), но Дельфи при передаче строк как Proc(Const strPar:String), передает ссылку, и не делает копию строки. А Proc(strPar:String) должен сделать копию строки, т.к. меняя в процедуре strPar не должна изменяться строка в вызывающем коде.

Добавлено спустя 5 минут 21 секунду:
Re: Inline vs CopyPast и способ передачи параметров в процедуры.
iN0k писал(а):то есть шаманское изменение привело к интерисующему меня результату.
а шаманскому, по тому что для меня не очевидна.
может кто-нибудь разьяснить полученный результат?


Результат говорит о том, что inline - работает так, как требовалось. Он весь код вызываемой процедуры копирует в место вызова.

Что касается Вашего исходного вопроса:

Код: Выделить всё

procedure F1(prm:rMyRecord);
begin
    F0(@prm);
end;


То нужно сделать так:

Код: Выделить всё

procedure F1(Var prm:rMyRecord);inline;
begin
    F0(@prm);
end;


Тогда вызов F1 будет идентичен вызову F0 без дубляжа кода обработки из F0, но и без лишнего перевызова в F1.
Ask
постоялец
Сообщения: 163
Зарегистрирован: 25.12.2008 02:51:37

Сообщение Ask »

Как видим, заблуждения о том, как работает const, действительно широко распространены :)
Господа, Я согласен с тем, что Ваше описание -- это хорошая модель для модификатора const.
К сожалению, на самом деле const работает не так.
Я потратил немало нажатий на кнопки, пытаясь убедить разработчиков FPC сделать, как Вы предлагаете --
прочтите хотя бы тот трэд, что Я привёл, а таких было несколько.
Разработчики отказались (и у них есть свои основания, хотя Я с ними и не согласен).
Как Я уже говорил:
1) const даёт компилятору выбор относительно способа передачи параметра (например, записи передаются по ссылке либо по значению в зависимости от SizeOf и платформы). Таким образом, при использовании const программист должен внимательно следить, чтобы семантика кода не зависела от способа передачи.
2) const даёт компилятору возможность предполагать, что обозначенная переменная не изменится *нигде в программе*, а не только непосредственно
в текущей процедуре. В частности, refcounted типы c модификатором const не делают incref/decref! Если вдруг значение переменной, переданной в качестве параметра по const, изменится, программа упадёт с *очень* трудно отлавливаемым AV в *эпилоге процедуры*, т.е. вообще не в пользовательском коде. Поскольку модифицирующий код может находиться в другом модуле, быть написанным другим программистом в другое время, и вообще относиться к другому уровню приложения, эту ошибку можно предотвратить только одним способом -- не использовать const c refcounted типами.

Отдельно хочу сказать, что "если нужна гарантированная ссылка, то пишем var" -- это стилистически неправильно, поскольку смешивает понятия способа передачи параметра и необходимости его модификации. Правильно будет "если нужна гарантированная ссылка, *и мы планируем изменять переменную*, то пишем var, иначе пишем constref.
Kemet
постоялец
Сообщения: 241
Зарегистрирован: 10.02.2010 18:28:32
Откуда: Временно оккупированная территория
Контактная информация:

Сообщение Kemet »

Видимо это какое-то индивидуальное восприятие.
Ведь строки, объекты, интерфейсы и тд - изначально ссылочные типы, т.е., по сути, указатели. Поэтому в процедуру в качестве параметра передается не строка, объект и т.д, а типизированный указатель и именно к нему, к этому указателю, как параметру, и применяется модификатор const.
Понятно, что в таком случае, доступ только для чтения распространяется лишь на указатель, а не на содержимое экземпляра. Хотя я встречал реализации Паскаля, в которых конст честно обеспечивал доступ только для чтения для любых обращений к парамерту, как к lvalue.
Аватара пользователя
stikriz
энтузиаст
Сообщения: 612
Зарегистрирован: 15.03.2006 08:37:47

Сообщение stikriz »

Kemet писал(а): Хотя я встречал реализации Паскаля, в которых конст честно обеспечивал доступ только для чтения для любых обращений к парамерту, как к lvalue.

Для этого надо делать объекты на стеке.
Kemet
постоялец
Сообщения: 241
Зарегистрирован: 10.02.2010 18:28:32
Откуда: Временно оккупированная территория
Контактная информация:

Сообщение Kemet »

stikriz, зачем на стеке? Это же контроль времени компиляции. Нет никакой разницы, в стеке или в куче.
Аватара пользователя
stikriz
энтузиаст
Сообщения: 612
Зарегистрирован: 15.03.2006 08:37:47

Сообщение stikriz »

Потому, что я могу скопировать адрес в другой поинтер, или вычислить его. А пометить область памяти как только для чтения и поймать прерывание процессора - это железобетонно.
Kemet
постоялец
Сообщения: 241
Зарегистрирован: 10.02.2010 18:28:32
Откуда: Временно оккупированная территория
Контактная информация:

Сообщение Kemet »

stikriz, мы о разных вещах говорим. Я имею ввиду контроль именно во время компиляции и именно над параметром, коим в данном случае является ссылка на экземпляр - её изменить ( если она конст ) нельзя.
То, что мы можем скопировать - это другое дело, хотя и здесь что-то можно на этапе компиляции отследить, атам, где неоднозначность - варнинги. А то, о чем Вы пишете нужно отслеживать в рантайме использую какую-либо реализацию механизма охраны экземпляра.
alexey38
долгожитель
Сообщения: 1627
Зарегистрирован: 27.04.2011 19:42:31

Сообщение alexey38 »

Ask писал(а):Как видим, заблуждения о том, как работает const, действительно широко распространены :)
Господа, Я согласен с тем, что Ваше описание -- это хорошая модель для модификатора const.
К сожалению, на самом деле const работает не так.
Я потратил немало нажатий на кнопки, пытаясь убедить разработчиков FPC сделать, как Вы предлагаете --
прочтите хотя бы тот трэд, что Я привёл, а таких было несколько.
Разработчики отказались (и у них есть свои основания, хотя Я с ними и не согласен).
Как Я уже говорил:
1) const даёт компилятору выбор относительно способа передачи параметра (например, записи передаются по ссылке либо по значению в зависимости от SizeOf и платформы). Таким образом, при использовании const программист должен внимательно следить, чтобы семантика кода не зависела от способа передачи.
2) const даёт компилятору возможность предполагать, что обозначенная переменная не изменится *нигде в программе*, а не только непосредственно
в текущей процедуре. В частности, refcounted типы c модификатором const не делают incref/decref! Если вдруг значение переменной, переданной в качестве параметра по const, изменится, программа упадёт с *очень* трудно отлавливаемым AV в *эпилоге процедуры*, т.е. вообще не в пользовательском коде. Поскольку модифицирующий код может находиться в другом модуле, быть написанным другим программистом в другое время, и вообще относиться к другому уровню приложения, эту ошибку можно предотвратить только одним способом -- не использовать const c refcounted типами.

Отдельно хочу сказать, что "если нужна гарантированная ссылка, то пишем var" -- это стилистически неправильно, поскольку смешивает понятия способа передачи параметра и необходимости его модификации. Правильно будет "если нужна гарантированная ссылка, *и мы планируем изменять переменную*, то пишем var, иначе пишем constref.


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

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

Про ситуации типа "Если вдруг значение переменной, переданной в качестве параметра по const, изменится, программа упадёт с *очень* трудно отлавливаемым AV в *эпилоге процедуры*, т.е. вообще не в пользовательском коде." - это как раз все элементарно определяется, т.к. программа хотя бы падает.
Намного хуже, когда ошибка функциональная, что приводит не к падению, а к искажению результата. То есть декларация процедуры (метода) не соответствует фактическому функционалу. И здесь кроме ясности кода мало, что может помочь.

А насчет constref, то я так и не смог придумать реального примера, когда нужна именно ссылка, но менять мы ничего не будем. Кроме хаккерских целей ничего на ум не приходит. Ну разве что в многозадачной постановке, но тут без атомарных операций и блокирово все равно ничего не будет работать, так что назначение constref так и не понял.
Ask
постоялец
Сообщения: 163
Зарегистрирован: 25.12.2008 02:51:37

Сообщение Ask »

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

Ну, const на refcounted типах вызывает обращение к освобождёной области памяти,
так что там как повезёт -- обычно падает, но может и неправильный результат получить.

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

Код: Выделить всё

procedure MyCopy(out ADest: TMyBigRec; constref ASrc: TMyBigRec); begin if @ADest = @ASrc then exit; ...
Аватара пользователя
runewalsh
энтузиаст
Сообщения: 579
Зарегистрирован: 27.04.2010 00:15:25

Сообщение runewalsh »

А насчет constref, то я так и не смог придумать реального примера

Он нужен для совместимости с сишными либами.
Пример: http://newtondynamics.com/wiki/index.ph ... rldRayCast
p0 и p1 — указатели на трёхмерные векторы. Напрашивается вариант с var p0, p1: NewtonVec3. Но var-параметры нельзя вычислять на месте, а загромождать код не хочется. Constref просто сообщает компилятору, что ссылка на rvalue — фактически временную переменную на стеке — тоже подойдёт, и человеческий код вида

Код: Выделить всё

NewtonWorldRayCast(world, NewtonVec3(camera.pos), NewtonVec3(camera.pos + MaxDistance * camera.dir), ...).

становится валиден.
Ответить