Jump to content

Uwe Raabe

Members
  • Content Count

    2558
  • Joined

  • Last visited

  • Days Won

    150

Posts posted by Uwe Raabe


  1. Make the function a static class function of a record:

    type
      TMyPassHandler = record
      public
        class function Pass<T:record>(const Value: T): Boolean; static;
      end;
    
    class function TMyPassHandler.Pass<T>(const Value: T): Boolean;
    begin
      Result := False;
      { do whatever is needed }
    end;
    

    Most of the time the compiler should be able to infer the proper type, so a call would look like:

    TMyPassHandler.Pass(myRec);

     

    • Like 2

  2. I imagine what is happening and I assume it to be expected behavior, although I admit one has to know how variable capturing works to get it. It is even documented (Variable Binding Mechanism)

    Quote

    Note that variable capture captures variables--not values. If a variable's value changes after being captured by constructing an anonymous method, the value of the variable the anonymous method captured changes too, because they are the same variable with the same storage.

     

    There is still a lot of guessing about what is THandlerClass, OnHandle and how AddHandler is defined and what it does with its parameter, so it is difficult to give a detailed analysis.

     

    • Like 1
    • Thanks 1

  3. 46 minutes ago, dummzeuch said:

    I assume that the decimal separator is either a comma or a dot. I try to guess which one or is.

    One criterion: The decimal separator is only allowed once.

    function GuessDecimalSeparator(const Value: string): Char;
    { Assumes that the decimal separator is the last one and does not appeat more than once.
      Only dot and comma are treated as possible decimal separator. }
    const
      cSeparators: array[Boolean] of Char = ('.', ','); // possible decimal separators
    var
      idx: Integer;
    begin
      Result := cSeparators[False]; // default if none of the separators is found - alternative "True"
      idx := Value.LastIndexOfAny(cSeparators);
      if (idx >= 0) then begin
        Result := Value.Chars[idx];
        if Value.CountChar(Result) > 1 then begin
          { if it appears more than once we use the other one }
          Result := cSeparators[Result = cSeparators[False]];
        end;
      end
    end;

     

    • Thanks 1

  4. 35 minutes ago, dummzeuch said:

    Will also fail on '1.000.000'.

    Define "fail". For German language settings it returns a comma, which is probably correct in most cases.

     

    If the choice is either comma or point, the algorithm can be adapted to that. Otherwise the task is just not fully defined for all inputs.

     

    So what would you call a correct result when the decimal separator is not part of the string?

    • Like 1

  5. function GuessDecimalSeparator(const Value: string): Char;
    { assumes that the decimal separator is the last one and does not appear more than once }
    var
      idx: Integer;
    begin
      idx := Value.LastIndexOfAny(['.', ',']); // possible decimal separators
      if (idx >= 0) then begin
        Result := Value.Chars[idx];
        if Value.CountChar(Result) = 1 then Exit;
      end;
      Result := FormatSettings.DecimalSeparator; // Default
    end;

     

    • Like 1
    • Thanks 1

  6. In Delphi a set is denoted with square brackets. Unfortunately a constant array is also denoted with square brackets. I have to admit that I am not happy with this language design decision either.

     

    The term [10] as well as (i being an integer) can be both a set or array constant. There is no rule for the compiler to choose one or another. There are scenarios where the compiler chooses different than the developer intended.

     

    If you feel that the compiler is doing wrong here, please open a bug report with this example.

     


  7. Probably because in the second case "[10]" is seen as a set constant by the compiler. It works if you declare a variable with a proper type:

    var
      b: boolean;
      a: array of Integer;
      r: TMyRecord2;
    begin
      try
        a := [10];
        r := r * a; // this line is compiled
        b := r = a; // this line is not compiled
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
    end.

     

     

    • Like 1

  8. 10 minutes ago, Stefan Glienke said:

    I like using SourceTree (after the 2.x disaster in 3.x it's usable again).

    As I am still looking for a usable GIT client that matches the ease of the TortoiseHG workbench, I should probably evaluate SourceTree again.

    • Like 1
    • Thanks 1

  9. This is an class approach. The benefit is the full encapsulation of the private parts inside the implementation section.

    unit uGlobalData;
    
    interface
    
    type
      TGlobalData = class
      type
        TValue = record
          ValName: string;
          ValInProject: boolean;
        end;
      public
        { these should probably be properties instead }
        Id: Integer;
        RecName: string;
        Values: TArray<TValue>;
        procedure PrepareGlobalRec; virtual; abstract;
      end;
    
    function GlobalData: TGlobalData;
    
    implementation
    
    type
      TGlobalDataImpl = class(TGlobalData)
      private type
        TLocalRec = record
          LocalRecStr: string;
          // ...
        end;
    
        procedure LocalProc1;
        procedure LocalProc2;
    
      var
        LocalStrVar: string;
        LocalRecVar: TLocalRec;
      public
        procedure PrepareGlobalRec; override;
      end;
    
    procedure TGlobalDataImpl.LocalProc1;
    begin
    
    end;
    
    procedure TGlobalDataImpl.LocalProc2;
    begin
    
    end;
    
    procedure TGlobalDataImpl.PrepareGlobalRec;
    begin
      // prepare GlobalRec data
      LocalStrVar := 'local test';
      LocalRecVar.LocalRecStr := 'test';
      LocalProc1;
      LocalProc2;
    end;
    
    var
      GlobalDataInstance: TGlobalData = nil;
    
    function GlobalData: TGlobalData;
    begin
      if GlobalDataInstance = nil then begin
        GlobalDataInstance := TGlobalDataImpl.Create;
      end;
      Result := GlobalDataInstance;
    end;
    
    initialization
    
    finalization
      GlobalDataInstance.Free;
      GlobalDataInstance := nil;
    end.

     

    • Thanks 1

  10. I wonder how this folder is going to be used as Shared folder. For a non-admin installation that should rather be %APPDATA%\Raabe Software\Shared and not %APPDATA%\Raabe Software\MMX Code Explorer. On the other hand I can imagine some buggy setup scheme from my side.

     

    So whatever that registry RootDir entry points to, copy the Indexers folder into that folder and you should be done.

     

    I will investigate the setup for any glitches - or better - rethink the folder architecture in general.

    • Like 1

  11. The Indexer templates are expected in the Indexers sub folder from the shared folder set in registry under HKEY_CURRENT_USER\Software\Raabe Software\Shared\RootDir.

     

    It is quite possible that something went wrong during the installation and the indexer files were not copied to that folder. I will check the setup for that.


  12. 9 hours ago, Mike Torrettinni said:

    Settings related screens, the logic says: create once, use for the life of application and all the values that developer set once, will be there until closing.

    For these cases I prefer moving the settings to a separate settings class independent from the settings form. Then I can create and initialize an instance of this settings class at startup, load the settings from some storage and adjust with some command line parameters. Then the settings are available all over the application without having to rely on the settings form creation. I often have the case where I need access to the settings long before the main form is created. This would be hard to achieve using the settings form as the central storage for the settings.

    • Like 4
×