Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
Топик располагается на нескольких страницах: 1 2 3 4 5      [все]
 Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Я пытаюсь написать бота. Больше нужна идея, нежели код и пример, но и примеры тоже будут полезны.
Важно, чтобы в итоге код получился легко масштабируемым, чтобы можно было легко добавлять обработчики (процедуры) новых команд.

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

Дженерики почти никогда не использовал. Использовал что-то простое для строк и объектов или чисел.
Буду благодарен за примеры и идеи.
Хоть как это правильно объявить, с чего начать?
28 окт 18, 12:33    [21717171]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
что-то типа
Var
  DicCmds: TDictionary<String, а здесь что>;

чтобы можно было потом подставить название процедуры в качестве параметра
и как это потом использовать?
28 окт 18, 12:45    [21717178]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
asviridenkov
Member

Откуда:
Сообщений: 3827
X11
что-то типа
Var
  DicCmds: TDictionary<String, а здесь что>;

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


type
TMyProc = function(const Command: string): string;
28 окт 18, 12:47    [21717179]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Внутри секции ptivate пишу
...
...
Type
    Type botStart = procedure;
    Type botFindByID = procedure (id: integer);
    Type botFindByPhone= procedure(const sPhone, sType: string);

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


так?
даже если разное количество параметров у процедур будет?
28 окт 18, 13:07    [21717185]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
X11
Type botStart = procedure;

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



Так правильно?
Type
  TcmdStart = procedure of object;
28 окт 18, 13:18    [21717191]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
ziv-2014
Member

Откуда:
Сообщений: 140
X11,
TMethod и Invoke на вскидку.
28 окт 18, 13:24    [21717197]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
ziv-2014,
???
28 окт 18, 13:48    [21717221]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
JaDi
Member

Откуда: Сызрань, Россия
Сообщений: 3845
Лучше уж добавлять обработку команд через о дельный метод:

procedure registerCommand(const ACommand: string; const AWorker: procedure of object);
28 окт 18, 13:54    [21717228]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
JaDi,

а дальше?
пока не понял идеи
28 окт 18, 13:58    [21717231]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
JaDi
Member

Откуда: Сызрань, Россия
Сообщений: 3845
и вообще, в обработчик ведь надо параметры разные передавать. Поэтому сначала надо определиться с форматом этих параметров. Например, в виде json-строки.

Type TBotWorker = procedure(const AParams: string);

После чего получится
procedure registerCommand(const ACommand: string; const AWorker: TBotWorker);

...

registerCommand('start', this.ProcessStart);
registerCommand('find_by_id', this.ProcessFindByID);
registerCommand('find_by_name', this.ProcessFindByName);
28 окт 18, 14:01    [21717233]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
JaDi, я пока не знаю, какие будут параметры и какого типа.
Мало того, заранее неизвестно, что напишет пользователь боту. Это может быть не только команда, но и простой текст, которые придётся распарсить и понять - какую процедуру обработки вызвать.
28 окт 18, 14:03    [21717235]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1161
X11
Внутри секции ptivate пишу
...
...
Type
    Type botStart = procedure;
    Type botFindByID = procedure (id: integer);
    Type botFindByPhone= procedure(const sPhone, sType: string);

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


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


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

И там неважно что будет тогда, метод, процедура или анонимная процедура
28 окт 18, 14:28    [21717247]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
X-Cite
reference to procedure;


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

Вот сделал так:
    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'

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;
28 окт 18, 15:27    [21717273]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
А если словарь объявлять так:
    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>;

То тогда придётся объявлять несколько разных словарей
28 окт 18, 15:35    [21717277]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
defecator
Member

Откуда:
Сообщений: 38835
Навскидку

type
    { один параметр }
    TParam = record
      ParamName  : string[64] ;
      ParamValue : Variant ;
    end ;

    TParams = array of TParam ;

    TBotProcRef = procedure(const aParams : TParams) ;
28 окт 18, 15:47    [21717288]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Я пока думаю, что всегда будет один параметр, т.е. в качестве этого одного параметра будет входящее написанное пользователем сообщение....
28 окт 18, 15:51    [21717296]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
X11
А если словарь объявлять так:
    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 - метод ресивера, который срабатывает при входящем сообщении
procedure TfmMainXXX.rcvMessage(ASender: TObject; AMessage: ITgMessage);
begin
  if DicCmdRows.ContainsKey(AMessage.Text) then
    cmdStart := DicCmdRows[AMessage.Text];



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


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

    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;
