Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
 Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
Инсомниа
Guest
Есть некоторое множество функций. Функции чужие и их самих корректировать не получится. Функции эти имеют одно общее - они возвращают код ошибки (ну или успех).
В большинстве случаев надо вызывать ряд функций друг за другом, но продолжать только если у предыдущих был успех.
А также было бы крайне хорошо получать информацию на каком именно "шаге" (и по возможности в какой функции) возникла проблема.

Можно городить жуткую лестницу:
+
FuncResult:=FuncA(a, b, c, ...);
if FuncResult=GoodResult then
    begin
    FuncResult:=FuncB(1, 2, 3, ...);
    if FuncResult=GoodResult then
        begin
        FuncResult:=FuncC(@a, -2, 'test', ...);
        if FuncResult=GoodResult then
            begin
            ...
                  ... ShowMessage('Done!');
            end
        else
            ShowMessage('Error on step 3, in function FuncC, with error code '+FuncResult);
        end
    else
        ShowMessage('Error on step 2, in function FuncB, with error code '+FuncResult);
    end
else
    ShowMessage('Error on step 1, in function FuncA, with error code '+FuncResult);

Можно городить подряд, но с кучей проверок и досрочными выходами:
+
FuncResult:=FuncA(a, b, c, ...);
if FuncResult<>GoodResult then
    begin
    ShowMessage('Error on step 1, in function FuncA, with error code '+FuncResult);
    exit;
    end;
FuncResult:=FuncB(1, 2, 3, ...);
if FuncResult<>GoodResult then
    begin
    ShowMessage('Error on step 2, in function FuncB, with error code '+FuncResult);
    exit;
    end;
FuncResult:=FuncC(@a, -2, 'test', ...);
if FuncResult<>GoodResult then
    begin
    ShowMessage('Error on step 3, in function FuncC, with error code '+FuncResult);
    exit;
    end;
...
ShowMessage('Done!');

А если у меня "цепочка" не из трёх, а их трёх десятков функций? И мне может понадобится их оперативно менять или переставлять местами или временно закоментировать какие-то?

Не оборачивать же каждую чужую функцию чтоб бросала исключение? Полагаю будет как-то неудобно и затратно... Нужна какая-то общая обёртка или какой-то совершенно иной подход...
Подскажите пожалуйста - что-то можно попробовать?


Пока крутится на уме а не попытаться ли соорудить что-то подобное:
+
SequentialSteps.Call(@FuncA, [a, b, c, ...]);
SequentialSteps.Call(@FuncB, [1, 2, 3, ...]);
SequentialSteps.Call(@FuncC, [@a, -2, 'test', ...]);
...
if SequentialSteps.Failed then
    ShowMessage('Error on step '+SequentialSteps.Step+', in function '+SequentialSteps.Func+', with error code '+SequentialSteps.Error);
else
    ShowMessage('Done!');
Но ума не приложу как такое реализовать...
2 июл 18, 17:24    [21537296]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
Dimitry Sibiryakov
Member

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

Инсомниа
А также было бы крайне хорошо получать информацию на каком именно "шаге" (и по возможности
в какой функции) возникла проблема.

Если избавиться от этого желания, то можно написать так:
Result := FuncA() and FuncB() and FuncC();

Если не избавляться, то так:
if not FuncA then raise Exception.Create('FuncA error');
if not FuncB then raise Exception.Create('FuncB error');
if not FuncC then raise Exception.Create('FuncC error');

Posted via ActualForum NNTP Server 1.5

2 июл 18, 17:28    [21537312]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 24160
Инсомниа
Но ума не приложу как такое реализовать...

Assert
2 июл 18, 17:35    [21537341]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
Инсомниа
Guest
Писать тридцать функций чрез "and"? К тому же они не boolean возвращают, а некие собственные коды.
+
Result:=
    (FuncA(...)=GoodResult)
and (FuncB(...)=GoodResult)
and (FuncC(...)=GoodResult)
and ...
;
Да и если функция FuncB() или FuncC() завершилась неуспешно - то остальные двадцать+ уже не надо и пытаться вызывать, бесполезно.

А ещё код ошибки как бы надо бы сообщать:
+
FuncResult:=FuncA(...);
if FuncResult<>GoodResult then raise Exception.Create('FuncA error '+FuncResult);
FuncResult:=FuncB(...);
if FuncResult<>GoodResult then raise Exception.Create('FuncB error '+FuncResult);
FuncResult:=FuncC(...);
if FuncResult<>GoodResult then raise Exception.Create('FuncC error '+FuncResult);

Как-то не сильно улучшилось в плане удобности написания/редактирования/чтения... Да и строковые константы вручную писать опять же...
2 июл 18, 17:47    [21537380]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 10492
Инсомниа
Да и если функция FuncB() или FuncC() завершилась неуспешно - то остальные двадцать+ уже не надо и пытаться вызывать, бесполезно.
Они и не вызовутся
2 июл 18, 17:49    [21537386]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 10492
Инсомниа
не boolean возвращают, а некие собственные коды.
Я пишу так (для сокетов)
procedure RaiseSocketError(AError: Integer);
var
  LExcept: ESocketError;
