Page 1 of 2 12 Last
  • Jump to page:
    #1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2012
    Posts
    23
    Rep Power
    0

    Invalid pointer problem


    I am getting an "Invalid pointer operation" exception, but it isn't clear why I am getting this exception. Basically, I call a function from my main unit, it computes the value in the function alright, but as it is exiting the function to go back to the main unit, it is raising this exception. I am not trying to free anything or trying to invoking something that might not exist. Don't know what is going on. Here is a snippet of the code:

    Main unit:
    ...
    EstimatedValues := Estimate(A, B, C, etc);
    ShowMessage(EstimatedValues);
    ...


    Function TForm1.Estimate(A, B, C, etc):string;
    ...
    begin
    try
    ...
    finally
    Result := ...;
    end;
    end;


    The code in the function works fine (and is accurate) even at the "Results := ..." statement. In fact I can see the Result when I hover over it. When it goes to the last "end," though, and I step over it, it raises this exception.

    What could be going on? I am very much a beginner in Delphi, so it is probably something pretty basic, but I am unable to find the answer. So please be patient.

    Thanks!
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    354
    Rep Power
    7
    Originally Posted by llaama2012
    I am getting an "Invalid pointer operation" exception, but it isn't clear why I am getting this exception. Basically, I call a function from my main unit, it computes the value in the function alright, but as it is exiting the function to go back to the main unit, it is raising this exception. I am not trying to free anything or trying to invoking something that might not exist. Don't know what is going on. Here is a snippet of the code:

    Main unit:
    ...
    EstimatedValues := Estimate(A, B, C, etc);
    ShowMessage(EstimatedValues);
    ...


    Function TForm1.Estimate(A, B, C, etc):string;
    ...
    begin
    try
    ...
    finally
    Result := ...;
    end;
    end;


    The code in the function works fine (and is accurate) even at the "Results := ..." statement. In fact I can see the Result when I hover over it. When it goes to the last "end," though, and I step over it, it raises this exception.

    What could be going on? I am very much a beginner in Delphi, so it is probably something pretty basic, but I am unable to find the answer. So please be patient.

    Thanks!
    In this type of case, you really ought to post your actual code. Obviously in the example you posted, there is nothing wrong, but it isn't enough for us to glean what the problem could be.

    Please include the code of your function and the entire procedure of where you call it....
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2012
    Posts
    23
    Rep Power
    0
    Thanks. I was hesitant to do so because I am still too new to include an attachment. Also, i was worried that there might be too much code. Since I don't want to leave out something that is potentially important, I am including all of the code (a little self-consciously, I might add ). But since I have been muddling along in Delphi, other pointers (no pun intended) to improve my code are most welcome too!

    Basically, I want to compute two parameters for a non-linear regression by means of iterating over a range of values for each of the two parameters. I will then pick those values that correspond to the lowest residual sum of squares as the best (least squares) estimates.

    Here is my code (in its entirety). I have highlighted the lines where I get the error. Sorry about the lengthy code and thanks in advance for any help! Also, please note that I get the same error message when I have included "ShareMem" in my uses clause. I have also downloaded sometjhing called "FastShareMem" that was supposed to mitigate some issues that ShareMem could not. But I get the same error message.

    ----------------------------
    unit HillSlope;

    interface

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

    type
    THillSlopeRegr = class(TForm)
    HelpCmdBtn: TButton;
    OpenFileCmdBtn: TButton;
    CloseFormCmdBtn: TButton;
    ComputeCmdBtn: TButton;
    OpenFileEditBx: TEdit;
    OpenDialog1: TOpenDialog;
    Label1: TLabel;
    YesHeadersRadBtn: TRadioButton;
    NoHeadersRadBtn: TRadioButton;
    GroupBox1: TGroupBox;
    Label2: TLabel;
    Label3: TLabel;
    EC50_LowSpinEdit: TSpinEdit;
    EC50_HighSpinEdit: TSpinEdit;
    Hill_LowSpinEdit: TSpinEdit;
    Hill_HighSpinEdit: TSpinEdit;
    Label4: TLabel;
    Label5: TLabel;
    procedure HelpCmdBtnClick(Sender: TObject);
    procedure CloseFormCmdBtnClick(Sender: TObject);
    procedure OpenFileCmdBtnClick(Sender: TObject);
    procedure ComputeCmdBtnClick(Sender: TObject);
    function Estimate(N_EC, N_Hill: integer; X_Array, Y_Array: array of double; EC50_Low, EC50_High, Hill_Low, Hill_High: double; EC50_Incr, Hill_Incr: double): string;
    private
    { Private declarations }
    public
    { Public declarations }
    end;

    var
    HillSlopeRegr: THillSlopeRegr;

    implementation

    {$R *.dfm}




    procedure THillSlopeRegr.HelpCmdBtnClick(Sender: TObject);
    var
    S: string;
    begin
    S := 'This program computes the Hillslope regression for the pair of' + #13#10 +
    'input variables, X and Y, where X is the percent animals that ' + #13#10 +
    'have shown the required response, and Y is the dose used.' + #13#10 +
    '' + #13#10 +
    'INSTRUCTIONS:'+ #13#10 +
    '1. Upload the input data as a comma-delimited (.csv) file, with' + #13#10 +
    ' two columns, X and Y, and press the "Compute" button. ' + #13#10 +
    '2. Wait for the message "Done!"' + #13#10 +
    '3. The results will be in a .csv file, and will be saved in the' + #13#10 +
    ' same folder as the input file.' + #13#10 +
    '4. The output file will contain the input variables, X and Y,' + #13#10 +
    ' the expected Y values (Y-hat), the lower and upper limits' + #13#10 +
    ' for the confidence interval (CI) and the prediction interval (PI)';
    ShowMessage(S);
    end;



    procedure THillSlopeRegr.OpenFileCmdBtnClick(Sender: TObject);
    begin
    if OpenDialog1.Execute then OpenFileEditBx.Text := OpenDialog1.FileName
    else ShowMessage('Cannot open file!');
    end;



    procedure THillSlopeRegr.ComputeCmdBtnClick(Sender: TObject);
    var
    I,J,K, Posit, N_EC, N_Hill : integer;
    Chr: char;
    RowData, X_Str, Y_Str, EstimatedValues: string;
    X_Value, Y_Value, EC50_Low, EC50_High, Hill_Low, Hill_High, EC50, EC50_Range, EC50_Incr, HillSlope, Hill_Range, Hill_Incr: double;
    InputFile, X_List, Y_List, YHat_List, LLCI_List, ULCI_List, LLPI_List, ULPI_List, Output: TStringList;
    X_Array, Y_Array, YHat_Array, EC50_Array, Hill_Array, LLCI_Array, ULCI_Array, LLPI_Array, ULPI_Array: array of double;
    const Letters = ['A'..'Z', 'a'..'z'];
    begin
    try
    InputFile := TStringList.Create;
    X_List := TStringList.Create;
    Y_List := TStringList.Create;
    YHat_List := TStringList.Create;
    LLCI_List := TStringList.Create;
    ULCI_List := TStringList.Create;
    LLPI_List := TStringList.Create;
    ULPI_List := TStringList.Create;
    Output := TStringList.Create;

    InputFile.LoadFromFile(OpenFileEditBx.Text);
    if YesHeadersRadBtn.Checked = True then K := 1
    else if NoHeadersRadBtn.Checked = True then K := 0
    else ShowMessage('Does your file have headers?' + #13#10 + 'Please answer Yes or No.');
    SetLength(X_Array, InputFile.Count - K);
    SetLength(Y_Array, InputFile.Count - K);
    N_EC := 10;
    N_Hill := 10;
    EC50_Range := EC50_HighSpinEdit.Value - EC50_LowSpinEdit.Value;
    EC50_Incr := EC50_Range/N_EC;
    Hill_Range := Hill_HighSpinEdit.Value - Hill_LowSpinEdit.Value;
    Hill_Incr := Hill_Range/N_Hill;



    for I := K to InputFile.Count-1 do//value of K depends on presence/abssence of headers in input file
    begin
    RowData := Trim(InputFile[I]);
    Posit := Pos(',', RowData) - 1;//excluding the comma
    X_Str := '';
    for J := 1 to Posit do X_Str := X_Str + RowData[J];
    X_Str := Trim(X_Str);
    //check if there are any non-numerical data (letters) in the values
    for J := 1 to Length(X_Str) do
    begin
    Chr := X_Str[J];
    if Chr in Letters then
    begin
    MessageDlg('Error! Your file has non-numerical data in row #' + IntToStr(I) + '!', mtError, [mbAbort], 0);
    Exit;
    end
    else continue;
    end;
    X_Value := StrToFloat(X_Str);
    X_Array[I-1] := X_Value;


    Y_Str := '';
    for J := (Posit+2) to Length(RowData) do Y_Str := Y_Str + RowData[J];
    Y_Str := Trim(Y_Str);
    //check if there are any non-numerical data (letters) in the values
    for J := 1 to Length(Y_Str) do
    begin
    Chr := Y_Str[J];
    if Chr in Letters then
    begin
    MessageDlg('Error! Your file has non-numerical data in row #' + IntToStr(I) + '!', mtError, [mbAbort], 0);
    Exit;
    end
    else continue;
    end;
    Y_Value := StrToFloat(Y_Str);
    Y_Array[I-1] := Y_Value;

    // ShowMessage(FloatToStr(X_Value) + ',' + FloatToStr(Y_Value));

    end;

    {The formula for the Hillslope regression is y-hat = min + (Max - Min)/(1 + (X/EC50)^Hillslope),
    where Min and Max are constrained to 0% and 100%, respectively. This program needs to
    iterate for y-hat values through the EC50_Range and Hill_Range, using the EC50_Incr and
    Hill_Incr as the respective increments, and then those values of EC50 and Hillslope
    are picked as the best estimates that result in the lowest Residual SS, that is:
    Sum(Y - y-hat)2.
    }

    //Obtain the estimated values of EC50 and Hillslope as a single comma-delimited string
    EC50_Low := EC50_LowSpinEdit.Value;
    EC50_High := EC50_HighSpinEdit.Value;
    Hill_Low := Hill_LowSpinEdit.Value;
    Hill_High := Hill_HighSpinEdit.Value;
    EC50_Incr := (EC50_HighSpinEdit.Value - EC50_LowSpinEdit.Value)/N_EC;
    Hill_Incr := (Hill_HighSpinEdit.Value - Hill_LowSpinEdit.Value)/N_Hill;
    EstimatedValues := Estimate(N_EC, N_Hill, X_Array, Y_Array, EC50_Low, EC50_High, Hill_Low, Hill_High, EC50_Incr, Hill_Incr); ShowMessage(EstimatedValues);


    finally
    InputFile.Free;
    X_List.Free;
    Y_List.Free;
    YHat_List.Free;
    LLCI_List.Free;
    ULCI_List.Free;
    LLPI_List.Free;
    ULPI_List.Free;
    Output.Free;
    ShowMessage('Done!');
    end;


    end;




    function THillSlopeRegr.Estimate(N_EC, N_Hill: integer; X_Array, Y_Array: array of double; EC50_Low, EC50_High, Hill_Low, Hill_High: double; EC50_Incr, Hill_Incr: double): string;
    var
    I, J, K, NumValues: integer;
    Min, Max, X_Val, Y_Val, A, B, y_hat, Res_Sq, ResSS, Min_ResSS, EC50, Low_EC50, Low_Hill, Hill: double;
    RSS_Array, MinSS_Array: array of double;
    EC50_Array, Hill_Array, ResSS_Array: array of array of double;//multiple YHat values for each X value, one for each iteration
    YHat_Array: array of array of array of double;
    begin
    try
    Min := 0;
    Max := 100;
    NumValues := Length(X_Array);
    SetLength(ResSS_Array, N_EC+1, 11);//1001*1001 matrix of residual sum of squares - for each X,Y value
    SetLength(YHat_Array, NumValues, N_EC+1, N_Hill+1);
    SetLength(EC50_Array, N_EC+1, N_Hill+1);
    SetLength(Hill_Array, N_EC+1, N_Hill+1);
    for I := 0 to N_EC do
    begin
    for J := 0 to N_Hill do
    begin
    ResSS_Array[I,J] := 0;
    EC50_Array[I,J] := 0;
    Hill_Array[I,J] := 0;
    end;
    end;

    for I := 0 to NumValues - 1 do
    begin
    for J := 0 to N_EC do //1001 iterations to accommodate variations in the EC50 value
    begin
    for K := 0 to N_Hill do //1001 iterations to accommodate variations in the HillSlope value
    begin
    YHat_Array[I,J,K] := 0;
    end;
    end;
    end;


    //iterate over 1000 Hillslope values nested within 1000 EC50 values

    for I := 0 to NumValues - 1 do
    begin
    X_Val := X_Array[I];
    Low_EC50 := EC50_Low;
    for J := 0 to N_EC do //1001 iterations to accommodate variations in the EC50 value
    begin
    Low_Hill := Hill_Low;
    for K := 0 to N_Hill do //1001 iterations to accommodate variations in the HillSlope value
    begin
    y_hat := Min + (Max - Min)/(1 + Power((X_Val/Low_EC50), Low_Hill));
    YHat_Array[I,J,K] := y_Hat;
    EC50_Array[J,K] := Low_EC50;
    Hill_Array[J,K] := Low_Hill;
    Low_Hill := Low_Hill + Hill_Incr;
    end;
    Low_EC50 := Low_EC50 + EC50_Incr;

    end;
    end;

    //now compute ResSS for every combination of EC50 and Hill value
    for I := 0 to N_EC do
    begin
    for J := 0 to N_Hill do
    begin
    ResSS := 0;
    for K := 0 to Length(X_Array) - 1 do
    begin
    Y_Val := Y_Array[K];
    y_hat := YHat_Array[K,I,J];
    Res_Sq := Power((Y_Val - y_hat), 2);
    ResSS := ResSS + Res_Sq;
    end;
    ResSS_Array[I,J] := ResSS;
    end;
    end;

    SetLength(RSS_Array, N_EC);
    SetLength(MinSS_Array, N_EC);
    for I := 0 to N_EC do
    begin
    RSS_Array[I] := 0;
    MinSS_Array[I] := 0;
    end;

    //find the minimum Res SS in a nested manner
    for I := 0 to N_EC do
    begin
    for J := 0 to N_Hill do
    begin
    RSS_Array[J] := ResSS_Array[I,J];
    end;
    MinSS_Array[I] := MinValue(RSS_Array);//array of mimimum SS among Hill values within EC50 value
    end;
    Min_ResSS := MinValue(MinSS_Array);//minimum SS value


    for I := 0 to N_EC do
    begin
    for J := 0 to N_Hill do
    begin
    ResSS := ResSS_Array[I,J];
    if (ResSS = Min_ResSS) then
    begin
    EC50 := EC50_Array[I,J];
    Hill := Hill_Array[I,J];
    end
    else continue;
    end;
    end;

    finally
    Result := FloatToStr(EC50) + ', ' + FloatToStr(Hill);
    end;
    end;

    procedure THillSlopeRegr.CloseFormCmdBtnClick(Sender: TObject);
    begin
    HillSlopeRegr.Close;
    end;

    end.
    Last edited by llaama2012; August 2nd, 2012 at 03:29 PM. Reason: Change in code
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    354
    Rep Power
    7
    In my cursory look at the code, I can see one possible issue, but that issue would also put into question your statement that the function returns the correct value.

    The piece of code in question is just prior to your finally result := FloatToStr(..).. end;

    Code:
    for I := 0 to N_EC do
    begin
       for J := 0 to N_Hill do
       begin
          ResSS := ResSS_Array[I,J];
          if (ResSS = Min_ResSS) then
          begin
             EC50 := EC50_Array[I,J];
             Hill := Hill_Array[I,J];
          end
          else 
             continue;
       end;
    end;
    The issue is that ResSS and Min_ResSS are both declared as double. Comparing two floating point values with a simple equal sign is asking for trouble. Delphi has a function called CompareValue that compares single/double/extended (and an overloaded function that compares integer/Int64) values. Using that function would look like this

    Code:
    for I := 0 to N_EC do
    begin
       for J := 0 to N_Hill do
       begin
          ResSS := ResSS_Array[I,J];
          if CompareValue(ResSS, Min_ResSS) = 0 then //Edit:  correction here
          {0  - Equal
           -1 - Less Than
           1  - Greather Than}
          begin
             EC50 := EC50_Array[I,J];
             Hill := Hill_Array[I,J];
          end
          else 
             continue;
       end;
    end;
    Which brings up the fact that EC50 and Hill can only get values when this particular if statement evaluates true. If it never evaluates true (which as currently coded is a distinct possibility), then they are uninitialized variables. Converting an uninitialized variable, which is what you are doing with FloatToStr(..), would definitely give you the error your experiencing.


    Also, Since your Estimate function would probably not be called from other units it should be added to the private section of you form's declaration:
    Code:
       procedure HelpCmdBtnClick(Sender: TObject);
       procedure CloseFormCmdBtnClick(Sender: TObject);
       procedure OpenFileCmdBtnClick(Sender: TObject);
       procedure ComputeCmdBtnClick(Sender: TObject);
    private
       { Private declarations }
       function Estimate(N_EC, N_Hill: integer; X_Array, Y_Array: array of double; EC50_Low, EC50_High, Hill_Low, Hill_High: double; EC50_Incr,                      Hill_Incr: double): string;
    public
       { Public declarations }
    end;
    Hope this addresses the issue..
    Last edited by majlumbo; August 2nd, 2012 at 07:19 PM.
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    354
    Rep Power
    7
    Also, one more, un-related, comment concerning the creation of your stringlists and then freeing them.

    The safest way to create and then free them is to do so using a try..finally block, which you do use, however improperly.

    As an example:
    Code:
    Var
       SL1, SL2, SL3: TStringList;
    begin
       SL1 := TStringList.Create;
       try
         // you can work with SL1 from here down.
          SL2 := TStringList.Create;
          try
             //you can work with SL1 and SL2 from here down
             SL3 := TStringList.Create;
             try
                //work with SL1/SL2 and SL3 from here down
             finally
                SL3.Free;
             end;
          finally
             SL2.Free;
          end;
       finally
          SL1.Free;
       end;
    end;
    basically after you create the object, put the try..finally block.

    To catch errors, you need to wrap the code that may cause an error within the try..except block
    Code:
    function DivideNums(A, B: Integer): Double;
    begin
       try
          Result := A/B;
        except
           on EZeroDivide do
              MessageDlg('Can not divide by zero!',mtError, [mbOK], 0) ;
        end;
    end;
    So where B is zero, this function would not return a value, but give an error message
  10. #6
  11. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2012
    Posts
    23
    Rep Power
    0
    Thanks mailjumbo. Appreciate your reading through my code. However, I am getting the same error even after incorporating both your suggestions. (By the way, it is "if CompareValue(ResSS, Min_ResSS) = 0 then ...", right, and not "if CompareValue(ResSS = Min_ResSS) = 0 then ..."?) In any case, I am still getting the same error.

    To investigate the error, I tried to save the result in a text file from the function itself. I know it is computing both parameter values accurately (when I step into the code), but when I try to execute the WriteLn command, it gives me an I/O error (103). And when I remove the textfile part of the code, it is giving the same error of invalid pointer. it even skips the "ShowMessage" command after I call the function.

    Also, I thought that CompareValue assumes that the two values are equal if they are "close enough" (whatever that means). I got that from http://www.delphibasics.co.uk/RTL.asp?Name=CompareValue.
  12. #7
  13. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2012
    Posts
    23
    Rep Power
    0
    Originally Posted by majlumbo
    Also, one more, un-related, comment concerning the creation of your stringlists and then freeing them.

    The safest way to create and then free them is to do so using a try..finally block, which you do use, however improperly.

    As an example:
    Code:
    Var
       SL1, SL2, SL3: TStringList;
    begin
       SL1 := TStringList.Create;
       try
         // you can work with SL1 from here down.
          SL2 := TStringList.Create;
          try
             //you can work with SL1 and SL2 from here down
             SL3 := TStringList.Create;
             try
                //work with SL1/SL2 and SL3 from here down
             finally
                SL3.Free;
             end;
          finally
             SL2.Free;
          end;
       finally
          SL1.Free;
       end;
    end;
    basically after you create the object, put the try..finally block.

    To catch errors, you need to wrap the code that may cause an error within the try..except block
    Code:
    function DivideNums(A, B: Integer): Double;
    begin
       try
          Result := A/B;
        except
           on EZeroDivide do
              MessageDlg('Can not divide by zero!',mtError, [mbOK], 0) ;
        end;
    end;
    So where B is zero, this function would not return a value, but give an error message
    Thanks. Both are good suggestions. I will incorporate them.
  14. #8
  15. No Profile Picture
    Contributing User
    Devshed Regular (2000 - 2499 posts)

    Join Date
    Jan 2006
    Location
    Carlsbad, CA
    Posts
    2,057
    Rep Power
    383
    A few of additional coding suggestions, since you invited other comments.
    1. Don't put the
    Result :=
    in the finally block.
    The finally block is for making sure resources are cleaned up and the states of any properties, variables etc.
    are returned to valid / consistent conditions whether the procedure completes successfully or fails to complete with an exception.
    2.
    It would be better to breakup those big procedures into multiple smaller ones and call
    them in sequence from a master procedure. Easier to read, easier to debug.
    IF speed is a concern then the most recent versions of Delphi offer the inline keyword
    which will instruct the compiler, if possible, to recombine them during compilation.
    3.
    Take a look at raising your own exceptions. They will usually give you a better design
    than using the MessageDlg / Exit combination.
    4.
    Delphi does not initialize local variables so that although (in this situation) the following code is very unlikely to fail:
    Code:
    InputFile := TStringList.Create;
    X_List := TStringList.Create;
    Y_List := TStringList.Create;
    YHat_List := TStringList.Create;
    LLCI_List := TStringList.Create;
    ULCI_List := TStringList.Create;
    LLPI_List := TStringList.Create;
    ULPI_List := TStringList.Create;
    Output := TStringList.Create;
    You should first initialize them all to nil. Otherwise your program will crash in the finally
    block when it tries to free the first TStringList that was not created.

    HTH

    Clive
  16. #9
  17. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2012
    Posts
    23
    Rep Power
    0
    Mailjumbo, actually, they turned out to be GREAT suggestions, for my code now works! This is bizzare, but all I did was move my "InputFile := TSrtingList.Create" statement to before the "try" statement as you suggested. Also, I added a try...except...end block to where I call the function "Estimate". My exception handling was that if the function returned nill ('') then let me know by means of a MessageDlg. That was it! And it works now. I wonder why...

    Also, I understand Clive's suggestions #1 and #4 (and will incorprate them) but not the other two. I will try to break up my main procedure into smaller parts but I do not understand the inline keyword part. But perhaps it is too advanced for me right now. As for the exception handling, though, I thought using the MessageDlg or ShowMessage was raising my own exceptions. Not sure I understand....
  18. #10
  19. No Profile Picture
    Contributing User
    Devshed Regular (2000 - 2499 posts)

    Join Date
    Jan 2006
    Location
    Carlsbad, CA
    Posts
    2,057
    Rep Power
    383
    Yes. Ignore inline for now. There are more important things to get your mind around first.

    Exceptions - this is more important!
    Do learn about them.
    You create your own with code such as this:
    Code:
      raise Exception.Create('my error message here');
    There are, of course, other variations; but this is the simplest.

    The big difference between this and your exit statements is this:
    Exit; simply exits the current procedure and the calling procedure will try to continue
    from the point at which the the procedure that just exited returns.
    An Exception raised will just keep walking up the call stack (without trying to run any intervening code)
    until it finds the first Except clause. If it does not find one of yours, then it will
    eventually hit the default except clause that Delphi has wrapped round your entire application.

    Hope this clarifies a little.

    Clive
  20. #11
  21. No Profile Picture
    Contributing User
    Devshed Regular (2000 - 2499 posts)

    Join Date
    Jan 2006
    Location
    Carlsbad, CA
    Posts
    2,057
    Rep Power
    383
    To follow up:
    I posted my suggestions before reading maljumbo's latest posts.

    maljumbo's suggestion of the nested try...finally blocks and creating the TStringLists before each try is the best.
    However, my suggestion of initializing the TStringList variables to nil first and then
    creating all but the first one inside the try...finally block is a technique often used
    when creating a large number of objects (as you are) to avoid having
    try...finally blocks nested six or seven deep.

    If you create each individual TStringList outside its own try...finally block you do
    not need to initialize it to nil first.
    Last edited by clivew; August 2nd, 2012 at 06:01 PM. Reason: Added the final paragraph
  22. #12
  23. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2012
    Posts
    23
    Rep Power
    0
    Originally Posted by clivew
    Yes. Ignore inline for now. There are more important things to get your mind around first.

    Exceptions - this is more important!
    Do learn about them.
    You create your own with code such as this:
    Code:
      raise Exception.Create('my error message here');
    There are, of course, other variations; but this is the simplest.

    The big difference between this and your exit statements is this:
    Exit; simply exits the current procedure and the calling procedure will try to continue
    from the point at which the the procedure that just exited returns.
    An Exception raised will just keep walking up the call stack (without trying to run any intervening code)
    until it finds the first Except clause. If it does not find one of yours, then it will
    eventually hit the default except clause that Delphi has wrapped round your entire application.

    Hope this clarifies a little.

    Clive
    Thanks, Clive. I think I understand. But it appears to me that raising an exception the way you demonstrated will help the developer only, whereas a ShowMessage or MessageDlg needs to be used if I want to give my GUI to an uninitiated user. But maybe there is a way?
  24. #13
  25. No Profile Picture
    Contributing User
    Devshed Regular (2000 - 2499 posts)

    Join Date
    Jan 2006
    Location
    Carlsbad, CA
    Posts
    2,057
    Rep Power
    383
    Thanks, Clive. I think I understand. But it appears to me that raising an exception the way you demonstrated will help the developer only, whereas a ShowMessage or MessageDlg needs to be used if I want to give my GUI to an uninitiated user. But maybe there is a way?
    Yes.
    When you get to the except block you do whatever cleanup is needed and then use messageDlg or showmessage, if appropriate, to inform the user.
    Delphi's default except clause does that if you do nothing in between.

    To use the example already given:
    Code:
    function DivideNums(A, B: Integer): Double;
    begin
       try
          Result := A/B;
        except
           on EZeroDivide do
              MessageDlg('Can not divide by zero!',mtError, [mbOK], 0) ;
        end;
    end;
    Now adjust that.
    Code:
    function DivideNums(A, B: Integer): Double;
    begin
       Result := A/B;
    end;
    function dothis(A, B: Integer): Double;
    begin
       result := DivideNums(A, B);
       ShowMessage('Division succeeded');
    end;
    
    procedure DoIt(A, B: Integer): Double;
    var
      myVal: Double;
    begin
       try
         myVal := doThis(A,B);
         showMessage(intToStr(trunc(myVal));
       except
          // catch something we know might happen
          on EZeroDivide do
             MessageDlg('Can not divide by zero!',mtError, [mbOK], 0) ;
          // Deal with the unknown mishap 
          on E: Exception do
             MessageDlg('Something unexpected happened. Here is all I know '
             +E.message,mtError, [mbOK], 0) ;
        end;  //except
    end;
    I hope this illustrates a couple of things.
    1.
    If B was zero then an EZeroDivide exception will be raised by Delphi.
    Because of that the code will jump immediately to the except clause in
    DoIt.
    a. The line ShowMessage('Division succeeded'); will not execute.
    b. You do not have to worry about anything invalid being returned into myVal
    c. showMessage(intToStr(trunc(myVal)); will not execute.

    2.
    If EZeroDivide is found then you know what you want to do.
    If it drops into the catch all Exception then you can show a custom message and add on the
    message that the unexpected exception returned that the user can tell the developer
    for debugging.

    Clive
  26. #14
  27. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    354
    Rep Power
    7
    Originally Posted by llaama2012
    (By the way, it is "if CompareValue(ResSS, Min_ResSS) = 0 then ...", right, and not "if CompareValue(ResSS = Min_ResSS) = 0 then ..."?)
    That is correct, copy and paste will get you every time....

    I edited my post to reflect this...
    Last edited by majlumbo; August 2nd, 2012 at 07:20 PM.
  28. #15
  29. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2012
    Posts
    23
    Rep Power
    0
    Originally Posted by clivew
    Yes.
    When you get to the except block you do whatever cleanup is needed and then use messageDlg or showmessage, if appropriate, to inform the user.
    Delphi's default except clause does that if you do nothing in between.

    To use the example already given:
    Code:
    function DivideNums(A, B: Integer): Double;
    begin
       try
          Result := A/B;
        except
           on EZeroDivide do
              MessageDlg('Can not divide by zero!',mtError, [mbOK], 0) ;
        end;
    end;
    Now adjust that.
    Code:
    function DivideNums(A, B: Integer): Double;
    begin
       Result := A/B;
    end;
    function dothis(A, B: Integer): Double;
    begin
       result := DivideNums(A, B);
       ShowMessage('Division succeeded');
    end;
    
    procedure DoIt(A, B: Integer): Double;
    var
      myVal: Double;
    begin
       try
         myVal := doThis(A,B);
         showMessage(intToStr(trunc(myVal));
       except
          // catch something we know might happen
          on EZeroDivide do
             MessageDlg('Can not divide by zero!',mtError, [mbOK], 0) ;
          // Deal with the unknown mishap 
          on E: Exception do
             MessageDlg('Something unexpected happened. Here is all I know '
             +E.message,mtError, [mbOK], 0) ;
        end;  //except
    end;
    I hope this illustrates a couple of things.
    1.
    If B was zero then an EZeroDivide exception will be raised by Delphi.
    Because of that the code will jump immediately to the except clause in
    DoIt.
    a. The line ShowMessage('Division succeeded'); will not execute.
    b. You do not have to worry about anything invalid being returned into myVal
    c. showMessage(intToStr(trunc(myVal)); will not execute.

    2.
    If EZeroDivide is found then you know what you want to do.
    If it drops into the catch all Exception then you can show a custom message and add on the
    message that the unexpected exception returned that the user can tell the developer
    for debugging.

    Clive
    Hmmm, so you raise the exceptions in the procedure rather than in the functions? Is that why the program jumps to the next command in the procedure upon Delphi finding an exception (such as dividing by zero), but now a custom message is displayed rather than the default message by Delphi? Is that the idea? Boy, it is going to take some mental gymnastics for me to come up with code like this! It seems to come so naturally to you guys.
Page 1 of 2 12 Last
  • Jump to page:

IMN logo majestic logo threadwatch logo seochat tools logo