Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Динамическая генерация процедур


Автор: Rohoss 19.8.2008, 20:09
Есть класс с множеством методов
Код

{$M+}
 TCommands = class
   function cCopyFile(OldFile,NewFile:string):Boolean;
   function cMoveRenameFile(OldFile,NewFile:string):Boolean;
   function cDeleteFile(cFile:string):Boolean;
   ..........

Мне нужно вызывать разные методы этого класса, типа как здесь
Код

type
Tnte=procedure(s:string) of object;
var
Method:TMethod;
nte:Tnte;
begin
Method.Data:=tc;
Method.Code:=tc.MethodAddress(' cCopyFile');
if Method.Code = nil then Exit;
nte:=Tnte(Method);
nte('test');
end;


только здесь у процедуры один параметр, а мне нужно чтобы количество параметров менялось. Для того чтобы этим процедурным типом можно было вызвать любой метод класса TCommands

Надеюсь у меня получилось правильно выразить мысль  smile 

Автор: Rohoss 20.8.2008, 02:19
Возможно ли такое вообще?

Автор: Rennigth 20.8.2008, 10:56
Цитата(Rohoss @  20.8.2008,  02:19 Найти цитируемый пост)
Возможно ли такое вообще? 

хм, ну в принципе как Tnte объявишь, так и вызывай... в чем проблемма то?

Автор: Rohoss 20.8.2008, 21:08
Так функции имеют разное количество параметров, причём не известно, сколько их будет (параметров).

Добавлено через 1 минуту и 45 секунд
Нужно как-то Tnte объявить как функцию с динамическим количеством параметров.

Добавлено через 7 минут и 14 секунд
Можно было параметром использовать динамический массив, но класс TCommands уже определён

Автор: Zmiy 20.8.2008, 22:05
Rohoss, все параметры - строки?

Добавлено через 10 минут и 58 секунд
Не знаю, попробуй
Код

tnde = procedure (const a: array of String) of object;

или
Код

tnde = procedure (const a: array of const) of object;


Автор: ama_kid 20.8.2008, 22:28
Цитата(Rohoss @  20.8.2008,  22:08 Найти цитируемый пост)
Нужно как-то Tnte объявить как функцию с динамическим количеством параметров.
Сделать-то это не мудрено:
Код
procedure Tnte(Caption:string; Args:array of const);
var
 s:string;
 i:integer;
begin
 s:='Parameters: ';
 for i:=0 to High(Args) do
  case Args[i].VType of
   vtInteger: s:=s+IntToStr(Args[i].VInteger)+', ';
   vtAnsiString:  s:=s+string(PChar(Args[i].VString))+', ';
   vtBoolean: s:=s+BoolToStr(Args[i].VBoolean)+', ';
   vtExtended: s:=s+FloatToStr(PExtended(Args[i].VExtended)^);
  end;
 MessageBox(0,PAnsiChar(s),PAnsiChar(Caption),0);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 DecimalSeparator:='.';
 Tnte('Caption',[1,true,'word']);
 Tnte('Caption',[false,'byte',-5438,4.65]);
end;
Только имхо ты пытаешься решать задачу с неопределенностью. Мне кажется проще убрать неопределенность...

Автор: Rohoss 20.8.2008, 22:52
Цитата(Zmiy @  20.8.2008,  22:05 Найти цитируемый пост)
Rohoss, все параметры - строки?

Да

Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    btn1: TButton;
    procedure btn1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

{$M+}
 TCommands = class
   function cCopyFile(OldFile,NewFile:string):Boolean;
   function cDeleteFile(cFile:string):Boolean;
 end;


var
  Form1: TForm1;
  tc:TCommands;

implementation

{$R *.dfm}

procedure TForm1.btn1Click(Sender: TObject);
type
ars=array of string;
tnte = function(const a: ars):Boolean of object;
var
Method:TMethod;
nte:Tnte;
c:ars;
begin
SetLength(c,2);
c[0]:='test';
c[1]:='dff';
Method.Data:=tc;
Method.Code:=tc.MethodAddress('cCopyFile');
if Method.Code = nil then Exit;
nte:=Tnte(Method);
nte(c);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
tc:=TCommands.Create;
end;

function TCommands.cCopyFile(OldFile: string; NewFile: string):Boolean;
begin
 ShowMessage(OldFile+'!copy!'+NewFile);
 Result:=True;
end;

function TCommands.cDeleteFile(cFile: string):Boolean;
begin
 ShowMessage('!Delete!'+cFile);
 Result:=True;