28 окт 18, 15:57    [21717298]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
X11
 DicCmdRows.Add('/Start', cmdStart);
  DicCmdRows.Add('/время', cmdDateTime);


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

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, в котором живёт
28 окт 18, 16:04    [21717302]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Да, работает, бот получил эти 2 команды и запустил нужные процедуры.
28 окт 18, 16:06    [21717303]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
JaDi
Member

Откуда: Сызрань, Россия
Сообщений: 3845
Пример реализации: менеджер ботов, два боты со своими командами, обработка и вывод результата. Можно дальше дорабатывать, например, добавить пользовательские сессии в менеджере и хранить там состояние о об активном боте и введенных данных.

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;



Полностью реализация:
+
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.
28 окт 18, 16:20    [21717308]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
JaDi, это тя прямо сейчас написал?
28 окт 18, 16:23    [21717310]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
JaDi
Member

Откуда: Сызрань, Россия
Сообщений: 3845
X11,

ога... дфм форму не прикладываю, она там простая - кнопка и поле для ввода.

К сообщению приложен файл. Размер - 6Kb
28 окт 18, 16:25    [21717313]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Ага, спасибо.
Значит, я шёл в нужном направлении.
28 окт 18, 16:31    [21717318]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Не смог найти.
Поэтому вопрос.
А есть ли у Delphi встроенный способ создать словарь с регистронезависимым (case-insensitive) поиском по ключам?

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


Или нужно самому пилить?
30 окт 18, 16:40    [21719217]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
zinpub
Member

Откуда:
Сообщений: 272
X11,

Надо TCustomEqualityComparer.GetHashCode и Equals - переопределить и вуаля
30 окт 18, 16:47    [21719229]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
zinpub
Member

Откуда:
Сообщений: 272
Ну и TCustomEqualityComparer.Create -> указать
30 окт 18, 16:49    [21719235]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
не понял...
30 окт 18, 16:49    [21719236]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Ну т.е. написать СВОИ методы?
30 окт 18, 16:50    [21719237]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
zinpub
Member

Откуда:
Сообщений: 272
X11,

Ну да, пишешь GetHashCode - с Upper вначале - и получаешь регистронезависимость
30 окт 18, 16:52    [21719239]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
zinpub
Member

Откуда:
Сообщений: 272
Откуда то со Стэка...

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;
30 окт 18, 16:53    [21719244]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Где-то я стормозил.
Пишу:

объявил
type
  TCustomEqualityComparer = class(TEqualityComparer<string>)
  public
    function Equals(const Left, Right: string): Boolean; override;
    function GetHashCode(const Value: string): Integer; override;
  end;