begin
  LExcept := ESocketError.CreateResFmt(@SOSError, [AError, SysErrorMessage(AError), '']);
  LExcept.ErrorCode := AError;
  raise LExcept;
end;

function SockCheck(ARes: Integer; ASkipNonBlock: Boolean): Integer;
var
  LError: Integer;
begin
  if ARes = SOCKET_ERROR then begin
    LError := WSAGetLastError;
    if not ASkipNonBlock or (LError <> WSAEWOULDBLOCK) then
      RaiseSocketError(LError);
  end;
  Result := ARes;
end;
и использование
  Result := SockCheck(Winapi.Winsock2.socket(AF_INET, AType, AProtocol));
  try
    SockCheck(bind(Result, sockaddr(LAddr), SizeOf(LAddr)));
  except
    on E: Exception do begin
      closesocket(Result);
      raise;
    end;
  end;
О том, где возникла ошибка узнаю на основании анализа стека исключения
2 июл 18, 17:54    [21537406]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
Dimitry Sibiryakov
Member

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

Инсомниа
Да и если функция FuncB() или FuncC() завершилась неуспешно - то остальные двадцать+ уже
не надо и пытаться вызывать, бесполезно.

Так они и не вызовутся: RTFM shortcut boolean evaluation.

Инсомниа
Как-то не сильно улучшилось в плане удобности
написания/редактирования/чтения... Да и
строковые константы вручную писать опять же...

А в чём проблема? Боишься пальцы стереть?
Procedure Call(Result: integer; GoodResult: Integer; const Where: String)
begin
   if Result <> GoodResult then
     raise Exception.Create(Where + ' returned ' + Result);
end;

Call(FuncA(1,2,3), 0, 'FuncA');
Call(FuncB(a,b,c), 0, 'FuncB');
Call(FuncC(x,y,z), 0, 'FuncC');

Посмотри как сделано и используется RaiseLastOSError, например.

Posted via ActualForum NNTP Server 1.5

2 июл 18, 17:59    [21537430]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
YuRock
Member

Откуда: Донецк
Сообщений: 3527
Инсомниа
А если у меня "цепочка" не из трёх, а их трёх десятков функций?
Если 3 десятка - то 3 десятка строк call( func(..) ).
Если переменное кол-во - то можно подумать о двумерном массиве указателей на функции и структуры их параметров. И в цикле вызывать, пока GoodResult возвращают.
2 июл 18, 23:10    [21538060]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 24160
Dimitry Sibiryakov
Так они и не вызовутся: RTFM shortcut boolean evaluation.

Может он отключил, пока руки чесались и теперь переживает. :)
3 июл 18, 08:33    [21538344]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
Василий 2
Member

Откуда:
Сообщений: 192
Как-то так. Не проверял.
type
  TFuncRef = reference to function: Integer;

procedure CallFuncs(const Funcs: array of TFuncRef);
var Func: TFuncRef;
begin
  for Func in Funcs do
    if Func() <> SUCCESS_CODE then
       Break;
end;

var param1, param2, param3...

CallFuncs([
  function (): Integer
  begin
     Result := DoSmth1(param3, param1);
  end,

  function (): Integer
  begin
     Result := DoSmth2(param1, param2);
  end,

  function (): Integer
  begin
     Result := DoSmth1(param1, param3);
  end
]);
3 июл 18, 10:02    [21538500]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
schi
Member

Откуда: Москва
Сообщений: 2578
Василий 2
Как-то так. Не проверял.
type
  TFuncRef = reference to function: Integer;

procedure CallFuncs(const Funcs: array of TFuncRef);
var Func: TFuncRef;
begin
  for Func in Funcs do
    if Func() <> SUCCESS_CODE then
       Break;
end;

var param1, param2, param3...

CallFuncs([
  function (): Integer
  begin
     Result := DoSmth1(param3, param1);
  end,

  function (): Integer
  begin
     Result := DoSmth2(param1, param2);
  end,

  function (): Integer
  begin
     Result := DoSmth1(param1, param3);
  end
]);


Вы насоветуете. А потом кому-то этот феерический трындец сопровождать.
3 июл 18, 10:36    [21538575]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
Василий 2
Member

Откуда:
Сообщений: 192
schi, это уже за рамками вопроса...
Вообще самый лучший вариант это
автор
Call(FuncA(1,2,3), 0, 'FuncA');
Call(FuncB(a,b,c), 0, 'FuncB');
Call(FuncC(x,y,z), 0, 'FuncC');
3 июл 18, 16:40    [21539843]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
AX-Class
Member

Откуда:
Сообщений: 126
Инсомниа,

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

Если нужно динамическое формирование цепочки, то можно обернуть функции в объекты:
+
unit Unit54;

interface

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