end;

end.


Сделал типо теста, сообщения выходят бредовые… 

Автор: Rohoss 20.8.2008, 23:16
ama_kid немного не то… Тип должен использоватся для функции с разным количеством параметрами, а у тебя параметр массив… 

Автор: ama_kid 21.8.2008, 07:08
Цитата(Rohoss @  21.8.2008,  00:16 Найти цитируемый пост)
Тип должен использоватся для функции с разным количеством параметрами, а у тебя параметр массив… 
Этот массив параметрами может содержать переменные любых типов в любом количестве и любом порядке smile :
Код
procedure TForm1.Button1Click(Sender: TObject);
var
 s:string;
 r:real;
 a:integer;
 b:boolean;
begin
 DecimalSeparator:='.';
 s:='string';
 r:=3.24;
 a:=-5322;
 b:=true;
 Tnte('Caption',[r,s,a,b]);
 Tnte('Caption',[a,b,r]);
end;


Автор: Felan 21.8.2008, 08:39
Тебе придется делать метод с переменным числом аргументов, потом проверять типы и приводить типы аргументов. Без этого не как. Есть другой вариант,
если есть возможность переделать архитектуру классов, то смотри в сторону паттерна проектирования Command. И, если неизвестно изначально, какие команды могут быть, смотри виртуальные конструкторы.

Общая суть такая:

Код

unit Unit3;

interface

type
  TCommand = class;

  //Мета-класс
  TCommandClass = class of TCommand;

  //Базовый класс команды
  TCommand = class
    private
      fLabel: String;
    public
      constructor Create(); virtual;
      procedure Execute(); virtual; abstract; //Метод выполняющий команду
      procedure Tune(); virtual; abstract; //Этот метод используется для того, что бы команды, еще не известные на этапе проектирования могли самонастраиваться!
  end;

  //Отдельная команда 1
  TCommand1 = class(TCommand)
    public
      Arg1: Integer;
      procedure Execute(); override;
      procedure Tune(); override;
  end;

  //Отдельная команда 2
  TCommand2 = class(TCommand)
    public
      Arg1: String;
      procedure Execute(); override;
      procedure Tune(); override;
  end;

  //Отдельная команда 3
  TCommand3 = class(TCommand)
    public
      Arg1: String;
      Arg2: Integer;
      procedure Execute(); override;
      procedure Tune(); override;
  end;

//Процедура демонстрирующая ипользование
procedure ExecuteCommands;

//Функция создающая неизвестную команду через вирт. конструктор, т.к. неизвестно, какие у нее могут быть параметры
function CreateUnknownCMD(cmdClass: TCommandClass): TCommand;

implementation

uses
  Dialogs, SysUtils;

procedure ExecuteCommands;
var
  vCmd: TCommand;
begin
  vCmd := TCommand1.Create();
  try
    With (vCmd as TCommand1) do
      begin
        Arg1 := 10;
      end;
    vCmd.Execute();
  finally
    FreeAndNil( vCmd );
  end;

  vCmd := TCommand2.Create();
  try
    With (vCmd as TCommand2) do
      begin
        Arg1 := 'Строка';
      end;
    vCmd.Execute();
  finally
    FreeAndNil( vCmd );
  end;

  vCmd := CreateUnknownCMD(TCommand3);
  try
    vCmd.Execute();
  finally
    FreeAndNil( vCmd );
  end;
end;

function CreateUnknownCMD(cmdClass: TCommandClass): TCommand;
var
  vCmd: TCommand;
begin
  vCmd := cmdClass.Create();
  vCmd.Tune(); //Настройка неизвестной команды, которая сама знает как настраиваться
  Result := vCmd;
end;

{ TCommand }

constructor TCommand.Create;
begin
  fLabel := ClassName;
end;

{ TCommand1 }

procedure TCommand1.Execute;
begin
  ShowMessage(Format('Аргумент команды %d', [Arg1]));
end;

procedure TCommand1.Tune;
begin
  Arg1 := 0; //Или исключение, если эта команда не должна вызвываться как неизвестная!
end;

{ TCommand2 }

procedure TCommand2.Execute;
begin
  ShowMessage(Format('Аргумент команды "%s"', [Arg1]));
end;

procedure TCommand2.Tune;
begin
  Arg1 := ''; //Или исключение, если эта команда не должна вызвываться как неизвестная!
end;

{ TCommand3 }