реализация
{ 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;


Создаю словарь:
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 - не выполняется
30 окт 18, 17:07    [21719269]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
zinpub
Member

Откуда:
Сообщений: 272
X11,

CustomEqualityComparer - а создать его?
30 окт 18, 17:12    [21719279]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
точно

но... вижу, что моя GetHashCode выполняется, а моя Equals - нет.
30 окт 18, 17:20    [21719291]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
zinpub
Member

Откуда:
Сообщений: 272
X11,

А нафига, он кстати при добавлении будет выполняться ?
30 окт 18, 17:22    [21719303]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
GetHashCode выполняется, когда в словарь добавляю
DicCmdRows.Add('/start', PcmdStart);
DicCmdRows.Add('/время', PcmdShowTime);


а потом ещё, когда приходит боту команда "/Start"
30 окт 18, 17:25    [21719312]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
zinpub
X11,

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


а я откуда знаю? я ж его не заставляю, видать разработчики дельфи так захотели
30 окт 18, 17:26    [21719315]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Пришлось переделать так:

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;


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

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

Так правильно?
30 окт 18, 17:28    [21719323]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Пошарился, по сети, вижу, что я не одинок
http://www.devsuperpage.com/search/Articles.aspx?G=2&ArtID=28868

тоже кто-то переделал GetHashCode
30 окт 18, 17:29    [21719324]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
zinpub
Member

Откуда:
Сообщений: 272
X11,

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

Единственное хэш, как-то по другому надо теперь получать THash.BobJenkins.GetHash - как-то так...
30 окт 18, 17:29    [21719326]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Странно, что разработчики Delphi не сделали это сами, заранее.
30 окт 18, 17:30    [21719327]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
zinpub
X11,

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

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


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

'BobJenkinsHash' is deprecated: 'Use System.Hash.THashBobJenkins.GetHashValue'
30 окт 18, 17:32    [21719328]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
JaDi
Member

Откуда: Сызрань, Россия
Сообщений: 3845
X11,

Всё проще -- AnsiLowerCase перед добавлением/проверкой...
30 окт 18, 17:34    [21719333]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
JaDi
X11,

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


Настоящие герои всегда идут в обход
30 окт 18, 17:36    [21719341]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
JaDi
Member

Откуда: Сызрань, Россия
Сообщений: 3845
X11
Странно, что разработчики Delphi не сделали это сами, заранее.

А не стоит ли задуматься, что если ради какой-то своей фичи приходится ТАК извращаться и лезть в самые дебри -- то что-то пошло не так и надо пересмотреть подход к решению своей проблемы?
30 окт 18, 17:37    [21719342]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
zinpub
Member

Откуда:
Сообщений: 272
JaDi,

Это если не надо хранить оригинал
30 окт 18, 17:37    [21719343]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
Valery_B
Member

Откуда: Москва
Сообщений: 1807
Я делаю примерно так же, как и пример у Jadi +-
Только пример с формами (TForm).
+
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.

30 окт 18, 17:46    [21719358]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
Valery_B
Member

Откуда: Москва
Сообщений: 1807
X11
Не смог найти.
Поэтому вопрос.
А есть ли у Delphi встроенный способ создать словарь с регистронезависимым (case-insensitive) поиском по ключам?
Или нужно самому пилить?


 FClassesList := TDictionary<string, TForm>.Create(TIStringComparer.Ordinal);
30 окт 18, 17:52    [21719368]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Valery_B
X11
Не смог найти.
Поэтому вопрос.
А есть ли у Delphi встроенный способ создать словарь с регистронезависимым (case-insensitive) поиском по ключам?
Или нужно самому пилить?


 FClassesList := TDictionary<string, TForm>.Create(TIStringComparer.Ordinal);


я пробовал Ordinal - он регистразависим
30 окт 18, 19:41    [21719463]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1161
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'];
30 окт 18, 20:29    [21719501]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
white_nigger
Member

Откуда: Тула
Сообщений: 1928
X11,
смотри создание TPersistentClassDictionary в System.Classes.pas
или как тебе Valery_B посоветовал
30 окт 18, 21:21    [21719542]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
X-Cite,

не это почти то же самое, только с использованием лямбла-функций?
31 окт 18, 09:31    [21719846]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Valery_B
X11
Не смог найти.
Поэтому вопрос.
А есть ли у Delphi встроенный способ создать словарь с регистронезависимым (case-insensitive) поиском по ключам?
Или нужно самому пилить?


 FClassesList := TDictionary<string, TForm>.Create(TIStringComparer.Ordinal);


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

ок. наверное я где-то ещё обшибся.
спасибо
31 окт 18, 09:33    [21719848]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
Valery_B
Member

Откуда: Москва
Сообщений: 1807
X11
Valery_B
пропущено...


 FClassesList := TDictionary<string, TForm>.Create(TIStringComparer.Ordinal);


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


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;


К сообщению приложен файл. Размер - 21Kb
31 окт 18, 09:46    [21719860]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
zinpub
Member

Откуда:
Сообщений: 272


К сообщению приложен файл. Размер - 47Kb
31 окт 18, 09:54    [21719868]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
Valery_B
Member

Откуда: Москва
Сообщений: 1807
X11
Не понял... недавно использовал именно TIStringComparer.Ordinal и регистронезависимость не работала, а теперь работает

ок. наверное я где-то ещё обшибся.
спасибо

Ок.
По теме - не создавай функции/процедуры, а создавай классы.
Как было предложено ранее:
TBot  = class
...
end;
И их производные.
TSuperBot = class(TBot)
TKillerBot = class (TBot)

С таким кодом, будет гораздо проще работать и понимать.
Ты всегда можешь инициализировать класс так, как нужно тебе в конкретном случае.
31 окт 18, 09:57    [21719871]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Valery_B, да верю, я верю, уже перепроверил и убедился, что ты прав.

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

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


Картинка с другого сайта.
31 окт 18, 09:57    [21719872]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
X11
Не понял... недавно использовал именно TIStringComparer.Ordinal и регистронезависимость не работала, а теперь работает


Вру. Скорей всего я "TIStringComparer.Ordinal" хотел использовать, но не использовал, т.к. полез в справку, а там написано, что - зависит от регистра.
31 окт 18, 09:59    [21719875]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
Valery_B
Member

Откуда: Москва
Сообщений: 1807
X11
Я не знаю, что не так у меня было в пенрвый раз, почему не сработало. Я именно с TIStringComparer.Ordinal и начинал.

Такое у всех было)
Потому что ты делал это в большом проекте, и тестировал не в тех местах.
31 окт 18, 09:59    [21719877]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
JaDi
Member