type
  TForm54 = class(TForm)
    Button1: TButton;
    CheckBox1: TCheckBox;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TFuncItem = class
  private
    FStopCode: integer;
  strict protected
    constructor Create(AStopCode: integer);
  public
    function Exec: integer; virtual; abstract;
    procedure HandleStop; virtual; abstract;
  end;

  TFuncA = class(TFuncItem)
  private
    FA: integer;
    FB: integer;
    FC: integer;
  public
    constructor Create(AStopCode, AA, AB, AC: integer); overload;
    function Exec: integer; override;
    procedure HandleStop; override;
  end;

  TFuncB = class(TFuncA)
  public
    function Exec: integer; override;
    procedure HandleStop; override;
  end;

  TFuncC = class(TFuncItem)
  private
    FA: integer;
    FB: integer;
  public
    constructor Create(AStopCode, AA, AB: integer); overload;
    function Exec: integer; override;
    procedure HandleStop; override;
  end;

  TScript = class(TObjectList<TFuncItem>)
  public
    function Exec: TFuncItem;
  end;

function FuncA(a, b, c: integer): integer;

function FuncB(a, b, c: integer): integer;

function FuncC(a, b: integer): integer;

var
  Form54: TForm54;

implementation

{$R *.dfm}

function FuncA(a, b, c: integer): integer;
begin
  Result := 0;
end;

function FuncB(a, b, c: integer): integer;
begin
  Result := 0;
end;

function FuncC(a, b: integer): integer;
begin
  Result := 0;
end;

constructor TFuncA.Create(AStopCode, AA, AB, AC: integer);
begin
  inherited Create(AStopCode);
  FA := AA;
  FB := AB;
  FC := AC;
end;

function TFuncA.Exec: integer;
begin
  Result := FuncA(FA, FB, FC);
end;

procedure TFuncA.HandleStop;
begin
  ShowMessage('TFuncA');
end;

function TFuncB.Exec: integer;
begin
  Result := FuncB(FA, FB, FC);
end;

procedure TFuncB.HandleStop;
begin
  Beep;
end;

function TScript.Exec: TFuncItem;
var
  F: TFuncItem;
  Return: integer;
begin
  for F in Self do
    if F.Exec = F.FStopCode then
      Exit(F);
  Result := nil;
end;

constructor TFuncItem.Create(AStopCode: integer);
begin
  FStopCode := AStopCode;
end;

constructor TFuncC.Create(AStopCode, AA, AB: integer);
begin
  inherited Create(AStopCode);
  FA := AA;
  FB := AB;
end;

function TFuncC.Exec: integer;
begin
  Result := FuncC(FA, FB);
end;

procedure TFuncC.HandleStop;
begin
  ShowMessage('TFuncC');
end;

procedure TForm54.Button1Click(Sender: TObject);
var
  Script: TScript;
  FuncItem: TFuncItem;
begin
  Script := TScript.Create(True);
  try
    Script.Add(TFuncA.Create(-1, 6, 6, 6));
    if CheckBox1.Checked then
      Script.Add(TFuncA.Create(0, 6, 6, 6));
    Script.Add(TFuncB.Create(0, 7, 7, 7));
    Script.Add(TFuncC.Create(0, 1, 1));
    FuncItem := Script.Exec;
    if Assigned(FuncItem) then
      FuncItem.HandleStop;
  finally
    Script.Free;
  end;
end;

end.

С MMX Code Explorer 30 функций обернуть - несколько мин. Виртуальность, одинаковые сообщения, одинаковая сигнатура итд.
5 июл 18, 23:46    [21547729]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
AX-Class
Member

Откуда:
Сообщений: 126
Букв можно сократить. Что-то вроде

Script.AddFunc3(CERROR_CODE, FuncA, 6, 6, 6, 'Error Message');
6 июл 18, 00:46    [21547796]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
AX-Class
Member

Откуда:
Сообщений: 126
Или, даже

Script.AddFunc<integer, string>(CERROR_CODE, FuncW, 888, 'String Param', 'Error Message');
6 июл 18, 00:47    [21547797]     Ответить | Цитировать Сообщить модератору
 Re: Как бы вызывать цепочку функций с проверкой каждого шага на возврат кода ошибки?  [new]
Василий 2
Member

Откуда:
Сообщений: 192
Или
Exec([
  TFuncA.Create(0, 6, 6, 6),
  TFuncB.Create(...),
  ...
])

Тогда и Script не нужен

А если объявить такой класс и функцию
TExecutor = class
  class function Exec(Func): TExecutor;
end;

class function TExecutor.Exec(Func): TExecutor;
begin
  DoExec(Func);
  Result := Self;
end;

function Exec(Func): TExecutor;
begin
  Result := TExecutor.Exec(Func);
end;


то можно делать в модной манере цепочкования
  Exec(TFuncA.Create(0, 6, 6, 6)).
  Exec(TFuncB.Create(...)).
  Exec(TFuncC.Create(...)).
  ...
6 июл 18, 11:36    [21548829]     Ответить | Цитировать Сообщить модератору
Все форумы / Delphi Ответить