procedure TCommand3.Execute;
begin
  ShowMessage(Format('Аргументы команды "%s", %d', [Arg1, Arg2]));
end;

procedure TCommand3.Tune;
begin
  //Здесь можно вызывать отдельный диалог, который будет настраиывать команду.
  Arg1 := 'Автонастройка команды';
  Arg2 := 999;
end;

end.



Тест кода:

Код

procedure TForm2.Button1Click(Sender: TObject);
begin
  ExecuteCommands();
end;



Автор: Rohoss 21.8.2008, 21:39
ama_kid либо я не понял твоего примера, либо ты не понял что я хочу…
Есть, к примеру, текстовый файл fun.txt
Код

start C:\1.txt
delete C:\temp.tmp
сopy C:\1.txt&D:\2.txt

приложения должно читать каждую строчку этого файла, вызывать метод класса TCommands и передавать этому методу параметрами то что идёт после пробела. «&» - разделитель между параметрами. Команды класса TCommands будут добавляться, а некоторые будут экспортироваться с dll, поэтому заранее определить не получится.

Добавлено через 10 минут и 56 секунд
Felan в твоём примере запутался, разберусь и отпишусь чуть позже… smile 

Автор: THandle 21.8.2008, 21:54
Rohoss, может и глупость сейчас говорю... Но что если сделать тип TNte с максимальным для методов класса TCommands параметров?

То есть например:

Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

{$M+}
 TCommands = class
   function cCopyFile(OldFile, NewFile : string) : Boolean;
   function cDeleteFile(cFile : string) : Boolean;
 end;

  TNte = function(Str, Str2 : string) : Boolean of object;

var
  Form1: TForm1;
  tc : TCommands;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  Method : TMethod;
  Nte : TNte;
begin
  Method.Data := tc;
  Method.Code := tc.MethodAddress('cDeleteFile');
  if Method.Code = nil then Exit;
  Nte:=Tnte(Method);
  Nte('AAA', '');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  tc:=TCommands.Create;
end;

function TCommands.cCopyFile(OldFile: string; NewFile: string):Boolean;
begin
  ShowMessage(OldFile+'!copy!'+NewFile);
  Result:=True;
end;
function TCommands.cDeleteFile(cFile: string):Boolean;
begin
  ShowMessage('!Delete!'+cFile);
  Result:=True;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  Method : TMethod;
  Nte : TNte;
begin
  Method.Data := tc;
  Method.Code := tc.MethodAddress('cCopyFile');
  if Method.Code = nil then Exit;
  Nte:=Tnte(Method);
  Nte('AAA', 'asss');
end;

end.

Автор: Rohoss 21.8.2008, 22:00
Felan для каждой команды создавать свой класс? smile

Добавлено через 4 минуты и 38 секунд
THandle там точно есть команда с больше чем десятью параметрами… это не весь код класса TCommands. Пока единственный способ вижу изменять класс TCommands (сделать параметром динамический массив), но этого очень не хотелось.

Автор: Felan 22.8.2008, 07:25
Цитата(Rohoss @  21.8.2008,  23:39 Найти цитируемый пост)
Felan в твоём примере запутался, разберусь и отпишусь чуть позже…

Почитай про паттерны проектирования. Это паттерн "Команда" и все станет понятно. smile

Цитата(Rohoss @  22.8.2008,  00:00 Найти цитируемый пост)
Felan для каждой команды создавать свой класс? smile

Ну и что? В чем проблема-то? Зато каждая команда будет полностью самостоятельной единицей, которую ты сможешь править независимо ни от чего, и все изменения будут локализованы.
Лучше что-ли когда у тебя класс с мильоном методов, да еще не дай бог, перегруженных? Да с переменными аргументами? Да тип у этих аргументов общий, который нужно приводить на месте к нужному типу? Да за порядком этих аргументов нужно тщательно следить, за безопасностью типов тоже... за их правильным приведением... Чуть что, все надо перелопачивать... опухнуть можно smile

Цитата(Rohoss @  21.8.2008,  23:39 Найти цитируемый пост)
Есть, к примеру, текстовый файл fun.txt
Код

start C:\1.txt
delete C:\temp.tmp
сopy C:\1.txt&D:\2.txt

приложения должно читать каждую строчку этого файла, вызывать метод класса TCommands и передавать этому методу параметрами то что идёт после пробела. «&» - разделитель между параметрами. Команды класса TCommands будут добавляться, а некоторые будут экспортироваться с dll, поэтому заранее определить не получится.