Откуда: Сызрань, Россия
Сообщений: 3845
Лол, чудаки из эмбы свою справку так и не исправили -- до сих пор стоит текст про регистрозависимость:
http://docwiki.embarcadero.com/Libraries/Tokyo/en/System.Generics.Defaults.TIStringComparer.Ordinal

Тогда как в коде там AnsiLowerCase используется (т.е. не зависит от регистра):
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;
31 окт 18, 10:05    [21719886]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
zinpub
Member

Откуда:
Сообщений: 272
JaDi,

Ха, прикольно... Ребята жгут
31 окт 18, 10:14    [21719900]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
white_nigger
Member

Откуда: Тула
Сообщений: 1928
Кстати, если правильно помню, то использование такого словаря приводило к утечке памяти в одной из версий делфи
31 окт 18, 12:08    [21720098]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
Василий 2
Member

Откуда:
Сообщений: 234
JaDi
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;

Неужели две конвертации строк и бинарное сравнение быстрее непосредственного сравнения без учета регистра?
1 ноя 18, 10:26    [21721160]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
zinpub
Member

Откуда:
Сообщений: 272
Василий 2,

А что такое "непосредственное сравнения без учета регистра" ?
1 ноя 18, 10:40    [21721190]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
Василий 2
Member

Откуда:
Сообщений: 234
zinpub
Василий 2,

А что такое "непосредственное сравнения без учета регистра" ?

AnsiCompare с параметром
1 ноя 18, 14:21    [21721538]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
zinpub
Member

Откуда:
Сообщений: 272
Василий 2,

Да так быстрее, но CompareString - на мой взгляд, несколько не подходит в данном случае, тк результат сравнения не всегда однозачен...
1 ноя 18, 14:38    [21721583]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Немножко переделал. Теперь в словаре живут не ссылки процедуры, а строковые имена процедур.
Кто работал с RTTI, пожалуйста, подскажите, как выполнить метод по имени.
Читаю про TRttiContext, TRTTIType и TRTTIMethod примеры и справку, не могу врубиться.

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;

спасибо
7 ноя 18, 17:40    [21727184]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
Tactical Nuclear Penguin
Member

Откуда: холодно тут
Сообщений: 2592
rmethod.Invoke ?
7 ноя 18, 17:52    [21727195]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
вроде бы, но не разобрался как правильно ему передать параметры
7 ноя 18, 19:29    [21727297]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1161
rmethod.Invoke(Self, TArray<TValue>.Create(TValue.From<string>('AMessage.Text'), TValue.From<string>('AMessage.From')));

В вашем случае скорее всего так...
7 ноя 18, 20:36    [21727373]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1161
Ошибся выше, надо так:
rmethod.Invoke(Self, TArray<TValue>.Create(TValue.From<string>(AMessage.Text), TValue.From<string>(AMessage.From)));
7 ноя 18, 20:37    [21727376]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
Гирлионайльдо
Member

Откуда:
Сообщений: 268
Весело настанет когда, пользователь случайно, а может и не случайно используя уже какой - то метод, вызовет Invoke с типом объявленным посредством type тот же type myString = string и не сможет вызвать с этим аргументом
7 ноя 18, 22:46    [21727488]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Гирлионайльдо, не понял...
8 ноя 18, 09:23    [21727705]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Проблема в том, что
rmethod := rtype.GetMethod(BotProcName);

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

Картинка с другого сайта.

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


Снимок сделан после выполнения строки:
rmethod := rtype.GetMethod(BotProcName);
8 ноя 18, 09:32    [21727714]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
тут есть похожий пример
https://forum.antichat.ru/threads/362046/
но тут даже целый цикл запилили...
Ну ОК, я тоже сделал:
    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'.
Как так?

Может, дело не в бабине?
8 ноя 18, 10:02    [21727758]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1161
Если метод не public или published то по умолчанию их в RTTI нет, вроде бы...
Из-за в System
{ 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)}


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

или
{$RTTI EXPLICIT
      METHODS([vcPrivate, vcPublished])}

не помню что переопределяет...
8 ноя 18, 11:49    [21727940]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
X-Cite
Если метод не public или published то по умолчанию их в RTTI нет, вроде бы..


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

