Jump to content

Uwe Raabe

Members
  • Content Count

    2907
  • Joined

  • Last visited

  • Days Won

    169

Posts posted by Uwe Raabe


  1. The data file has 140.748 bytes - that doesn't work well with 971 records, but with 951 records with 148 bytes each. That record size also matches the actual file content.

     

    You may get better results with this declaration:

      lexrec = record
        root_part: string32;
        filler: string[2];
        case speech_part: valid_range of
          - 1:
            (suppletive_type: supp_rec);
          0:
            (nsupp_type: integer);
          1, 2, 3, 4, 5:
            (normal_type: normal)
      end;

     


  2. Note that the Implicit string operator can make you fall into the same parameter order trap as AddToStock is for NativeInt.

     

    To avoid that, you may prefer to replace the operator with a dedicated ToString function, keeping the original call to ShowMessage:

    type
      TProductID = record
        ID: nativeint;
      public
        class operator Explicit(A: TProductID): NativeInt; overload;
        class operator Implicit(A: NativeInt): TProductID; overload;
        function ToString: string;
      end;
    
    class operator TProductID.Explicit(A: TProductID): NativeInt;
    begin
      Result := A.ID;
    end;
    
    class operator TProductID.Implicit(A: NativeInt): TProductID;
    begin
      Result.ID := A;
    end;
    
    function TProductID.ToString: string;
    begin
      Result := ID.ToString;
    end;
    
    ---
    
    procedure TForm6.AddToStock(pid: TProductID; cnt: nativeint);  // Both uses internally nativeint
    begin
      ShowMessage(pid.ToString);
    end;

     


  3. One option would be to add some operator overloading to TProductID:

    type
      TProductID = record
        ID: nativeint;
      public
        class operator Explicit(A: TProductID): NativeInt; overload;
        class operator Implicit(A: NativeInt): TProductID; overload;
        class operator Implicit(A: TProductID): string; overload;
      end;
    
    class operator TProductID.Explicit(A: TProductID): NativeInt;
    begin
      Result := A.ID;
    end;
    
    class operator TProductID.Implicit(A: NativeInt): TProductID;
    begin
      Result.ID := A;
    end;
    
    class operator TProductID.Implicit(A: TProductID): string;
    begin
      Result := A.ID.ToString;
    end;

    This allows to write something like this:

    var cid : TCustomerID;
    var pid: TProductID;
    
    pid := qryGetStockProductID.AsInteger;
    cid := qryGetStocCustID.AsInteger;
    
    ---
    
    procedure TForm6.AddToStock(pid: TProductID; cnt: nativeint);  // Both uses internally nativeint
    begin
      ShowMessage(pid);
    end;

    Note that the operator to convert TProductID to NativeInt is Explicit instead of Implicit. That prohibits the use of TProductID as a NativeInt parameter and the compiler throws an error when the parameters to AddToStock are given in the wrong order.

    • Like 2
    • Thanks 1

  4. I have written a couple of applications for CNC machines. The majority are for wood working and those usually have a resolution of 0.001 mm. Therefore the Epsilon used for matching element start/end points is set to 0.0005. When calculating parallel contours there often are cases where very small elements appear that have to be removed and the disjunct neighbors need some trimming to an intersection. These cases need a much finer epsilon than the first one.

     

    Side note: Regression tests comparing float calculations created under Win32 can easily fail when executing on a Win64 platform.


  5. 2 minutes ago, David Heffernan said:

    This entire approach of applying some arbitrary epsilon is rubbish.

    I beg to differ here. The approach is to have a decent epsilon when no one is given. It doesn't prohibit anyone to give an epsilon appropriate to the current scenario.

     

    12 minutes ago, David Heffernan said:

    I've only ever seen them used inappropriately

    The default values currently used actually cover a common beginner's mistake assuming that floats can be compared for exact equality. Usually the IsZero implementation works pretty good for the majority of the cases - at least those I encountered myself. Perhaps you just never stumbled about code using them appropriately?

    • Like 1

  6. 10 hours ago, GrumpyNoMore said:

    It seems strange if Interbase Client can only be installed on one machine under the Developer license

    Actually that is not the case. The developer license is needed for the Interbase Server, while the Client doesn't need a license - only access to the Interbase Server (i.e. the server PC has to be up and running and the client needs to establish a connection to it).

     

    Regarding the Firebird suggestion: Don't try to install Firebird (Server or Client) on a PC where Interbase already is installed unless you know exactly what you are doing. Most likely you will break at least one of these installations.

     

     


  7. On 10/18/2023 at 12:01 PM, dormky said:

    I'd like to extract a row's data as an object from a TMyQuery, so that I can give it as an argument to a function.

    I'm not sure if that is what you are after: Dataset Enumerator Reloaded. The current location for the unit described in that article is now on GitHub as part of my CmonLib library: https://github.com/UweRaabe/CmonLib/blob/main/source/Cmon.DataSetHelper.pas

     

    The code allows to retrieve the current record of a dataset either as a record or a class.

     

    The record example shown in the article requires the declaration of a record

    type
      [DBFields(mapAuto)]
      TEmployee = record
        EmpNo: Integer;
        LastName: string;
        FirstName: string;
        PhoneExt: string;
        HireDate: TDateTime;
        Salary: Double;
      end;

    With that you can retrieve the data of the current dataset record like this:

    var
      Employee: TEmployee;
    begin
      { Show the employee's name and the hire date. }
      Employee := QuEmployee.GetCurrentRec<TEmployee>;
      ShowMessage(Format('%s %s was hired on %s', 
          [Employee.FirstName, Employee.LastName, FormatDateTime('dddddd', Employee.HireDate)]));
    end;

    The article also has another example using a class instead of a record.

    • Like 3

  8. As long as you don't change it, any field in  a new record is initialized with Null. A TDBCheckBox displays a NULL as grayed - that is what you see.

     

    The easiest way to avoid this is to wire the OnNewRecord event of the dataset and initialize the relevant fields with False.


  9. Perhaps I have a different concept of selected, but any non-empty dataset always has a current record, wich is where the indicator is when dgIndicator is part of the options. The field values from the dataset resemble this current record/row.

     

    In addition to this current row, a TDBGrid also has some SelectedRows represented by a TBookmarkList.

     

    So with

    45 minutes ago, Lainkes said:

    If a record (person) is selected in the DBGrid

    do you mean the current row or the bookmark list?


  10. 50 minutes ago, JonRobertson said:

    I use a TDataModule for resources and components, such as TImageCollection and TVirtualImageList, that are used by multiple forms.

    Using the same TVirtualImageList for several forms will fail if these forms are placed on monitors with different DPI. One of the main tasks of TVirtualImageList is to adjust the size of its images to match the DPI of the form. To achieve this they have to be owned by the form. See TVirtualImageList.DPIChangedMessageHandler.


  11. 1 hour ago, JonRobertson said:

    The one difference that I encounter is that a "SubForm" is not linked to anything at design-time. If you have placed a TFrame anywhere in the project, the frame is now linked and "in use" by the designer.

    Indeed - and it is just because you simply cannot place a "SubForm" anywhere at design time. If you don't do it with a frame both are on par again.


  12. Can you explain how you come to this conclusion?

     

    Adding a TEdit to a frame is no different from adding a TEdit to a form.

     

    Basically a "SubForm" is just a TForm descendant, which Parent is set to another control at runtime. During design time the "SubForm" is designed as any other form. So is a frame.

     

    I guess, you are referring to adding a TEdit to a frame during design time while it is inlined (placed) into another form. You have to switch to the frame designer to do that and switch back to the form to see the change in the form.

    You also need to switch to the form designer of a "SubForm" to do that. In contrast to the inlined frame you can see the actual outcome at runtime only.

     

     

×