И вот это как раз реализуется отлично. В моем примере добавляешь абстрактный метод Parse, в каждой команде реализуешь специфичный для нее парсинг строки. Динамически, создаешь нужную команду и выполняешь ее.... Все работает.
В принципе, я тебе даже показал способ как можно команды без перекомпиляции добавлять... Только нужно будет их хранить в длл, и использовать паккаджи для общих классов.

ЗЫЖ Логика такая. Есть базвый класс команды. Он используется для полиморфного выполнения и передачи объекта !ЛЮБОЙ! команды, которая унаследована от него (тут правда можно еще интерфейс использовать, но тогда не получит использовать виртуальные конструкторы напрямую). Каждая команда предаставляет собой класс-наследник базовой команды, которые реализует специфичный для себя функционал и инкапсулирует в себе все вспомогательные действия типа парсинга строки команды.... Все команды выполняются единообразно, после того как создается и настраивается класс команды. Их даже хранить не надо, создал команду, когда понадобилась, выполнил ее, уничтожил... 

Автор: Rohoss 22.8.2008, 18:10
Felan, вопрос в том, чтобы не переопределять класс TCommands

Добавлено через 12 минут и 8 секунд
А если уже без переопределения класса никак, то чем твой метод лучше от использования динамического массива?
1.    Не нужно процедуру парсинга писать для каждой команды.
2.    Получится более компактно.
3.    Можно использовать общие поля класса для всех команд

Автор: Felan 23.8.2008, 11:48
Цитата(Rohoss @  22.8.2008,  20:10 Найти цитируемый пост)
Felan, вопрос в том, чтобы не переопределять класс TCommands

Ну тогда конечно такой способ не подойдет. Ибо я сразу сказал, "если есть возможность переделать архитектуру".

Цитата(Rohoss @  22.8.2008,  20:10 Найти цитируемый пост)
А если уже без переопределения класса никак, то чем твой метод лучше от использования динамического массива?

Тем, что это классика ООП. Следовательно проверена не раз на практике. Тем что он понятный, прозрачный, типо-безопасный, расширяемый. Ессно программист использующий его должен иметь определенный уровень, что бы четко понимать ООП, виртуальные конструкторы делфи. (не принимай на свой счет, просто это факт. Что бы с чем-то разобраться и понять нужны определенные знания и опыт).


Цитата(Rohoss @  22.8.2008,  20:10 Найти цитируемый пост)
1.    Не нужно процедуру парсинга писать для каждой команды.

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

Цитата(Rohoss @  22.8.2008,  20:10 Найти цитируемый пост)
2.    Получится более компактно.

Цель разработчика сделать код понятным, простым, устойчивым, что бы можно было его легко поддерживать исправлять. А компактность это не показатель. Можно все в одну строку написать без пробелов, будет еще компактнее. Как будешь читать модуль в 1000 символов?

С такой компактностью потеряешь все преимущества, про которые я говорил в предидущем пункте + к ним добавишь проблемы с ручным контролем типов.

Цитата(Rohoss @  22.8.2008,  20:10 Найти цитируемый пост)
3.    Можно использовать общие поля класса для всех команд

Вот это просто ОШИБКА проектирования! Пройдет пару месяцев и для того, что бы исправить одну команду будешь неделю проверять не затронуло ли это другую.
Зачем смешивать данные? Ты что для контроллера пишешь? Тебе принципиально сэкономить несколько байт?


Это конечно все не критично, если пишешь проект с тремя командами, который один раз отдашь кому-то и забудешь, а если у тебя будет много команд, тебе придется его поддерживать и расширять, исправлять ошибки, все это хлебнешь по полной smile

Ну а если тебе принципиально нельзя менять архитектуру, то выбирай из того, что тебе уже сказали. Или можешь все-таки сделать метод, который выбирает команду например таким:

Код

procedure (arg: TArgument) ofbject;


Где TArgument базой класс типа TCommand в моем варианте, а от него наследника для каждой команды. Тогда по типу класса сможешь определить точно, какую команду нужно вызвать а в полях объекта передать нужные данные.

Автор: Rohoss 23.8.2008, 14:36
Цитата(Felan @  23.8.2008,  11:48 Найти цитируемый пост)
Цитата(Rohoss @  22.8.2008,  20:10 )1.    Не нужно процедуру парсинга писать для каждой команды.Плохо. Т.к. будет одна процедура для всех команд. Команд много - процедура становится огромной, трудной для восприятия и понимания за счет объема. Для расширения списка команд обязательно требуется перекомпиляция и очень внимательное и осторожное добавление команды. Пройдет время и ты не сможешь изменить ее так, что бы не потратить кучу времени на отладку.В моем случае у каждой команды своя маленькая процедурка, ошибки в которой влияют только на эту команду. Проще поддерживать, отлаживать, ошибки локализуются.


