powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Дженерики: передача процедуры в качестве параметра
108 сообщений из 108, показаны все 5 страниц
Дженерики: передача процедуры в качестве параметра
    #39724027
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я пытаюсь написать бота. Больше нужна идея, нежели код и пример, но и примеры тоже будут полезны.
Важно, чтобы в итоге код получился легко масштабируемым, чтобы можно было легко добавлять обработчики (процедуры) новых команд.

Я так понимаю, что нужно создать какой-то список, где будет ключ: команда боту /start, /find, /найти, /Ещё что-то) и значение: название, соответствующей этому ключевому слову процедуре. Не писать же в процедуре входящих команд 100500 IF`ов?

Дженерики почти никогда не использовал. Использовал что-то простое для строк и объектов или чисел.
Буду благодарен за примеры и идеи.
Хоть как это правильно объявить, с чего начать?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724034
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
что-то типа
Код: pascal
1.
2.
Var
  DicCmds: TDictionary<String, а здесь что>;


чтобы можно было потом подставить название процедуры в качестве параметра
и как это потом использовать?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724035
asviridenkov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11что-то типа
Код: pascal
1.
2.
Var
  DicCmds: TDictionary<String, а здесь что>;


чтобы можно было потом подставить название процедуры в качестве параметра
и как это потом использовать?

type
TMyProc = function(const Command: string): string;
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724038
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Внутри секции ptivate пишу
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
...
...
Type
    Type botStart = procedure;
    Type botFindByID = procedure (id: integer);
    Type botFindByPhone= procedure(const sPhone, sType: string);

...
...
Var
  DicCmds: TDictionary<String, TProcedure>;



так?
даже если разное количество параметров у процедур будет?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724041
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11
Код: pascal
1.
Type botStart = procedure;


в итоге ругается компилятор
Incompatible types: 'regular procedure and method pointer'


Так правильно?
Код: pascal
1.
2.
Type
  TcmdStart = procedure of object;
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724045
ziv-2014
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11,
TMethod и Invoke на вскидку.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724061
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ziv-2014,
???
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724065
Фотография JayDi
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Лучше уж добавлять обработку команд через о дельный метод:

Код: pascal
1.
procedure registerCommand(const ACommand: string; const AWorker: procedure of object);
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724067
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
JaDi,

а дальше?
пока не понял идеи
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724069
Фотография JayDi
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
и вообще, в обработчик ведь надо параметры разные передавать. Поэтому сначала надо определиться с форматом этих параметров. Например, в виде json-строки.

Type TBotWorker = procedure(const AParams: string);

После чего получится
Код: pascal
1.
2.
3.
4.
5.
6.
7.
procedure registerCommand(const ACommand: string; const AWorker: TBotWorker);

...

registerCommand('start', this.ProcessStart);
registerCommand('find_by_id', this.ProcessFindByID);
registerCommand('find_by_name', this.ProcessFindByName);
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724070
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
JaDi, я пока не знаю, какие будут параметры и какого типа.
Мало того, заранее неизвестно, что напишет пользователь боту. Это может быть не только команда, но и простой текст, которые придётся распарсить и понять - какую процедуру обработки вызвать.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724075
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11Внутри секции ptivate пишу
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
...
...
Type
    Type botStart = procedure;
    Type botFindByID = procedure (id: integer);
    Type botFindByPhone= procedure(const sPhone, sType: string);

...
...
Var
  DicCmds: TDictionary<String, TProcedure>;



так?
даже если разное количество параметров у процедур будет?

Уже давно пора писать
Код: pascal
1.
botStart = reference to procedure;


И там неважно что будет тогда, метод, процедура или анонимная процедура
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724081
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-Cite
Код: pascal
1.
reference to procedure;



Тогда как объявить словарь?

Вот сделал так:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
    Type TBotProcRef  = reference to procedure;
    Type TBotProcRef1 = reference to procedure (id: integer);
    Type TBotProcRef2 = reference to procedure(const sPhone, sType: string);

    Var      
      DicCmdRows: TDictionary<String, TProcedure>;
      cmdStart:  TBotProcRef;
      cmdDateTime: TBotProcRef;
      cmdFindByID: TBotProcRef1;




Получаю ошибку при компиляции:
Incompatible types: 'TProcedure' and 'TfmXXX.TBotProcRef'

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
procedure TfmMainTelegramBot.FillDicProcs;
begin
  if Not Assigned(DicCmdRows) then
    DicCmdRows := TDictionary<string, TProcedure>.Create
  else
    DicCmdRows.Clear;

  DicCmdRows.Add('/Start', cmdStart);
//  DicCmdRows.Add('/время', cmdDateTime);

end;
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724083
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А если словарь объявлять так:
Код: pascal
1.
2.
3.
4.
5.
6.
    Type TBotProcRef  = reference to procedure;
    Type TBotProcRef1 = reference to procedure (id: integer);
    Type TBotProcRef2 = reference to procedure(const sPhone, sType: string);

   Var
    DicCmdRows: TDictionary<String, TBotProcRef>;


То тогда придётся объявлять несколько разных словарей
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724087
Фотография defecator
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор форума
Навскидку

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
type
    { один параметр }
    TParam = record
      ParamName  : string[64] ;
      ParamValue : Variant ;
    end ;

    TParams = array of TParam ;

    TBotProcRef = procedure(const aParams : TParams) ;
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724088
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я пока думаю, что всегда будет один параметр, т.е. в качестве этого одного параметра будет входящее написанное пользователем сообщение....
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724089
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11А если словарь объявлять так:
Код: pascal
1.
2.
3.
4.
5.
6.
    Type TBotProcRef  = reference to procedure;
    Type TBotProcRef1 = reference to procedure (id: integer);
    Type TBotProcRef2 = reference to procedure(const sPhone, sType: string);

   Var
    DicCmdRows: TDictionary<String, TBotProcRef>;



То тогда придётся объявлять несколько разных словарей

Ладно, продолжаю тормозить

rcvMessage - метод ресивера, который срабатывает при входящем сообщении
Код: pascal
1.
2.
3.
4.
procedure TfmMainXXX.rcvMessage(ASender: TObject; AMessage: ITgMessage);
begin
  if DicCmdRows.ContainsKey(AMessage.Text) then
    cmdStart := DicCmdRows[AMessage.Text];




И как запустить на выполнение cmdStart?


Сейчас объявлено так:

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
    procedure PcmdStart;
    procedure FillDicProcs;

    Type TBotProcRef  = reference to procedure;
    Type TBotProcRef1 = reference to procedure (id: integer);
    Type TBotProcRef2 = reference to procedure(const sPhone, sType: string);



    Var
      sUserDocsKvxTelegramBotPath: string;
      DicCmdRows: TDictionary<String, TBotProcRef>;
      cmdStart:  TBotProcRef;
      cmdDateTime: TBotProcRef;
      cmdFindByID: TBotProcRef1;


...
...
...

// наполняем словарь ключами и ссылками на процедуры-обработчики команд
procedure TfmMainXXX.FillDicProcs;
begin
  if Not Assigned(DicCmdRows) then
    DicCmdRows := TDictionary<string, TBotProcRef>.Create
  else
    DicCmdRows.Clear;

  DicCmdRows.Add('/Start', cmdStart);
  DicCmdRows.Add('/время', cmdDateTime);

end;

// в итоге должен сработать метод PcmdStart, когда пользователь прислал /start
procedure TfmMainXXX.PcmdStart;
begin
// обработка команды /Start
  ShowMessage('/Start');
end;
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724090
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11
Код: pascal
1.
2.
 DicCmdRows.Add('/Start', cmdStart);
  DicCmdRows.Add('/время', cmdDateTime);



здесь я ошибся, нужно подставлять имена реальных процедур

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
procedure TfmMainXXX.PcmdStart;
begin
// обработка команды /Start
  ShowMessage('/Start');
end;


procedure TfmMainXXX.PcmdShowTime;
begin
// обработка команды /время
  ShowMessage('/время');
end;

// заполняем словарь
procedure TfmMainTelegramBot.FillDicProcs;
begin
  if Not Assigned(DicCmdRows) then
    DicCmdRows := TDictionary<string, TBotProcRef>.Create
  else
    DicCmdRows.Clear;

  DicCmdRows.Add('/Start', PcmdStart);
  DicCmdRows.Add('/время', PcmdShowTime);

end;


procedure TfmMainXXX.rcvMessage(ASender: TObject; AMessage: ITgMessage);
begin
  if DicCmdRows.ContainsKey(AMessage.Text) then
    BotProcRef := DicCmdRows[AMessage.Text];

// просто написать, так правильно?
 BotProcRef;



и как теперь выполнить cmdStart, в котором живёт
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724091
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Да, работает, бот получил эти 2 команды и запустил нужные процедуры.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724095
Фотография JayDi
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пример реализации: менеджер ботов, два боты со своими командами, обработка и вывод результата. Можно дальше дорабатывать, например, добавить пользовательские сессии в менеджере и хранить там состояние о об активном боте и введенных данных.

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
type
  // обработчик одной команды
  TBotWorker = procedure(const AParams: string; var AResults: string) of object;

  TBotsManager = class;
  TBot = class
  public
    procedure RegisterCommands(const ABotsManager: TBotsManager); virtual; abstract;
  end;

  // менеджер для управления ботами, командами и обработки
  TBotsManager = class
  private
    BotsList: TObjectList<TBot>;
    CommandsList: TDictionary<string, TBotWorker>;
    procedure ProcessCommand(const ACommandLine: string);
  public
    constructor Create; overload;
    destructor Destroy; override;

    procedure RegisterBot(ABot: TBot);
    procedure RegisterCommand(const ACommand: string; const AWorker: TBotWorker);
  end;

  // бот 1
  TBotDialogHello = class(TBot)
  private
    procedure ProcessHello(const AParams: string; var AResults: string);
  public
    procedure RegisterCommands(const ABotsManager: TBotsManager); override;
  end;

  // бот 2
  TBotDialogSearch = class(TBot)
  private
    procedure ProcessStart(const AParams: string; var AResults: string);
    procedure ProcessFindByName(const AParams: string; var AResults: string);
    procedure ProcessFindByID(const AParams: string; var AResults: string);

    procedure ProcessFind(const AParams: string; var AResults: string);
  public
    procedure RegisterCommands(const ABotsManager: TBotsManager); override;
  end;




Полностью реализация:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
unit Unit14;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  System.Generics.Collections;

type
  TForm14 = class(TForm)
    Button_Send: TButton;
    edit_Command: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure Button_SendClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
  public
  end;

type
  // обработчик одной команды
  TBotWorker = procedure(const AParams: string; var AResults: string) of object;

  TBotsManager = class;
  TBot = class
  public
    procedure RegisterCommands(const ABotsManager: TBotsManager); virtual; abstract;
  end;

  // менеджер для управления ботами, коммандами и обработки
  TBotsManager = class
  private
    BotsList: TObjectList<TBot>;
    CommandsList: TDictionary<string, TBotWorker>;
    procedure ProcessCommand(const ACommandLine: string);
  public
    constructor Create; overload;
    destructor Destroy; override;

    procedure RegisterBot(ABot: TBot);
    procedure RegisterCommand(const ACommand: string; const AWorker: TBotWorker);
  end;

  // бот 1
  TBotDialogHello = class(TBot)
  private
    procedure ProcessHello(const AParams: string; var AResults: string);
  public
    procedure RegisterCommands(const ABotsManager: TBotsManager); override;
  end;

  // бот 2
  TBotDialogSearch = class(TBot)
  private
    procedure ProcessStart(const AParams: string; var AResults: string);
    procedure ProcessFindByName(const AParams: string; var AResults: string);
    procedure ProcessFindByID(const AParams: string; var AResults: string);

    procedure ProcessFind(const AParams: string; var AResults: string);
  public
    procedure RegisterCommands(const ABotsManager: TBotsManager); override;
  end;

var
  Form14: TForm14;

implementation

{$R *.dfm}

var
  BotsManager: TBotsManager;

{ TBotManager }

constructor TBotsManager.Create;
begin
  inherited;

  Self.BotsList := TObjectList<TBot>.Create;
  Self.CommandsList := TDictionary<string, TBotWorker>.Create();
end;

destructor TBotsManager.Destroy;
begin
  FreeAndNil(Self.CommandsList);
  FreeAndNil(Self.BotsList);

  inherited;
end;

procedure TBotsManager.ProcessCommand(const ACommandLine: string);
var
  commandName: string;
  commandParams: string;
  foundedWorker: TBotWorker;
  s: string;
  res: string;
begin
  s := Trim(ACommandLine);
  if s.IndexOf(' ') >= 0 then
  begin
    commandName := AnsiLowerCase(trim(s.Substring(0, s.IndexOf(' '))));
    commandParams := trim(s.Substring(commandName.Length + 1));
  end
  else
  begin
    commandName := AnsiLowerCase(s);
    commandParams := '';
  end;

  if Self.CommandsList.TryGetValue(commandName, foundedWorker) then
  begin
    res := '';
    foundedWorker(commandParams, res);
    ShowMessage(res);
  end
  else
  begin
    ShowMessage('unknown command: ' + commandName);
  end;
end;

procedure TBotsManager.RegisterBot(ABot: TBot);
begin
  Self.BotsList.Add(ABot);

  ABot.RegisterCommands(Self);
end;

procedure TBotsManager.RegisterCommand(const ACommand: string; const AWorker: TBotWorker);
begin
  Self.CommandsList.Add(ACommand, AWorker);
end;

procedure TForm14.Button_SendClick(Sender: TObject);
begin
  BotsManager.ProcessCommand(edit_Command.Text);
end;

procedure TForm14.FormCreate(Sender: TObject);
begin
  BotsManager := TBotsManager.Create;
  BotsManager.RegisterBot(TBotDialogHello.Create);
  BotsManager.RegisterBot(TBotDialogSearch.Create);
end;

procedure TForm14.FormDestroy(Sender: TObject);
begin
  FreeAndNil(BotsManager);
end;

procedure TBotDialogSearch.ProcessFind(const AParams: string; var AResults: string);
var
  id: Integer;
begin
  id := StrToIntDef(AParams, -1);
  if id <> -1 then
  begin
    Self.ProcessFindByID(AParams, AResults);
  end
  else
  begin
    Self.ProcessFindByName(AParams, AResults);
  end;
end;

procedure TBotDialogSearch.ProcessFindByID(const AParams: string; var AResults: string);
var
  id: Integer;
begin
  id := StrToIntDef(AParams, -1);
  if id <> -1 then
  begin
    AResults := 'searching by id = ' + id.ToString;
  end
  else
  begin
    AResults := 'unknown search params';
  end;
end;

procedure TBotDialogSearch.ProcessFindByName(const AParams: string; var AResults: string);
begin
  if AParams <> '' then
  begin
    AResults := 'searching by name = ' + AParams;
  end
  else
  begin
    AResults := 'unknown search params';
  end;
end;

procedure TBotDialogSearch.ProcessStart(const AParams: string; var AResults: string);
begin
  AResults := 'started';
end;

procedure TBotDialogSearch.RegisterCommands(const ABotsManager: TBotsManager);
begin
  ABotsManager.RegisterCommand('start', Self.ProcessStart);
  ABotsManager.RegisterCommand('find by name', Self.ProcessFindByName);
  ABotsManager.RegisterCommand('find by id', Self.ProcessFindByID);
  ABotsManager.RegisterCommand('find', Self.ProcessFind);
end;

procedure TBotDialogHello.ProcessHello(const AParams: string; var AResults: string);
begin
  AResults := 'Hello, username!';
end;

procedure TBotDialogHello.RegisterCommands(const ABotsManager: TBotsManager);
begin
  ABotsManager.RegisterCommand('hello', Self.ProcessHello);
end;

end.

...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724097
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
JaDi, это тя прямо сейчас написал?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724099
Фотография JayDi
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11,

ога... дфм форму не прикладываю, она там простая - кнопка и поле для ввода.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724103
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ага, спасибо.
Значит, я шёл в нужном направлении.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725074
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не смог найти.
Поэтому вопрос.
А есть ли у Delphi встроенный способ создать словарь с регистронезависимым (case-insensitive) поиском по ключам?

Код: pascal
1.
DicCmdRows := TDictionary<string, TBotProcRef>.Create(что здесь указать);



Или нужно самому пилить?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725083
zinpub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
X11,

Надо TCustomEqualityComparer.GetHashCode и Equals - переопределить и вуаля
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725088
zinpub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ну и TCustomEqualityComparer.Create -> указать
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725089
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
не понял...
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725090
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну т.е. написать СВОИ методы?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725092
zinpub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
X11,

Ну да, пишешь GetHashCode - с Upper вначале - и получаешь регистронезависимость
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725095
zinpub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Откуда то со Стэка...

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
function TCustomEqualityComparer.Equals(const Left, Right: string): Boolean;
begin
  {$IFDEF UNICODE }
    Result := (Left.ToUpper = Right.ToUpper);
  {$ELSE }
    Result := SameText(Left, Right);
  {$ENDIF }
end;

function TCustomEqualityComparer.GetHashCode(const Value: string): Integer;
var s: string;
begin
  s := {$IFDEF UNICODE } Value.ToUpper {$ELSE } UpperCase(Value) {$ENDIF };
  Result := BobJenkinsHash(s[1], Length(s) * SizeOf(s[1]), 0);
end;
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725112
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Где-то я стормозил.
Пишу:

объявил
Код: pascal
1.
2.
3.
4.
5.
6.
type
  TCustomEqualityComparer = class(TEqualityComparer<string>)
  public
    function Equals(const Left, Right: string): Boolean; override;
    function GetHashCode(const Value: string): Integer; override;
  end;




реализация
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
{ TCustomEqualityComparer }

function TCustomEqualityComparer.Equals(const Left, Right: string): Boolean;
begin
ставлю точку останова - сюда даже не заходит 
  Result := Left.ToLowerInvariant = Right.ToLowerInvariant;
end;

function TCustomEqualityComparer.GetHashCode(const Value: string): Integer;
begin
  Result := BobJenkinsHash(Value[1], Length(Value) * SizeOf(Value[1]), 0);
end;



Создаю словарь:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
procedure TfmMainTelegramBot.RegisterCommands;
Var
  CustomEqualityComparer: TCustomEqualityComparer;
begin
  if Not Assigned(DicCmdRows) then
    DicCmdRows := TDictionary<string, TBotProcRef>.Create(CustomEqualityComparer)
  else
    DicCmdRows.Clear;

//добавление соответствия команды и процедуры, которая будет срабатывать
  DicCmdRows.Add('/start', PcmdStart);
  DicCmdRows.Add('/время', PcmdShowTime);

end;



/start выполняется
/Start - не выполняется
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725120
zinpub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
X11,

CustomEqualityComparer - а создать его?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725126
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
точно

но... вижу, что моя GetHashCode выполняется, а моя Equals - нет.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725131
zinpub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
X11,

А нафига, он кстати при добавлении будет выполняться ?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725136
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
GetHashCode выполняется, когда в словарь добавляю
Код: pascal
1.
2.
DicCmdRows.Add('/start', PcmdStart);
DicCmdRows.Add('/время', PcmdShowTime);



а потом ещё, когда приходит боту команда "/ S tart"
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725138
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
zinpubX11,

А нафига, он кстати при добавлении будет выполняться ?

а я откуда знаю? я ж его не заставляю, видать разработчики дельфи так захотели
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725144
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пришлось переделать так:

Код: pascal
1.
2.
3.
4.
5.
6.
7.
function TCustomEqualityComparer.GetHashCode(const Value: string): Integer;
Var
  Value2: string;
begin
  Value2 := Value.ToLowerInvariant;
  Result := BobJenkinsHash(Value2[1], Length(Value2) * SizeOf(Value2[1]), 0);
end;



т.е. сперва приводим к нижнему регистру.

Теперь работает.

Так правильно?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725145
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пошарился, по сети, вижу, что я не одинок
http://www.devsuperpage.com/search/Articles.aspx?G=2&ArtID=28868

тоже кто-то переделал GetHashCode
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725147
zinpub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
X11,

Ну как-то так...

Единственное хэш, как-то по другому надо теперь получать THash.BobJenkins.GetHash - как-то так...
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725148
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Странно, что разработчики Delphi не сделали это сами, заранее.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725149
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
zinpubX11,

Ну как-то так...

Единственное хэш, как-то по другому надо теперь получать THash.BobJenkins.GetHash - как-то так...

Да, за такие подсказки разработчикам спасибо.

'BobJenkinsHash' is deprecated: 'Use System.Hash.THashBobJenkins.GetHashValue'
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725151
Фотография JayDi
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11,

Всё проще -- AnsiLowerCase перед добавлением/проверкой...
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725153
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
JaDiX11,

Всё проще -- AnsiLowerCase перед добавлением/проверкой...

Настоящие герои всегда идут в обход
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725154
Фотография JayDi
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11Странно, что разработчики Delphi не сделали это сами, заранее.
А не стоит ли задуматься, что если ради какой-то своей фичи приходится ТАК извращаться и лезть в самые дебри -- то что-то пошло не так и надо пересмотреть подход к решению своей проблемы?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725155
zinpub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
JaDi,

Это если не надо хранить оригинал
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725163
Valery_B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я делаю примерно так же, как и пример у Jadi +-
Только пример с формами (TForm).
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
type

TModalForm = class(TForm)
 public
  procedure Initialize;virtual;
  function Execute:Boolean; virtual;
end;
  
TModalFormClass = class of TModalForm;

TDialogManager  =  class
 public
  function ShowDialog(const Name:String):Boolean;
  class procedure RegisterDialog(const DialogName:string; AClass:TModalFormClass); 
end;

implementation
var
  FClassesList = TDictionary <string, TModalFormClass>

class procedure RegisterDialog(const DialogName:string; AClass:TModalFormClass); 
begin
 if not Assigned(FClassesList) then 
  FClassesList:=TDictionary <string, TModalFormClass>;
  FClassesList.Add(Name,AClass);
end;

function TDialogManager.ShowDialog(const Name:String):Boolean;
var
 Dlg:TModalForm;
begin
 Dlg:=FClassesList[Name].Create(nil);
  try
    Dlg.Initialize;
    Result:=Dlg.Execute;
  finally
    Dlg.Free;
 end;
end;

initialization

finalization
 FClassesList.Free;
end.


...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725166
Valery_B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11Не смог найти.
Поэтому вопрос.
А есть ли у Delphi встроенный способ создать словарь с регистронезависимым (case-insensitive) поиском по ключам?
Или нужно самому пилить?

Код: pascal
1.
 FClassesList := TDictionary<string, TForm>.Create(TIStringComparer.Ordinal);
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725229
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Valery_BX11Не смог найти.
Поэтому вопрос.
А есть ли у Delphi встроенный способ создать словарь с регистронезависимым (case-insensitive) поиском по ключам?
Или нужно самому пилить?

Код: pascal
1.
 FClassesList := TDictionary<string, TForm>.Create(TIStringComparer.Ordinal);



я пробовал Ordinal - он регистразависим
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725253
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
var
  A: TDictionary<string, Int32>;
begin
  A := TDictionary<string, Int32>.Create(
    TEqualityComparer<string>.Construct(
      function(const aLeft, aRight: string): Boolean
      begin
        Exit(aLeft.ToUpper().Equals(aRight.ToUpper()));
      end,
      function(const aValue: string): Integer
      begin
        Exit(THashBobJenkins.GetHashValue(aValue.ToUpper()));
      end
    )
  );
  A.Add('k', 50);
  A.Items['K'];
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725287
white_nigger
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11,
смотри создание TPersistentClassDictionary в System.Classes.pas
или как тебе Valery_B посоветовал
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725501
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-Cite,

не это почти то же самое, только с использованием лямбла-функций?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725504
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Valery_BX11Не смог найти.
Поэтому вопрос.
А есть ли у Delphi встроенный способ создать словарь с регистронезависимым (case-insensitive) поиском по ключам?
Или нужно самому пилить?

Код: pascal
1.
 FClassesList := TDictionary<string, TForm>.Create(TIStringComparer.Ordinal);



Не понял... недавно использовал именно TIStringComparer.Ordinal и регистронезависимость не работала, а теперь работает

ок. наверное я где-то ещё обшибся.
спасибо
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725513
Valery_B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11Valery_Bпропущено...


Код: pascal
1.
 FClassesList := TDictionary<string, TForm>.Create(TIStringComparer.Ordinal);



я пробовал Ordinal - он регистразависим

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
procedure TForm1.Button1Click(Sender: TObject);
var
 FDict:TDictionary<string, string>;
begin
  FDict:=TDictionary<string, string>.Create(TIStringComparer.Ordinal);
  try
   FDict.Add('hEllOwOrlD','Всем привет');
   ShowMessage(FDict['HelloWorld']);
  finally
   FDict.Free;
  end;
end;
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725521
zinpub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725524
Valery_B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11Не понял... недавно использовал именно TIStringComparer.Ordinal и регистронезависимость не работала, а теперь работает

ок. наверное я где-то ещё обшибся.
спасибо
Ок.
По теме - не создавай функции/процедуры, а создавай классы.
Как было предложено ранее:
Код: pascal
1.
2.
3.
4.
5.
6.
TBot  = class
...
end;
И их производные.
TSuperBot = class(TBot)
TKillerBot = class (TBot)


С таким кодом, будет гораздо проще работать и понимать.
Ты всегда можешь инициализировать класс так, как нужно тебе в конкретном случае.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725525
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Valery_B, да верю, я верю, уже перепроверил и убедился, что ты прав.

Я не знаю, что не так у меня было в пенрвый раз, почему не сработало. Я именно с TIStringComparer.Ordinal и начинал.

вот и в справке написано:Возвращает объект TStringComparer, который выполняет сравнение строк с учетом регистра

...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725528
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11Не понял... недавно использовал именно TIStringComparer.Ordinal и регистронезависимость не работала, а теперь работает

Вру. Скорей всего я "TIStringComparer.Ordinal" хотел использовать, но не использовал, т.к. полез в справку, а там написано, что - зависит от регистра.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725530
Valery_B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11Я не знаю, что не так у меня было в пенрвый раз, почему не сработало. Я именно с TIStringComparer.Ordinal и начинал.

Такое у всех было)
Потому что ты делал это в большом проекте, и тестировал не в тех местах.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725536
Фотография JayDi
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Лол, чудаки из эмбы свою справку так и не исправили -- до сих пор стоит текст про регистрозависимость:
http://docwiki.embarcadero.com/Libraries/Tokyo/en/System.Generics.Defaults.TIStringComparer.Ordinal

Тогда как в коде там AnsiLowerCase используется (т.е. не зависит от регистра):
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
function TOrdinalIStringComparer.Compare(const Left, Right: string): Integer;
var
  L, R: string;
  len, lenDiff: Integer;
begin
  L := AnsiLowerCase(Left); 
  R := AnsiLowerCase(Right); 
  len := Length(L);
  lenDiff := len - Length(R);
  if Length(R) < len then
    len := Length(R);
  Result := BinaryCompare(PChar(L), PChar(R), len * SizeOf(Char));
  if Result = 0 then
    Exit(lenDiff);
end;
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725548
zinpub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
JaDi,

Ха, прикольно... Ребята жгут
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39725697
white_nigger
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Кстати, если правильно помню, то использование такого словаря приводило к утечке памяти в одной из версий делфи
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39726351
Василий 2
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
JaDi
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
function TOrdinalIStringComparer.Compare(const Left, Right: string): Integer;
var
  L, R: string;
  len, lenDiff: Integer;
begin
  L := AnsiLowerCase(Left); 
  R := AnsiLowerCase(Right); 
  len := Length(L);
  lenDiff := len - Length(R);
  if Length(R) < len then
    len := Length(R);
  Result := BinaryCompare(PChar(L), PChar(R), len * SizeOf(Char));
  if Result = 0 then
    Exit(lenDiff);
end;


Неужели две конвертации строк и бинарное сравнение быстрее непосредственного сравнения без учета регистра?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39726375
zinpub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Василий 2,

А что такое "непосредственное сравнения без учета регистра" ?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39726581
Василий 2
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
zinpubВасилий 2,

А что такое "непосредственное сравнения без учета регистра" ?
AnsiCompare с параметром
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39726609
zinpub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Василий 2,

Да так быстрее, но CompareString - на мой взгляд, несколько не подходит в данном случае, тк результат сравнения не всегда однозачен...
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729479
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Немножко переделал. Теперь в словаре живут не ссылки процедуры, а строковые имена процедур.
Кто работал с RTTI, пожалуйста, подскажите, как выполнить метод по имени.
Читаю про TRttiContext, TRTTIType и TRTTIMethod примеры и справку, не могу врубиться.

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
Var
  BotProc: TBotProc;// её нужно выполнить
  BotProcName: string;// ссылка на процедуру
  ctx: TRttiContext;
  rtype: TRTTIType;
  rmethod: TRTTIMethod;
begin
...
...

  if DicCmds.ContainsKey(sInCmd) then
  begin
    BotProcName := DicCmds[sInCmd];

    rtype := ctx.GetType(self.ClassType);
    rmethod := rtype.GetMethod(BotProcName);

  дальше что ? здесь застрял

    if Assigned(BotProc) then// если нашли
      BotProc(AMessage.Text, AMessage.From);
  end;


спасибо
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729489
Tactical Nuclear Penguin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: pascal
1.
rmethod.Invoke ?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729540
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
вроде бы, но не разобрался как правильно ему передать параметры
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729568
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: pascal
1.
rmethod.Invoke(Self, TArray<TValue>.Create(TValue.From<string>('AMessage.Text'), TValue.From<string>('AMessage.From')));


В вашем случае скорее всего так...
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729570
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ошибся выше, надо так:
Код: pascal
1.
rmethod.Invoke(Self, TArray<TValue>.Create(TValue.From<string>(AMessage.Text), TValue.From<string>(AMessage.From)));
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729612
Гирлионайльдо
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Весело настанет когда, пользователь случайно, а может и не случайно используя уже какой - то метод, вызовет Invoke с типом объявленным посредством type тот же type myString = string и не сможет вызвать с этим аргументом
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729760
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Гирлионайльдо, не понял...
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729764
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Проблема в том, что
Код: pascal
1.
rmethod := rtype.GetMethod(BotProcName);


не находит нужный метод, хотя он 100% есть у формы:



хотя метод PcmdStart объявлен в секции private формы:
Код: pascal
1.
    procedure PcmdStart(const sCmd: string; User: ItgUser);



Снимок сделан после выполнения строки:
Код: pascal
1.
rmethod := rtype.GetMethod(BotProcName);
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729786
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
тут есть похожий пример
https://forum.antichat.ru/threads/362046/
но тут даже целый цикл запилили...
Ну ОК, я тоже сделал:
Код: pascal
1.
2.
3.
    for rmethod in rtype.GetMethods do
      if (rmethod.Parent = rtype) and (rmethod.Name = BotProcName) then
        rmethod.Invoke(Self, TArray<TValue>.Create(TValue.From<string>(AMessage.Text), TValue.From<string>(AMessage.From.ID.ToString)));


но вот что странно:



Видите, сработала бряка на строке 725, хотя:
rmethod.Name = 'Destroy', а BotProcName = 'PcmdStart'.
Как так?

Может, дело не в бабине?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729854
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если метод не public или published то по умолчанию их в RTTI нет, вроде бы...
Из-за в System
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
{ RTTI Visibility }
type
  TVisibilityClasses = set of (vcPrivate, vcProtected, vcPublic, vcPublished);

const
  { These constants represent the default settings built into the compiler.
    For classes, these settings are normally inherited from TObject. }
  DefaultMethodRttiVisibility = [vcPublic, vcPublished];
  DefaultFieldRttiVisibility = [vcPrivate..vcPublished];
  DefaultPropertyRttiVisibility = [vcPublic, vcPublished];

type
  { Default RTTI settings }
  {$RTTI INHERIT
      METHODS(DefaultMethodRttiVisibility)
      FIELDS(DefaultFieldRttiVisibility)
      PROPERTIES(DefaultPropertyRttiVisibility)}



Укажите в том модуле где ваш класс с приватными методами
Код: pascal
1.
2.
{$RTTI INHERIT
      METHODS([vcPrivate, vcPublished])}


или
Код: pascal
1.
2.
{$RTTI EXPLICIT
      METHODS([vcPrivate, vcPublished])}


не помню что переопределяет...
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729862
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-CiteЕсли метод не public или published то по умолчанию их в RTTI нет, вроде бы..

Я про это читал, но думал, что это только старой self.GetProcAddress касается.

Ок, ладно, но почему условие срабатывает, если имя метода не находит?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729888
Cobalt747
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11Ок, ладно, но почему условие срабатывает, если имя метода не находит?
Скорее всего. косяк отладчика.
Сделай вывод в лог, наверняка там будет все правильно.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729891
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
нифигасебе косяк... это довольно серьёзно
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729897
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ок, я перенес нужные методы в public.
Теперь нужный метод найден, но...

exceptionProject raised exception class EInvalidCast with message 'Invalid class typecast'
на вот этой строке
Код: pascal
1.
rmethod.Invoke(Self, TArray<TValue>.Create(TValue.From<string>(AMessage.Text), TValue.From<string>(AMessage.From.ID.ToString)));
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729899
Фотография JayDi
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Наглядное пособие переинженеринга -- как вот эта простая конструкция:
Код: pascal
1.
bot.Callback(params);


превратилась вот в этого монстра:
Код: pascal
1.
rmethod.Invoke(Self, TArray<TValue>.Create(TValue.From<string>(AMessage.Text), TValue.From<string>(AMessage.From.ID.ToString)));
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729903
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
JaDi, сперва нужно сделать, чтобы "заработало" пусть даже и в виде монстра, а потом уже и причесать можно.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729917
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11Ок, я перенес нужные методы в public.
Теперь нужный метод найден, но...

exceptionProject raised exception class EInvalidCast with message 'Invalid class typecast'
на вот этой строке
Код: pascal
1.
rmethod.Invoke(Self, TArray<TValue>.Create(TValue.From<string>(AMessage.Text), TValue.From<string>(AMessage.From.ID.ToString)));


У вас в вашем методе, оба параметра типа string ?
Передавайте те значения параметров и того типа, что в методе
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729921
Фотография JayDi
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11,

видимо, я что-то пропустил -- чем не устроила универсальная модель, когда на вход обработчику подаются параметры в универсальном виде типа строки/списка/json'а?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729923
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
JaDiчем не устроила универсальная модель, когда на вход обработчику подаются параметры в универсальном виде типа строки/списка/json'а?
Поддерживаю.
Первое слово всегда команда, остальное, если есть, параметры.
В обработчик команды передаются только параметры.

К чему эти огороды?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729936
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-CiteУ вас в вашем методе, оба параметра типа string ?

блиииииииииииииин

Код: pascal
1.
PcmdStart(const sCmd: string; User: ItgUser);
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729940
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
переделал на
From: ItgUser

Код: pascal
1.
2.
3.
    if Assigned(rmethod) then
      rmethod.Invoke(rmethod.CodeAddress, TArray<TValue>.Create(TValue.From<string>(AMessage.Text),
                                              TValue.From<ItgUser>(AMessage.From)));



всё равно Invalid class typecast
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729942
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanJaDiчем не устроила универсальная модель, когда на вход обработчику подаются параметры в универсальном виде типа строки/списка/json'а?
Поддерживаю.
Первое слово всегда команда, остальное, если есть, параметры.
В обработчик команды передаются только параметры.

К чему эти огороды?

1. Первым словом может быть не /команда, а просто текст какой-нибудь. И тоже нужно обработать.
2. Если всё же /команда, то нужно понять, что именно за команда и вызвать соответствующую процедуру, передав ей ВЕСЬ текст, например, "/Показать 15973".

Вроде бы я никуда не отклонялся. Пытаюсь сделать универсальную масштабируемую модель.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729946
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11И тоже нужно обработать.
Боты без команд не работают. По меньшей мере я не сталкивался.

Ну или пример "какого-нибудь" текста? Из жизни, по возможности.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729952
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman, работают ещё как


Что такое "команда боту"? Это то, что ты САМ, как программист, запрограммируешь в бота, т.е. любое сочетание любых символов. Это же просто текст. Просто есть какие-то общепринятые нормы и понятия.

Бот ведь работает не на их стороне, т.е. не на стороне Вайбера или Телеграма, а на твоей. Сервер тебе просто пересылает то, что напечатал/выбрал пользователь.

Никто не запрещает сделать так, что команды будут начинаться не чертой /, а * звёздочкой, например. Или вообще без таковых. Например, боты с ИИ: ты ему пишешь текст, а он отвечает, разобрав текст предварительно. Например, пишешь "какая сейчас погода в Харькове", а бот лезет в сеть, получает данные и отвечает "такая-то погода". И нет никаких команд, разве не так?

ну как-то так
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729955
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanНу или пример "какого-нибудь" текста? Из жизни, по возможности.

Есть идея запилить приём текстовых объявлений на какой-нибудь портал: автомобильный или по недвижимости типа вашего Авито или нашего OLX, хотя это одно и то же (владелец из Южной Африки - Насперс).

Распарсил текст, разбил на части: тип, цена, район, площади, коробка передач, размер трусов, фокусное расстояние объектива и т.д.
И не надо никаких команд.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729961
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11Например, пишешь "какая сейчас погода в Харькове", а бот лезет в сеть, получает данные и отвечает "такая-то погода". И нет никаких команд, разве не так?
Тогда и не нужно никакого списка с командами и методами?

Изначально-то тема касалась именно списка команд. А тут уже и ИИ вылез и скоро тело прикрутим. Женское. :)
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729962
Фотография JayDi
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X111. Первым словом может быть не /команда, а просто текст какой-нибудь. И тоже нужно обработать.
2. Если всё же /команда, то нужно понять, что именно за команда и вызвать соответствующую процедуру, передав ей ВЕСЬ текст, например, "/Показать 15973".

Вроде бы я никуда не отклонялся. Пытаюсь сделать универсальную масштабируемую модель.

Было еще в самом начале 21717308

Менеджер определяет, что за команда и отправляет ее нужному боту:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
procedure TBotsManager.ProcessCommand(const ACommandLine: string);
var
  commandName: string;
  commandParams: string;
  foundedWorker: TBotWorker;
  s: string;
  res: string;
begin
  s := Trim(ACommandLine);
  if s.IndexOf(' ') >= 0 then
  begin
    commandName := AnsiLowerCase(trim(s.Substring(0, s.IndexOf(' '))));
    commandParams := trim(s.Substring(commandName.Length + 1));
  end
  else
  begin
    commandName := AnsiLowerCase(s);
    commandParams := '';
  end;

  if Self.CommandsList.TryGetValue(commandName, foundedWorker) then
  begin
    res := '';
    foundedWorker(commandParams, res);
    ShowMessage(res);
  end
  else
  begin
    ShowMessage('unknown command: ' + commandName);
  end;
end;
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729968
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanТогда и не нужно никакого списка с командами и методами?

это уже сам программист решает, мне - нужно
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729970
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
JaDiБыло еще в самом начале 21717308

Менеджер определяет, что за команда и отправляет ее нужному боту:

да не проблема....
проблема в том, когда пользователь скажет, что "я хочу, чтобы бот выполняет вот это запрограммированное действие на три похожие команды" и что, мне каждый раз добавлять и перекомпилировать?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729975
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11переделал на
From: ItgUser

Код: pascal
1.
2.
3.
    if Assigned(rmethod) then
      rmethod.Invoke(rmethod.CodeAddress, TArray<TValue>.Create(TValue.From<string>(AMessage.Text),
                                              TValue.From<ItgUser>(AMessage.From)));



всё равно Invalid class typecast

Код: pascal
1.
rmethod.Invoke(rmethod.CodeAddress


Вы читали хелп? Что идет первым параметром?
Если у вас метод класса, то надо передать Указатель на экземпляр класса в контексте которого будет вызываться метод
Если у вас классовый метод, то надо передать сам класс
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729982
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11и что, мне каждый раз добавлять и перекомпилировать?
Есть иные способы? Разве что на сервер отправлять, вот и весь бот.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729983
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-Cite, т.е. просто self вписать?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729984
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-Cite, спасибо, теперь дошло

Код: pascal
1.
2.
3.
    if Assigned(rmethod) then
      rmethod.Invoke(self, TArray<TValue>.Create(TValue.From<string>(AMessage.Text),
                                              TValue.From<ItgUser>(AMessage.From)));
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729985
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman,

да, я же уже запилил.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729990
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman, на предыдущих страницах я показывал скрины. Идея в том, что у дерева есть корневые узлы - это действия, к которым привязаны методы (процедуры), т.е. то, что умеет бот. Дочерние узлы - это команды. Команды пользователь может редактировать (добавлять, удалять, переименовывать, отключать, включать)



т.е. можно сделать так, что если первое или единственное слово, полученное ботом, будет не "/старт", а "старт", то его тоже можно обработать, так же, как и "/start". Для этого пользователь просто добавляет узел см текстом "старт" в качестве дочернего к корневому "СТАРТ".
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729992
Фотография JayDi
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11JaDiБыло еще в самом начале 21717308

Менеджер определяет, что за команда и отправляет ее нужному боту:

да не проблема....
проблема в том, когда пользователь скажет, что "я хочу, чтобы бот выполняет вот это запрограммированное действие на три похожие команды" и что, мне каждый раз добавлять и перекомпилировать?
Нет, достаточно договориться о протоколе. Например, что каждая команда должна начинаться со спецсимвола типа \. Или что несколько команд можно задать в виде скобок. Или в видео start -end. Короче, вариантов масса. И всё это реализуется как раз в одном месте -- в менеджере, который должен распарсить сообщение пользователя и дальше передать в обработку (одному, нескольким ботам или сразу пачкой команд).

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


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

Пример: "бот1 скачай лалала преобразуй в какака сожми отправь ссылку на лулулу"
Команда: "бот1"
Параметры: "скачай лалала.... на лулулу".

На вход бот1 получает список и дальше сам парсит. Например, команда, команда и параметр.
Команда: "скачай", параметр: "лалала"
Команда: "преобразуй в", параметр: "какака"
Команда: "сожми", дефолтный параметр: результат с последней команды
Команда: "отправь ссылку на", параметр: "лулулу"
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729997
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
JaDiИ всё это реализуется как раз в одном месте -- в менеджере, который должен распарсить сообщение пользователя и дальше передать в обработку (одному, нескольким ботам или сразу пачкой команд).

ну я вроде бы так и делаю
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39729998
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
JaDiПример: "бот1 скачай лалала преобразуй в какака сожми отправь ссылку на лулулу"
Команда: "бот1"
Параметры: "скачай лалала.... на лулулу".

да, я так примерно и делаю
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39730249
Гирлионайльдо
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А точно, баг в Invoke происходит из за Pointer

Код: pascal
1.
  MyWide = type PWideChar;



Не сможет вызвать не один тип PInteger, Pointer и прочие.

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
program Project1;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils, rtti;

type
  MyWide = type PWideChar;

  TMyClass = class
  public
    procedure badCall(const p: MyWide);
  end;

procedure TMyClass.badCall(const p: MyWide);
begin
  Writeln(PWideChar(p));
end;

var
  context: TRttiContext;
  myclass: TMyClass;
  badCall: TRttiMethod;

begin
  try
    myclass := TMyClass.Create;

    badCall := context.GetType(myclass.ClassInfo).GetMethod('badCall');
    badCall.Invoke(myclass, [PWideChar('Hello World')]);

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;

end.



Хочу напомнить, что всё же можно напороться на этот баг, примеру с вызовом функции TStyleManager.SetStyle
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39730429
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Так типы то якобы разные...
Вот так работает...
Не надо совмещать похожие но не одинаковые типы...
Код: pascal
1.
TRttiContext.Create.GetType(ClassType).GetMethod('badCall').Invoke(Self, [TValue.From<MyWide>(MyWide(PWideChar('Hello World')))])
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39730519
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Гирлионайльдо, да, я читал твою тему :)
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39883647
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Итак, оно работает :)
как-то так

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
// прилетело сообщение
  if DicCmds.ContainsKey(sInCmd) and Assigned(AMessage.From) then
  begin
    BotProcName := DicCmds[sInCmd];// получаем имя процедуры (метода: PcmdHelp или PcmdStart, иди др.)

    rtype   := ctx.GetType(self.ClassType);
    rmethod := rtype.GetMethod(BotProcName);// метод должен быть в public

    if Assigned(rmethod) then
      rmethod.Invoke(self, TArray<TValue>.Create(TValue.From<string>(AMessage.Text),
                                                 TValue.From<ItgUser>(AMessage.From)))
    else
      logE(constError + '. ' + constBotProcNameError + ' ' + BotProcName);

  end;



Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
procedure TfmMainTelegramBot.PcmdHelp(const sCmd: string; User: ItgUser);
begin
  SendMsgByBot(User.id, GetLocalizeStr('constStartHelp1', User.LanguageCode), nil, TtgParseMode.Markdown);
end;

procedure TfmMainTelegramBot.PcmdStart(const sCmd: string; User: ItgUser);
Var
 s: string;
begin
  s := GetLocalizeStr('constStartCmd1', User.LanguageCode);
  SendMsgByBot(User.ID, s, nil, TtgParseMode.Markdown);
  SendReplyKeyboardMarkup(User);
end;





Но это всё работает в модуле главной формы и получается так, что интерфейс виснет, если много команд сразу...
теперь следующий этап - это нужно перенести в отдельный поток...
вопрос в том, будет ли rmethod.Invoke работать не в модуле формы, а в отдельном потоке? Или это не важно, был бы класс и public-методы?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39883740
ziv-2014
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11, Invoke будет работать в потоке.
...
Рейтинг: 0 / 0
108 сообщений из 108, показаны все 5 страниц
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Дженерики: передача процедуры в качестве параметра
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]