Ок, ладно, но почему условие срабатывает, если имя метода не находит?
8 ноя 18, 11:59    [21727965]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
Cobalt747
Member

Откуда:
Сообщений: 2037
X11
Ок, ладно, но почему условие срабатывает, если имя метода не находит?

Скорее всего. косяк отладчика.
Сделай вывод в лог, наверняка там будет все правильно.
8 ноя 18, 13:22    [21728063]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
нифигасебе косяк... это довольно серьёзноКартинка с другого сайта.
8 ноя 18, 13:30    [21728073]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Ок, я перенес нужные методы в public.
Теперь нужный метод найден, но...

exception
Project raised exception class EInvalidCast with message 'Invalid class typecast'

на вот этой строке
rmethod.Invoke(Self, TArray<TValue>.Create(TValue.From<string>(AMessage.Text), TValue.From<string>(AMessage.From.ID.ToString)));
8 ноя 18, 13:34    [21728085]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
JaDi
Member

Откуда: Сызрань, Россия
Сообщений: 3845
Наглядное пособие переинженеринга -- как вот эта простая конструкция:
bot.Callback(params);

превратилась вот в этого монстра:
rmethod.Invoke(Self, TArray<TValue>.Create(TValue.From<string>(AMessage.Text), TValue.From<string>(AMessage.From.ID.ToString)));
8 ноя 18, 13:38    [21728091]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
JaDi, сперва нужно сделать, чтобы "заработало" пусть даже и в виде монстра, а потом уже и причесать можно.
8 ноя 18, 13:46    [21728099]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1161
X11
Ок, я перенес нужные методы в public.
Теперь нужный метод найден, но...

exception
Project raised exception class EInvalidCast with message 'Invalid class typecast'

на вот этой строке
rmethod.Invoke(Self, TArray<TValue>.Create(TValue.From<string>(AMessage.Text), TValue.From<string>(AMessage.From.ID.ToString)));

У вас в вашем методе, оба параметра типа string ?
Передавайте те значения параметров и того типа, что в методе
8 ноя 18, 14:03    [21728128]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
JaDi
Member

Откуда: Сызрань, Россия
Сообщений: 3845
X11,

видимо, я что-то пропустил -- чем не устроила универсальная модель, когда на вход обработчику подаются параметры в универсальном виде типа строки/списка/json'а?
8 ноя 18, 14:08    [21728135]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 24486
JaDi
чем не устроила универсальная модель, когда на вход обработчику подаются параметры в универсальном виде типа строки/списка/json'а?

Поддерживаю.
Первое слово всегда команда, остальное, если есть, параметры.
В обработчик команды передаются только параметры.

К чему эти огороды?
8 ноя 18, 14:11    [21728142]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
X-Cite
У вас в вашем методе, оба параметра типа string ?


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

PcmdStart(const sCmd: string; User: ItgUser);
8 ноя 18, 14:26    [21728168]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
переделал на
From: ItgUser

    if Assigned(rmethod) then
      rmethod.Invoke(rmethod.CodeAddress, TArray<TValue>.Create(TValue.From<string>(AMessage.Text),
                                              TValue.From<ItgUser>(AMessage.From)));


всё равно Invalid class typecast
8 ноя 18, 14:31    [21728181]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
wadman
JaDi
чем не устроила универсальная модель, когда на вход обработчику подаются параметры в универсальном виде типа строки/списка/json'а?

Поддерживаю.
Первое слово всегда команда, остальное, если есть, параметры.
В обработчик команды передаются только параметры.

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


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

Вроде бы я никуда не отклонялся. Пытаюсь сделать универсальную масштабируемую модель.
8 ноя 18, 14:35    [21728187]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 24486
X11
И тоже нужно обработать.

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

Ну или пример "какого-нибудь" текста? Из жизни, по возможности.
8 ноя 18, 14:37    [21728192]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
wadman, работают ещё как Картинка с другого сайта.

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

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

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

ну как-то так
8 ноя 18, 14:47    [21728216]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
wadman
Ну или пример "какого-нибудь" текста? Из жизни, по возможности.


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

Распарсил текст, разбил на части: тип, цена, район, площади, коробка передач, размер трусов, фокусное расстояние объектива и т.д.
И не надо никаких команд.
8 ноя 18, 14:50    [21728222]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
wadman
Member

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

Тогда и не нужно никакого списка с командами и методами?

Изначально-то тема касалась именно списка команд. А тут уже и ИИ вылез и скоро тело прикрутим. Женское. :)
8 ноя 18, 14:54    [21728236]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
JaDi
Member

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

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


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