Ну вот функция:
Код

TArrayParam = array of string;

function cParse(sCommandsParam:string):TArrayParam;
var
I:Integer;
ArraySize:Word;
sTemp:string;
n:Integer;
begin
ArraySize:=1;
SetLength(Result,ArraySize);
if Length(sCommandsParam)=0 then
begin
  Result[0]:='';
  Exit;
end;
n:=Pos('&',sCommandsParam);
if n=0 then n:=Length(sCommandsParam)else n:=n-1;
Result[0]:=Copy(sCommandsParam,0,n);
ShowMessage(Result[0]);
 for I := 0 to Length(sCommandsParam) do
  begin
   if sCommandsParam[I]='&' then
    begin
      sTemp:=Copy(sCommandsParam,I+1,Length(sCommandsParam)-I);
      n:=Pos('&',sTemp);
      if n=0 then n:=Length(sTemp) else n:=n-1;
      sTemp:=Copy(sTemp,0,n);
      Inc(ArraySize);
      SetLength(Result,ArraySize);
      Result[I]:=sTemp;
    end;
  end;
end;


Раз написал и забыл о парсинге… smile

Добавлено @ 14:44
А так выглядят функции класса TCommands
Код

function cCopyFile(ArsParam:TArrayParam):Boolean;
function cMoveRenameFile(ArsParam:TArrayParam):Boolean;

Автор: Rohoss 23.8.2008, 15:00
Цитата(Felan @  23.8.2008,  11:48 Найти цитируемый пост)
Цитата(Rohoss @  22.8.2008,  20:10 )3.    Можно использовать общие поля класса для всех командВот это просто ОШИБКА проектирования! Пройдет пару месяцев и для того, что бы исправить одну команду будешь неделю проверять не затронуло ли это другую.Зачем смешивать данные? Ты что для контроллера пишешь? Тебе принципиально сэкономить несколько байт?

А почему они должны смешиваться? К примеру полем является класс для ведения логов
Код

fLog:TLogger;

у него функции типа
Код

function AddText(s:string):Boolean;

Почему они будут смешиваться? В принципе они независимы, разница в том, что этот класс создаётся один раз, а не для каждой команды. Каждый байт конечно не важен, но ИМХО это не оправдано… smile 

Автор: Rohoss 23.8.2008, 15:18
Цитата(Felan @  23.8.2008,  11:48 Найти цитируемый пост)
Ну а если тебе принципиально нельзя менять архитектуру, то выбирай из того, что тебе уже сказали. Или можешь все-таки сделать метод, который выбирает команду например таким:код Pascal/Delphi1:procedure (arg: TArgument) ofbject;highlightSyntax('delphi_YWQ0Mz','delphi');Где TArgument базой класс типа TCommand в моем варианте, а от него наследника для каждой команды. Тогда по типу класса сможешь определить точно, какую команду нужно вызвать а в полях объекта передать нужные данные.


ну, здесь я так понимаю опять же нужно переопределять класс… smile 
Дело в том, что TCommands писал не я, и в нём 186 команд, общей длиной 11700 строк  smile 


Автор: Felan 23.8.2008, 16:45
Цитата(Rohoss @  23.8.2008,  16:36 Найти цитируемый пост)
Раз написал и забыл о парсинге…

Ну если так то хорошо. А если придется добавлять еще одну команду, которую надо будет парсить по другому?

Цитата(Rohoss @  23.8.2008,  17:00 Найти цитируемый пост)
Почему они будут смешиваться? В принципе они независимы, разница в том, что этот класс создаётся один раз, а не для каждой команды. Каждый байт конечно не важен, но ИМХО это не оправдано… smile 

Ну возможно я не совсем понял, что ты имеешь ввиду под "общими полями для всех команд".
Создашь один раз и он будет у тебя висеть все время, пока работает программа... В моем случае команда существует пока выполняется.

Цитата(Rohoss @  23.8.2008,  17:18 Найти цитируемый пост)
ну, здесь я так понимаю опять же нужно переопределять класс… smile 


Нет. Здесь как раз его не надо переопределять. Здесь ты делаешь обвязку для этого класса, которая изолирует его.

