Использовать процедурный тип при объявлении процедуры\функци
Модератор: Модераторы
так сделано не просто так, это заставляет заглянуть в каждую функцию и провести аудит.
>>так сделано не просто так, это заставляет заглянуть в каждую функцию и провести аудит.
Нифига не заставляет. Много раз ловил глюки потому что забывал в некоторых функциях скопипастить новые сигнатуры, а компилятор это пропускал. (емнип в режиме делфи параметры вообще не контролируются) Такчто это наоборот гарантия актуальности определения функциипроцедуры.
Добавлено спустя 1 минуту 47 секунд:
Re: Использовать процедурный тип при объявлении процедуры\функци
ElectroGuard
>>скриптовые интерпретаторы нужно какие-то искать. возможно есть что-то готовое
Это никак не связано с сутью топика
Нифига не заставляет. Много раз ловил глюки потому что забывал в некоторых функциях скопипастить новые сигнатуры, а компилятор это пропускал. (емнип в режиме делфи параметры вообще не контролируются) Такчто это наоборот гарантия актуальности определения функциипроцедуры.
Добавлено спустя 1 минуту 47 секунд:
Re: Использовать процедурный тип при объявлении процедуры\функци
ElectroGuard
>>скриптовые интерпретаторы нужно какие-то искать. возможно есть что-то готовое
Это никак не связано с сутью топика
zub писал(а):Нифига не заставляет. Много раз ловил глюки потому что забывал в некоторых функциях скопипастить новые сигнатуры, а компилятор это пропускал. (емнип в режиме делфи параметры вообще не контролируются) Такчто это наоборот гарантия актуальности определения функциипроцедуры.
а причем здесь кривой fpc? ТС хочет внести изменения в язык и я про язык.
zub писал(а):Имеем примерно такой код
....
Т.е. куча функций, та которую надо вызвать вычисляется в рантайме, и вызывается через переменную процедурного типа.
Зачем при объявлении 100500 функций надо писать полную декларацию?
...
Потом приспичивает добавить например arg2 и приходится 100500 раз копипастить.
какнибудь так было бы гораздо логичней:
...
т.е. при декларации использовать уже определенный процедурный тип. все изменения можно производить централизовано.
Есть мнения за или против?
А чё никто ООП не предлагает?!
вроде такого
Код: Выделить всё
type
TCommand = class
arg1 : integer;
procedure Execute; virtual;
end;
TCommand1 = class(TCommand)
...
TCommand100500 = class(TCommand)
при добавлении arg2 в TCommand, все наследники получают его автоматом... не?
P.S. а не, дож же и предложил во втором же посте.
ООП есть, но конечно по другим причинам:
Т.е.там где у команд сложное поведение, или где применимо наследование - несколько чуток отличающихся команд (например move\copy).
Но в большинстве случаев вполне хватает простой процедуры, зачем усложнять на ровном месте?
Код: Выделить всё
type
TCommand = class
arg1 : integer;
procedure CommandStart(...); virtual;
procedure CommandEnd(...); virtual;
procedure CommandCancel(...); virtual;
procedure Execute(...); virtual;
procedure MouseMove(...); virtual;
....
end;
TCommand1 = class(TCommand)
...
TCommand100500 = class(TCommand)Т.е.там где у команд сложное поведение, или где применимо наследование - несколько чуток отличающихся команд (например move\copy).
Но в большинстве случаев вполне хватает простой процедуры, зачем усложнять на ровном месте?
zub писал(а):Но в большинстве случаев вполне хватает простой процедуры, зачем усложнять на ровном месте?
лёгкости поддержки ради.
(тот же пример с добавлением аргументов)
Добавлено спустя 5 часов 12 минут 20 секунд:
Re: Использовать процедурный тип при объявлении процедуры\функци
А вообще, переписывать всё на ООП тоже лень!
давайте по-jit-им чуток!
Код: Выделить всё
program project1;
{$mode delphi}{$H+}
uses
SysUtils, Classes
{ you can add units after this };
type
TMyCommandResult = (mcrOk, mcrFail);
TMyArg = integer;
TMyArg2 = string;
TMyCommandOld=function(arg1:TMyArg):TMyCommandResult;
TMyCommand=function(arg1: TMyArg; const arg2: TMyArg2): TMyCommandResult;
function command1(arg1:TMyArg):TMyCommandResult;
begin
writeln('command1 ',arg1);
Result:=mcrOk;
end;
function command2(arg1:TMyArg):TMyCommandResult;
begin
writeln('command2 ',arg1);
Result:=mcrOk;
end;
function command100500(arg1:TMyArg):TMyCommandResult;
begin
writeln('command1005000 ',arg1);
Result:=mcrOk;
end;
{$PUSH}
{$OPTIMIZATION OFF}
function commandArg1Dummy(arg1: TMyArg): TMyCommandResult;
begin
Result:=mcrOk;
end;
function commandArg2ToArg1(arg1: TMyArg; const arg2: TMyArg2): TMyCommandResult;
begin
Result:=commandArg1Dummy(arg1);
end;
function getArg2ToArg1CodeSize: PtrInt;
begin
Result:=abs(PtrUInt(@getArg2ToArg1CodeSize)-PtrUint(@commandArg2ToArg1));
end;
{$POP}
function commandOver9999(arg1:TMyArg; const arg2: TMyArg2):TMyCommandResult;
begin
writeln('Over9999 ');
writeln('arg1= ', arg1);
writeln('arg2= ', arg2);
Result:=mcrOk;
end;
function MakeCommandArg2(oldcommand: TMyCommandOld): TMyCommand;
var
sz : integer;
i : integer;
pb : PByte;
dst : PtrUint;
src : PtrUint;
fn : Pointer;
begin
Result:=nil;
dst:=PtrUInt(@oldcommand);
sz:=getArg2ToArg1CodeSize;
Getmem( fn, sz);
Move( Pointer(@commandArg2ToArg1)^, fn^, sz);
pb:=fn;
// a hack as is itself! works for i386 only
for i:=0 to sz-1 do begin
// this is presumably x86 "call" ... but an ability to shoot yourself in a foot!
if pb^=$e8 then begin
PtrUInt(pb):=PtrUInt(pb)+1;
src:=PtrUint(pb)+4;
PPtrInt(pb)^:=dst-src;
break;
end;
PtrUInt(pb):=PtrUInt(pb)+1
end;
Result:=TMyCommand(fn);
end;
var
//todo: replace with registry list
command1ptr : TMyCommand = nil;
command2ptr : TMyCommand = nil;
command100500ptr : TMyCommand = nil;
function GetNeededCommand(const cmdname: string): TMyCommand;
begin
if cmdname='command1' then begin
if not Assigned(command1ptr) then command1ptr:= MakeCommandArg2(command1);
Result:=command1ptr;
end else if cmdname='command2' then begin
if not Assigned(command2ptr) then command2ptr:= MakeCommandArg2(command2);
Result:=command2ptr;
end else if cmdname='command100500' then begin
if not Assigned(command100500ptr) then command100500ptr:= MakeCommandArg2(command100500);
Result:=command100500ptr;
end else if cmdname='commandOver9000' then begin
Result:=@commandOver9999;
end else
Result:=commandArg2ToArg1;
end;
var
com: TMyCommand;
res: TMyCommandResult;
nm : string;
begin
if ParamCount=0 then begin
writeln('no command specicifed, using default "command1"')
end else begin
nm:=ParamStr(1);
writeln('executing command: ', nm);
end;
com:=GetNeededCommand(nm);
res:=com(1,'test');
writeln(res);
end.
какие проблемы в таком решении:
* самомодифицирующийся код - в данном случае, это не такое большое зло, т.к. просто напросто создаётся враппер, но тем не менее.
* платформенная привязка. придётся подпиливать под каждую новую систему
* при каждом случае изменения параметров TMyCommand, придётся писать дополнительные враперы к предыдущим. Но по времени, это быстрее чем делать 100500 рисковых замен?!
ЗЫ: кстати будет уместно заявить, что в будущем RTTI планируется invoke()
ЗЗЫ: кстати делать через переопредление call совершенно необязательно.
можно пойти проще, функция вида:
Код: Выделить всё
function commandArg2ToArg1(arg1: TMyArg; const arg2: TMyArg2): TMyCommandResult;
var
p : TMyCommandOld;
begin
p := nil; //да, господа!
Result:=p(arg);
end;
даёт вот такой вот листинг
Код: Выделить всё
00401660 <P$PROJECT1_$$_COMMANDARG2TOARG1$LONGINT$ANSISTRING$$TMYCOMMANDRESULT>:
401660: 55 push %ebp
401661: 89 e5 mov %esp,%ebp
401663: 8d 64 24 f0 lea -0x10(%esp),%esp
401667: 89 45 fc mov %eax,-0x4(%ebp)
40166a: 89 55 f8 mov %edx,-0x8(%ebp)
40166d: c7 45 f0 00 00 00 00 movl $0x0,-0x10(%ebp)
401674: 8b 45 fc mov -0x4(%ebp),%eax
401677: ff 55 f0 call *-0x10(%ebp)
40167a: 88 45 f4 mov %al,-0xc(%ebp)
40167d: 8a 45 f4 mov -0xc(%ebp),%al
401680: c9 leave
401681: c3 ret
очевидно, что c7 45 f0 00 00 00 00
меняется на
c7 45 f0 ку да на до
и готово! (функция врапер создаётся за счёт присвоения одного PtrUInt - положение же статично).
но я пошёл долгим путём, чтобы commandArg2ToArg1 использовалась как заглушка, в том случае, если команда не найдена.
>>but an ability to shoot yourself in a foot!
Качественно комментированный код редко встретишь))
В списке рассылки предложение завернули, впрочем как и ожидалось
Качественно комментированный код редко встретишь))
В списке рассылки предложение завернули, впрочем как и ожидалось
zub писал(а):В списке рассылки предложение завернули, впрочем как и ожидалось
в списке рассылки?
zub писал(а):В списке рассылки предложение завернули, впрочем как и ожидалось
И правильно сделали.
скалогрыз
>>в списке рассылки?
http://lists.freepascal.org/pipermail/f ... 50895.html
Mikhail
Внятное объяснение чем это чревато?
>>в списке рассылки?
http://lists.freepascal.org/pipermail/f ... 50895.html
Mikhail
Внятное объяснение чем это чревато?
zub писал(а):http://lists.freepascal.org/pipermail/fpc-pascal/2017-April/050895.html
понятно.
Но лично я бы пошёл по пути ООП, и пошёл бы я по нему, не после 100500й команды, а уже после 5й команды, т.е. пока не поздно )))
(рекорд TArguments не стал бы использовать, ибо недостаточно гибко)
на 173 команде, я бы наверное уже пошёл бы по пути обёртки (как написал выше).
Потому что писать 173 класса нифига не улыбается.
Но может быть поменять способ вызова команды
Код: Выделить всё
cmd:=GetCommand;
if isOldCommand then
TMyOldCommand(cmd)(arg1)
else
TMyCommand(cmd)(arg1,arg2)
скалогрыз писал(а):TMyCommand(cmd)(arg1,arg2)
Ну это точно неправильно. Менять: arg1 на arg1+arg2 <== это есть 100% быть плохо придумано.
В данной задаче, всегда можно и нужно обходиться только одним arg1 и тогда ничего и никогда не придётся копи/пастить. ИМХО
.
>>Но лично я бы пошёл по пути ООП, и пошёл бы я по нему, не после 100500й команды, а уже после 5й команды, т.е. пока не поздно )))
я наоборот после 5й команды понял что с классов надо слазить в пользу простых процедур\функций, классы это слишком многословно - надо объявить, создать, зарегестрировать... Но меня долго сдерживала событийная ориентированность, одно дело класс с методом, совсем другое процедура с Переход от одного к другому совсем не так прост как кажется.
>>if isOldCommand then
У меня и так 3 типа команд (на самом деле больше
) такчто плодить еще типы - точно ненадо
я наоборот после 5й команды понял что с классов надо слазить в пользу простых процедур\функций, классы это слишком многословно - надо объявить, создать, зарегестрировать... Но меня долго сдерживала событийная ориентированность, одно дело класс с методом
Код: Выделить всё
procedure MouseMove(...); virtual;Код: Выделить всё
get3dpoint>>if isOldCommand then
У меня и так 3 типа команд (на самом деле больше
zub писал(а):Код: Выделить всё
type
TCommand = class
arg1 : integer;
procedure CommandStart(...); virtual;
procedure CommandEnd(...); virtual;
procedure CommandCancel(...); virtual;
procedure Execute(...); virtual;
procedure MouseMove(...); virtual;
....
end;
Мне почему то интуитивно показалось очень хоррерным.. не знаю почему, но чувство тяжелое.. Почему в базовой команде "MouseMove(...)"
Я могу точно сказать, что проще из простого сделать сложное.. а из сложного простое не возможно..
zub писал(а):я наоборот после 5й команды понял что с классов надо слазить в пользу простых процедурфункций, классы это слишком многословно - надо объявить, создать, зарегестрировать... Но меня долго сдерживала событийная ориентированность, одно дело класс с методом
Подразумеваю, что вы собрали модель-монстр и начали работать с ней, и пришли к тому что нужно много-много процедурфункций на все случаи жизни..
Добавлено спустя 19 минут 25 секунд:
Re: Использовать процедурный тип при объявлении процедуры\функци
Как выбирать будете какую из 100500 функцию выполнить?
>>В сообщении зашито работа? Это правильно?
С чего вы взяли что зашита? Может быть зашита, а может быть не зашита. Какие еще варианты получить координаты мышки при событийной организации UI?
>>Подразумеваю, что вы собрали модель-монстр и начали работать с ней, и пришли к тому что нужно много-много процедурфункций на все случаи жизни..
Монстр - не монстр, но всё довольно сложно. И проще наврятли получится сделать - команды могут быть "мгновенными" (например стереть всё - отработала и сразу завершилась) а могут быть интерактивными (запрашивая у пользователя кучу точек и рисуя чтонить в соответствии с движениями мыши), могут выпонятся одна поверх другой... куча тонкостей которые нужно учесть
Добавлено спустя 1 минуту 9 секунд:
Re: Использовать процедурный тип при объявлении процедуры\функци
>>Как выбирать будете какую из 100500 функцию выполнить?
скачайте зкад и глянте как это работает для пользователя
С чего вы взяли что зашита? Может быть зашита, а может быть не зашита. Какие еще варианты получить координаты мышки при событийной организации UI?
>>Подразумеваю, что вы собрали модель-монстр и начали работать с ней, и пришли к тому что нужно много-много процедурфункций на все случаи жизни..
Монстр - не монстр, но всё довольно сложно. И проще наврятли получится сделать - команды могут быть "мгновенными" (например стереть всё - отработала и сразу завершилась) а могут быть интерактивными (запрашивая у пользователя кучу точек и рисуя чтонить в соответствии с движениями мыши), могут выпонятся одна поверх другой... куча тонкостей которые нужно учесть
Добавлено спустя 1 минуту 9 секунд:
Re: Использовать процедурный тип при объявлении процедуры\функци
>>Как выбирать будете какую из 100500 функцию выполнить?
скачайте зкад и глянте как это работает для пользователя