Менеджер определяет, что за команда и отправляет ее нужному боту:
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;
8 ноя 18, 14:55    [21728239]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
wadman
Тогда и не нужно никакого списка с командами и методами?


это уже сам программист решает, мне - нужно
8 ноя 18, 15:01    [21728251]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
JaDi
Было еще в самом начале 21717308

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


да не проблема....
проблема в том, когда пользователь скажет, что "я хочу, чтобы бот выполняет вот это запрограммированное действие на три похожие команды" и что, мне каждый раз добавлять и перекомпилировать?
8 ноя 18, 15:03    [21728256]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1161
X11
переделал на
From: ItgUser

    if Assigned(rmethod) then
      rmethod.Invoke(rmethod.CodeAddress, TArray<TValue>.Create(TValue.From<string>(AMessage.Text),
                                              TValue.From<ItgUser>(AMessage.From)));


всё равно Invalid class typecast


rmethod.Invoke(rmethod.CodeAddress

Вы читали хелп? Что идет первым параметром?
Если у вас метод класса, то надо передать Указатель на экземпляр класса в контексте которого будет вызываться метод
Если у вас классовый метод, то надо передать сам класс
8 ноя 18, 15:06    [21728264]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 24486
X11
и что, мне каждый раз добавлять и перекомпилировать?

Есть иные способы? Разве что на сервер отправлять, вот и весь бот.
8 ноя 18, 15:11    [21728280]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
X-Cite, т.е. просто self вписать?
8 ноя 18, 15:15    [21728285]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
X-Cite, спасибо, теперь дошло

    if Assigned(rmethod) then
      rmethod.Invoke(self, TArray<TValue>.Create(TValue.From<string>(AMessage.Text),
                                              TValue.From<ItgUser>(AMessage.From)));
8 ноя 18, 15:16    [21728289]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
wadman,

да, я же уже запилил.
8 ноя 18, 15:17    [21728290]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
wadman, на предыдущих страницах я показывал скрины. Идея в том, что у дерева есть корневые узлы - это действия, к которым привязаны методы (процедуры), т.е. то, что умеет бот. Дочерние узлы - это команды. Команды пользователь может редактировать (добавлять, удалять, переименовывать, отключать, включать)

Картинка с другого сайта.

т.е. можно сделать так, что если первое или единственное слово, полученное ботом, будет не "/старт", а "старт", то его тоже можно обработать, так же, как и "/start". Для этого пользователь просто добавляет узел см текстом "старт" в качестве дочернего к корневому "СТАРТ".
8 ноя 18, 15:24    [21728302]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
JaDi
Member

Откуда: Сызрань, Россия
Сообщений: 3845
X11
JaDi
Было еще в самом начале 21717308

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


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

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

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


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

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

На вход бот1 получает список и дальше сам парсит. Например, команда, команда и параметр.
Команда: "скачай", параметр: "лалала"
Команда: "преобразуй в", параметр: "какака"
Команда: "сожми", дефолтный параметр: результат с последней команды
Команда: "отправь ссылку на", параметр: "лулулу"
8 ноя 18, 15:27    [21728306]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
JaDi
И всё это реализуется как раз в одном месте -- в менеджере, который должен распарсить сообщение пользователя и дальше передать в обработку (одному, нескольким ботам или сразу пачкой команд).


ну я вроде бы так и делаю
8 ноя 18, 15:31    [21728312]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
JaDi
Пример: "бот1 скачай лалала преобразуй в какака сожми отправь ссылку на лулулу"
Команда: "бот1"
Параметры: "скачай лалала.... на лулулу".


да, я так примерно и делаю
8 ноя 18, 15:34    [21728313]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
Гирлионайльдо
Member

Откуда:
Сообщений: 268
А точно, баг в Invoke происходит из за Pointer

  MyWide = type PWideChar;


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

+
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
9 ноя 18, 01:50    [21728939]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1161
Так типы то якобы разные...
Вот так работает...
Не надо совмещать похожие но не одинаковые типы...
TRttiContext.Create.GetType(ClassType).GetMethod('badCall').Invoke(Self, [TValue.From<MyWide>(MyWide(PWideChar('Hello World')))])
9 ноя 18, 11:53    [21729208]     Ответить | Цитировать Сообщить модератору
 Re: Дженерики: передача процедуры в качестве параметра  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12480
Гирлионайльдо, да, я читал твою тему :)
9 ноя 18, 12:59    [21729340]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: 1 2 3 4 5      [все]
Все форумы / Delphi Ответить