Цитата(Rohoss @  23.8.2008,  17:18 Найти цитируемый пост)
Дело в том, что TCommands писал не я, и в нём 186 команд, общей длиной 11700 строк  smile 

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

ЗЫЖ Количество классов одно, количество объектов - другое. Я не настаиваю на своем способе и не говорю, что твой не правильный. Я просто привел другой вариант, который на мой взгляд более правильный. Но это только на мой взгляд smile

Автор: Rohoss 23.8.2008, 17:34
Цитата(Felan @  23.8.2008,  16:45 Найти цитируемый пост)
Цитата(Rohoss @  23.8.2008,  17:18 )ну, здесь я так понимаю опять же нужно переопределять класс… smile Нет. Здесь как раз его не надо переопределять. Здесь ты делаешь обвязку для этого класса, которая изолирует его.

а можешь привести маленький пример?

Автор: Felan 25.8.2008, 08:00
Ну я даже не знаю, Как тут его привести...

Ну например так:
Код

unit Unit4;

interface

uses
  Classes;

type
  //Твой класс, как есть
 TCommands = class
   function cCopyFile(OldFile, NewFile : string) : Boolean;
   function cDeleteFile(cFile : string) : Boolean;
 end;

 //Базовый класс, для унифицированного обращения
 TArguments = class
 public
  //метод который парсит строку с командой, если здесь ошибка,
  //то значит строка не соответствует предпологаемой команде
  //Сюда можно вынести код для парсинга однотипных команд, который пбудет просто
  //наследоваться, если таких команд много
  procedure TryParseString(aCMDString: String); virtual; abstract;
  //Выполняем команду. Каждый подкласс этого класса жестко обращается к нужной
  //команде
  procedure Execute(aOldCommands: TCommands); virtual; abstract;
 end;

 //конкретный подкласс для конкретной команды.
 //Парсинг и выполнение инкапсулированы в нем.
 TArguments_CopyFile = class(TArguments)
 private
  fOldFile: String;
  fNewFile: String;
 public
  procedure TryParseString(aCMDString: String); override;
  procedure Execute(aOldCommands: TCommands); override;
 end;

 //Объект-парсер, который обрабатывает файл
 TParser = class
   private
    fCMD: array of TArguments;
    function getObj(Ind: Integer): TArguments;
   public
    //Парсим команды и заполняем массив команд, к каждую команду в котором можно
    //выполнить универсально.
    procedure Parse(aStrs: TStringList);
    property CMDs[Ind: Integer]: TArguments read getObj; default;
 end;

implementation

{ TCommands }

function TCommands.cCopyFile(OldFile, NewFile: string): Boolean;
begin
  //Copy File
end;

function TCommands.cDeleteFile(cFile: string): Boolean;
begin
  //Delete File
end;

{ TParser }

function TParser.getObj(Ind: Integer): TArguments;
begin
  Result := fCMD[Ind];
end;

procedure TParser.Parse(aStrs: TStringList);
begin
  //Парсим команды и заполняем массив команд;
end;

{ TArguments_CopyFile }

procedure TArguments_CopyFile.Execute(aOldCommands: TCommands);
begin
  aOldCommands.cCopyFile(fOldFile, fNewFile);
end;

procedure TArguments_CopyFile.TryParseString(aCMDString: String);
begin
  //Парсим строку и заполняем параметры
end;

end.



ЗЫЖ Но учти, что это обертка smile Так что естественно некоторая "слоеность" логики smile

Автор: CodeMonkey 25.8.2008, 11:19
Пробежал тему по-диагонали.
Вопрос: есть имя метода, скажем, 'XXX'. Есть ли возможность узнать, сколько у него параметров в классе? Т.е. по имени определить число параметров? 
Если да, то я не вижу проблемы. Можно сделать, например, как-то так:

Код
type
Tnte1=procedure(s1:string) of object;
Tnte2=procedure(s1,s2:string) of object;
Tnte3=procedure(s1,s2,s3:string) of object;
Tnte4=procedure(s1,s2,s3,s4:string) of object;
...
Tnte20=procedure(s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,s14,s15,s16,s17,s18,s19,s20:string) of object;
begin
  ...
  case NomberOfArguments(Method) of
    1: Tnte1(Method)(S[0]);
    2: Tnte1(Method)(S[0], S[1]);
    3: Tnte1(Method)(S[0], S[1], S[2]);
    4: Tnte1(Method)(S[0], S[1], S[2], S[3]);
  ...
    20: Tnte1(Method)(S[0], S[1], S[2], S[3], S[4], S[5], ...);
  else
    raise ...;
  end;
