Использовать процедурный тип при объявлении процедуры\функци

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

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

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение sts » 20.04.2017 23:32:43

так сделано не просто так, это заставляет заглянуть в каждую функцию и провести аудит.
sts
постоялец
 
Сообщения: 406
Зарегистрирован: 04.04.2008 12:15:44
Откуда: Тольятти

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение zub » 20.04.2017 23:41:01

>>так сделано не просто так, это заставляет заглянуть в каждую функцию и провести аудит.
Нифига не заставляет. Много раз ловил глюки потому что забывал в некоторых функциях скопипастить новые сигнатуры, а компилятор это пропускал. (емнип в режиме делфи параметры вообще не контролируются) Такчто это наоборот гарантия актуальности определения функциипроцедуры.

Добавлено спустя 1 минуту 47 секунд:
Re: Использовать процедурный тип при объявлении процедуры\функци
ElectroGuard
>>скриптовые интерпретаторы нужно какие-то искать. возможно есть что-то готовое
Это никак не связано с сутью топика
zub
долгожитель
 
Сообщения: 2884
Зарегистрирован: 14.11.2005 23:51:26

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение sts » 20.04.2017 23:50:05

zub писал(а):Нифига не заставляет. Много раз ловил глюки потому что забывал в некоторых функциях скопипастить новые сигнатуры, а компилятор это пропускал. (емнип в режиме делфи параметры вообще не контролируются) Такчто это наоборот гарантия актуальности определения функциипроцедуры.

а причем здесь кривой fpc? ТС хочет внести изменения в язык и я про язык.
sts
постоялец
 
Сообщения: 406
Зарегистрирован: 04.04.2008 12:15:44
Откуда: Тольятти

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение скалогрыз » 21.04.2017 00:32:26

zub писал(а):Имеем примерно такой код
....
Т.е. куча функций, та которую надо вызвать вычисляется в рантайме, и вызывается через переменную процедурного типа.
Зачем при объявлении 100500 функций надо писать полную декларацию?
...
Потом приспичивает добавить например arg2 и приходится 100500 раз копипастить.
какнибудь так было бы гораздо логичней:
...
т.е. при декларации использовать уже определенный процедурный тип. все изменения можно производить централизовано.
Есть мнения за или против?


А чё никто ООП не предлагает?!
вроде такого
Код: Выделить всё
type
  TCommand = class
    arg1 : integer;
    procedure Execute; virtual;
  end;

  TCommand1 = class(TCommand)
  ...
  TCommand100500 = class(TCommand)


при добавлении arg2 в TCommand, все наследники получают его автоматом... не?

P.S. а не, дож же и предложил во втором же посте.
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение zub » 21.04.2017 01:03:42

ООП есть, но конечно по другим причинам:
Код: Выделить всё
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
долгожитель
 
Сообщения: 2884
Зарегистрирован: 14.11.2005 23:51:26

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение скалогрыз » 21.04.2017 01:09:48

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 использовалась как заглушка, в том случае, если команда не найдена.
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение zub » 21.04.2017 08:25:16

>>but an ability to shoot yourself in a foot!
Качественно комментированный код редко встретишь))

В списке рассылки предложение завернули, впрочем как и ожидалось
zub
долгожитель
 
Сообщения: 2884
Зарегистрирован: 14.11.2005 23:51:26

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение скалогрыз » 21.04.2017 16:31:47

zub писал(а):В списке рассылки предложение завернули, впрочем как и ожидалось

в списке рассылки?
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение Mikhail » 21.04.2017 19:16:17

zub писал(а):В списке рассылки предложение завернули, впрочем как и ожидалось

И правильно сделали. :)
Mikhail
энтузиаст
 
Сообщения: 562
Зарегистрирован: 24.10.2013 16:06:47

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение zub » 21.04.2017 23:41:16

скалогрыз
>>в списке рассылки?
http://lists.freepascal.org/pipermail/f ... 50895.html

Mikhail
Внятное объяснение чем это чревато?
zub
долгожитель
 
Сообщения: 2884
Зарегистрирован: 14.11.2005 23:51:26

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение скалогрыз » 22.04.2017 01:23:10

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)
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение vitaly_l » 22.04.2017 08:56:03

скалогрыз писал(а):TMyCommand(cmd)(arg1,arg2)

Ну это точно неправильно. Менять: arg1 на arg1+arg2 <== это есть 100% быть плохо придумано.
В данной задаче, всегда можно и нужно обходиться только одним arg1 и тогда ничего и никогда не придётся копи/пастить. ИМХО

.
Аватара пользователя
vitaly_l
долгожитель
 
Сообщения: 3333
Зарегистрирован: 31.01.2012 16:41:41

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение zub » 22.04.2017 10:59:22

>>Но лично я бы пошёл по пути ООП, и пошёл бы я по нему, не после 100500й команды, а уже после 5й команды, т.е. пока не поздно )))
я наоборот после 5й команды понял что с классов надо слазить в пользу простых процедур\функций, классы это слишком многословно - надо объявить, создать, зарегестрировать... Но меня долго сдерживала событийная ориентированность, одно дело класс с методом
Код: Выделить всё
procedure MouseMove(...); virtual;
, совсем другое процедура с
Код: Выделить всё
get3dpoint
Переход от одного к другому совсем не так прост как кажется.

>>if isOldCommand then
У меня и так 3 типа команд (на самом деле больше :) ) такчто плодить еще типы - точно ненадо
zub
долгожитель
 
Сообщения: 2884
Зарегистрирован: 14.11.2005 23:51:26

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение olegy123 » 22.04.2017 13:10:39

zub писал(а):
Код: Выделить всё
type
  TCommand = class
    arg1 : integer;
    procedure CommandStart(...); virtual;
    procedure CommandEnd(...); virtual;
    procedure CommandCancel(...); virtual;
    procedure Execute(...); virtual;
    procedure MouseMove(...); virtual;
    ....
  end;

Мне почему то интуитивно показалось очень хоррерным.. не знаю почему, но чувство тяжелое.. Почему в базовой команде "MouseMove(...)" :shock:? А-а-а..

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

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

Добавлено спустя 19 минут 25 секунд:
Re: Использовать процедурный тип при объявлении процедуры\функци
Как выбирать будете какую из 100500 функцию выполнить?
olegy123
долгожитель
 
Сообщения: 1643
Зарегистрирован: 25.02.2016 12:10:20

Re: Использовать процедурный тип при объявлении процедуры\фу

Сообщение zub » 22.04.2017 13:31:17

>>В сообщении зашито работа? Это правильно?
С чего вы взяли что зашита? Может быть зашита, а может быть не зашита. Какие еще варианты получить координаты мышки при событийной организации UI?

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

Добавлено спустя 1 минуту 9 секунд:
Re: Использовать процедурный тип при объявлении процедуры\функци
>>Как выбирать будете какую из 100500 функцию выполнить?
скачайте зкад и глянте как это работает для пользователя
zub
долгожитель
 
Сообщения: 2884
Зарегистрирован: 14.11.2005 23:51:26

Пред.След.

Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 30

Рейтинг@Mail.ru