end;


Не нравится объявление 20-ти типов? Можно вызвать процедуру и без них, если код вызова записать на ассемблере. Первые три аргумента пойдут по регистрам, остальные - циклом до числа параметров пихаются в стек, после чего делаем call.

P.S. Что будет, если число параметров у метода и число строк, которое мы напарсили, отличается?

Автор: Rohoss 25.8.2008, 21:20
Цитата(CodeMonkey @  25.8.2008,  11:19 Найти цитируемый пост)
Вопрос: есть имя метода, скажем, 'XXX'. Есть ли возможность узнать, сколько у него параметров в классе? 

Ты имел ввиду в функции? Хз, думаю как-то можно…

Цитата(CodeMonkey @  25.8.2008,  11:19 Найти цитируемый пост)
Не нравится объявление 20-ти типов? Можно вызвать процедуру и без них, если код вызова записать на ассемблере.

В ассемблере я полный ноль… smile 

Цитата(CodeMonkey @  25.8.2008,  11:19 Найти цитируемый пост)
P.S. Что будет, если число параметров у метода и число строк, которое мы напарсили, отличается?

Не понял вопроса… Если мы передадим в метод не то количество параметров которые у него есть smile ? Исключения наверное будет, или я не понял…

Добавлено через 7 минут и 13 секунд
Felan  твоего примера я пока не понял, попробую разобраться чуть позже, что-то он мне кажется сильно раздутым… Ты предлагаешь писать наследника TArguments для каждой команды??? smile  Если так, то мне в 50 раз проще будет изменить параметры функций класса TCommands

Автор: ama_kid 25.8.2008, 22:09
Цитата(Rohoss @  21.8.2008,  22:39 Найти цитируемый пост)
ama_kid либо я не понял твоего примера, либо ты не понял что я хочу
Может быть, конечно и второе, но посмотри, правильно ли я тебя понял на этот раз?
Код
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

 TCommands = class
  published
   function cMethod1(Param:array of string):Boolean;
   function cMethod2(Param:array of string):Boolean;
   function cMethod3(Param:array of string):Boolean;
 end;

 type
  TNte = function(s:array of string):boolean of object;
var
  Form1: TForm1;
  TC:TCommands;
implementation

{$R *.dfm}

function ExecuteMetod(sName:string;Params:array of string):boolean;
var
 Method:TMethod;
 nte:TNte;
begin
 Result:=false;
 Method.Data:=TC;
 Method.Code:=TC.MethodAddress(sName);
 if Method.Code = nil then Exit;
 nte:=TNte(Method);
 Result:=nte(Params);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 if ExecuteMetod('cMethod2',['String','String']) then
   ShowMessage('Execution of cMethod2 is Successfull');

 if ExecuteMetod('cMethod1',['Param']) then
   ShowMessage('Execution of cMethod1 is Successfull');

 if ExecuteMetod('cMethod3',['String','String','Param']) then
   ShowMessage('Execution of cMethod3 is Successfull');
end;

{ TCommands }

function TCommands.cMethod1(Param:array of string): Boolean;
begin
 Result:= Param[0] = 'Param';
end;

function TCommands.cMethod2(Param:array of string): Boolean;
begin
 Result:=CompareStr(Param[0],Param[1])=0;
end;

function TCommands.cMethod3(Param:array of string): Boolean;
begin
 Result:=(Param[0]=Param[1])and(Length(Param[2])<Length(Param[0]));
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 TC:=TCommands.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
 TC.Free;
end;

end.



Добавлено
Цитата(Rohoss @  22.8.2008,  19:10 Найти цитируемый пост)
вопрос в том, чтобы не переопределять класс TCommands
а, ну после этого - я тебя понял smile Тогда да - сложно smile

Автор: CodeMonkey 26.8.2008, 12:58
Цитата(Rohoss @  25.8.2008,  21:20 Найти цитируемый пост)
Ты имел ввиду в функции? Хз, думаю как-то можно…

Значит ваше кунг-фу ещё недостаточно хорошо ;)

Код
uses
  ObjAuto;

type
  {$M+}
  {$METHODINFO ON}
  TTestObj = class
  published
    procedure P1(S1: String);
    procedure P2(S1, S2: String);
    procedure P3(S1, S2, S3: String);
  end;

{ TTestObj }

procedure TTestObj.P1(S1: String);
begin
  OutputDebugString(PChar('"' + S1 + '"'));
end;

procedure TTestObj.P2(S1, S2: String);
begin
  OutputDebugString(PChar('"' + S1 + '", "' + S2 + '"'));
end;

procedure TTestObj.P3(S1, S2, S3: String);
begin
  OutputDebugString(PChar('"' + S1 + '", "' + S2 + '", "' + S3 + '"'));
end;

procedure TForm9.Button1Click(Sender: TObject);
type
  TParams = array of Variant;

  procedure CallMethod(const AObj: TObject; const AMethodName: String; const AParams: TParams);
  resourcestring
    sMethodNotFound = 'Method %s of class %s not found';
  var
    MI: PMethodInfoHeader;
    Ind: array of Integer;
    X: Integer;
  begin
    MI := GetMethodInfo(AObj, AMethodName);
    if not Assigned(MI) then
      raise Exception.CreateFmt(sMethodNotFound, [AMethodName, AObj.ClassName]);
    SetLength(Ind, Length(AParams));
    for X := 0 to High(Ind) do
      Ind[X] := X + 1;
    ObjectInvoke(AObj, MI, Ind, AParams);
  end;

var
  T: TTestObj;
  Arr: TParams;
begin
  T := TTestObj.Create;
  try

    SetLength(Arr, 1);
    Arr[0] := 'Test1';
    CallMethod(T, 'P1', Arr);

    SetLength(Arr, 2);
    Arr[1] := 'Test2';
    CallMethod(T, 'P2', Arr);

    SetLength(Arr, 3);
    Arr[2] := 'Test3';
    CallMethod(T, 'P3', Arr);

  finally
    FreeAndNil(T);
  end;
end;

Автор: Rohoss 26.8.2008, 20:27
CodeMonkey было бы очень не плохо если бы ты прокомментировал то, что написал… Так как гугл не заваливает инфой по запросу «delphi ObjAuto»  smile

Добавлено через 47 секунд
да и drbk тоже…

Автор: CodeMonkey 27.8.2008, 00:21
Всё это подробно разжёвывается, например, здесь: http://hallvards.blogspot.com/2006/09/extended-class-rtti.html и далее по ссылкам.

На русском: http://www.transl-gunsmoker.ru/2011/07/digging-into-soap-and-websnap.html (начало серии тут: http://www.transl-gunsmoker.ru/2011/07/polymorphism-ad-nauseum.html )

P.S. Респект за попытку разобраться вместо бездумного копирования! smile 

Автор: Rohoss 29.8.2008, 04:31
Цитата(CodeMonkey @  27.8.2008,  00:21 Найти цитируемый пост)
Всё это подробно разжёвывается, например, здесь: http://hallvards.blogspot.com/2006/09/exte...class-rtti.html и далее по ссылкам.P.S. Респект за попытку разобраться вместо бездумного копирования!  


Вроде в общих чертах понял! 
Спасибо!


Автор: CodeMonkey 13.9.2010, 20:49
Начиная с Delphi 2010 появилась новая RTTI, где можно делать много новых вещей.

Немного ссылок:
http://robstechcorner.blogspot.com/2009/09/delphi-2010-rtti-basics.html
http://habrahabr.ru/blogs/delphi/86840/
http://delphi.about.com/od/oopindelphi/a/delphirtti.htm
http://robstechcorner.blogspot.com/search/label/RTTI

Код
program Project11;
{$APPTYPE CONSOLE}
uses
  StdCtrls, TypInfo, Classes, Rtti;
var
  c: TRttiContext; 
  m: TRttiMethod; 
  t : TRttiInstanceType;
  SL: TValue; 
  Lines: TValue;
begin
  c := TRttiContext.Create;
  try
    t := c.FindType('Classes.TStringList') as TRttiInstanceType;
    SL := t.GetMethod('Create').Invoke(t.MetaclassType, []);

    t.GetMethod('Add').Invoke(SL, ['Hello Do you like my hat?']);
    t.GetMethod('Add').Invoke(SL, ['I like that hat, what a party hat!']);

    Lines := t.GetProperty('Text').GetValue(SL.AsObject);

    Writeln(Lines.ToString);
  finally
    FreeAndNil(c);
  end;
  ReadLn;
end.

Автор: bems 13.9.2010, 22:42

M
bems
Оффтоп удалён. Или вернитесь к теме обсуждения, или перестаньте насиловать труп

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)