Nov 152017
 

So I don’t forget:

The following Linux command shows a list of all subdirectories that contain a least one *.xml file:

find . -type f -name '*.xml' | sed -r 's|/[^/]+$||' | sort | uniq

This is an adaptation of this answer on unix.stackexchange.com.

Nov 022017
 

There was a discussion about using the PE flag IMAGE_FILE_LARGE_ADDRESS_AWARE and whether this is equivalent to compiling a program to 64 bit Windows target (no it’s not) in the German Delphi Praxis forum. This prompted me to have a look at what kind of flags can be specified and how to do that for a Delphi program.

A quick Bing search for SETPEFLAGS and Delphi also found a blog post by Halvard Vassbotn from 2006 about the IMAGE_FILE_RELOCS_STRIPPED PE flag.

Since one of my programs has recently started to throw out of memory
exceptions (when displaying large(!) data files) and our executables tend to be rather large anyway, I decided to add these PE flags to it.

  • IMAGE_FILE_LARGE_ADDRESS_AWARE – App can handle >2gb addresses
  • IMAGE_FILE_RELOCS_STRIPPED – Relocation info stripped from file

With the flags it already used …

  • IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP – If Image is on removable media, copy and run from the swap file
  • IMAGE_FILE_NET_RUN_FROM_SWAP – If Image is on Net, copy and run from the swap file.

… this resulted in the following SETPEFLAGS compiler directive in the .dpr file:

{$SETPEFLAGS IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP
          or IMAGE_FILE_NET_RUN_FROM_SWAP
          or IMAGE_FILE_LARGE_ADDRESS_AWARE
          or IMAGE_FILE_RELOCS_STRIPPED}

The result was as I hoped: No out of memory error any more when displaying a file that always caused one before. The executable size also shrank, but not as significantly as I had hoped: 9698 KB where before it was 9987 KB, but every little helps.

But there is another side effect I found today: Setting IMAGE_FILE_NET_RUN_FROM_SWAP not only makes Windows load the executable into the swap file when executing it but apparently every time it accesses it. Simply selecting it in Windows Explorer resulted in the Explorer Window freezing for a short time (it feels like several seconds but probably is just about one), the same delay happens when opening the Properties dialog (e.g. to check the version info). That does not happen, when the flag isn’t set.

Oct 312017
 

Note to self: Delphi’s Advanced Records are also available in Lazarus / Free Pascal, but only if they have been explicitly enabled with the modeswitch compiler directive:

unit SomeUnit;

{$mode objfpc}{$H+}{$modeswitch advancedrecords}

interface

type
  TSomeRecord = record
    FSomeField: integer;
    function SomeMethod: integer;
  end;

implementation

function TSomeRecord.SomeMethod: integer;
begin
  Result := FSomeField;
end;

end.

If advancedrecords is not enabled this won’t compile:

Fatal: Syntax error: “END” expected but “FUNCTION” found.
Oct 212017
 

Flattr have changed their terms an conditions. Since I haven’t received a single cent via Flattr, I really can’t be bothered what exactly they have changed and whether that’s good or bad for me personally. So I have closed that account for good. That leaves only PayPal, if you want to make a donation to GExperts. Sorry, if that’s an inconvenience. And please remember that I’d prefer you to contribute to GExperts in one of the other means described on that page than money.

Oct 142017
 

Jeroen has submitted two enhancements to GExperts:

Thanks!

I am still working on the refactoring for the IDE form enhancements, but progress is slow, so the next release will probably take a while.

Sep 302017
 

A new test release of my dzDebugVisualizers for Delphi 2005, 2006 and 2007 is available. Apart from fixing an Access Violation when unloading the package I have added support for TDateTime and unquoted (multiline) strings to the Evaluate / Modify window:

In addition I have added a “Modifiers” button to the dialog which allows to add any of the supported display format specifiers to the expression.

Download that test release and tell me, what you think. What other data types would you like me to add? Are there any bugs?

Sep 272017
 

Wir suchen wieder einen Kollegen. Der Job ist inhaltlich definitiv interessant, auch die Kollegen aus der Hardwareentwicklung sind durchaus umgänglich.

Standort: Essen

Es geht nicht um die Prüfung von Kraftfahrzeugen, auch wenn wir zum TÜV Rheinland gehören.

Technischer Mitarbeiter im Bereich Elektrotechnik (w/m)

Ihre Aufgaben

  • Unterstützung des Fahrzeug Support-Teams bei
    Neuaufbau und Instandsetzung der Technik für unsere
    schnellfahrenden Messsysteme
  • Konstruktion, Aufbau und Test von elektronischen /
    elektromechanischen Prototypen für unsere
    Messfahrzeuge, Begleitung bis zur Serienreife
  • Organisation und Durchführung des Beschaffungswesens
    inkl. Lieferantenauswahl für den Bereich Systemtechnik
    Hardware
  • Betreuung der für Kunden entwickelten Mess- und
    Prüfsysteme

Nachteil: Man muss softwareseitig mit mir zusammenarbeiten 😉

Stellenangebot auf Stepstone

Sep 032017
 

I was made aware that dzPrepBuild no longer works with .dof files. Since I don’t use it with Delhi 6 or 7 any more, I didn’t realize that myself. The bugfix was easy, just pass the correct parameter.

The new version is available from SourceForge. Note that, for whatever reason, SoureForge still claims that the latest download is version 1.3.0. You’ll have to select version 1.3.2 yourself. I hope that it is just a matter of time for SourceForge to update that link.

Sep 022017
 

David Heffernan commented on Girish Patil’s post on G+

But you should never make instance methods on value types that mutate the value.

Otherwise you can call such a method on an instance passed to a function as a const parameter.

Where “value types” in this case is meant to be an advanced record in Delphi.

I must admit that I am guilty of doing this and have of course lived to regret it. Here is an example:

type
  TDistance = record
  private
    FMillimetres: Int64;
  public
    function InMillimetres: Int64;
    function InMetres: Double;
    function InKm: Double;
    procedure AssignMetres(_Value: Double); // don't do this!
  end;

In case it isn’t obvious: This is an advanced record for storing a distance with a fixed resolution on 1 mm. The idea is to prevent cases where you have got a variable containing a distance but it isn’t immediately clear what unit is it using.

There are functions that return the value converted to metres and kilometres. And then there is a procedure AssignMetres which violates the rule that David stated:

procedure TDistance.AssignMetres(_Value: double);
begin
  FMillimetres := Round(_Value * 1000);
end;

On first glance, there is nothing wrong with this method. It simply assigns a new value to the record.

But now, consider this procedure that takes a const TDistance parameter:

procedure doSomething(const _Dist: TDistance);
begin
  // ...
  Dist.AssignMetres(5);
  // ...
end;

// ...
var
  SomeDist: TDistance;
begin
  SomeDist.AssignMetres(3);
  doSomething(SomeDist);
  // ...

The compiler won’t complain because calling methods of value types passed as const is allowed. But since the method has a side effect, setting the value of _Dist, it will not just change the value of _Dist inside the procedure but also the value of the variable passed to doSomething. The caller of course relies on that value to remain unchanged, because this is a const parameter after all. But, after the call to doSomething, the value of SomeDist now is 5m rather than 3m as originally assigned.

That’s what David meant with this comment.

This was bad enough, but there is another trap which I fell for. Consider this class that has a property Length of the type TDistance:

 type
  TCar = class
  private
    FHasChanged: boolean;
    FLength: TDistance;
    procedure SetLength;
  public
    property Length: TDistance read GetLength write SetLength;
    property HasChanged: read FHasChanged;
  end;
// ...
function TCar.GetLength: TDistance;
begin
  Result := FLength;
end;

procedure TCar.SetLength(const _Value: TDistance);
begin
  FHasChanged := True;
  FLength := _Value;
end;

Again, nothing seems to be wrong here. The SetLength property setter, in additon to setting the FLength field also sets the FHasChanged field to True which supposedly is read later on to determine whether any changes have to be saved somewhere. You could probably argue that the property getter for Length is not necessary, but who knows, it might become necessary later on.

It works fine too:

var
  MyCar: TCar;
  Dist: TDistance;
begin
  Dist.AssignMetres(4.5);
  MyCar := TCar.Create;
  try
    MyCar.Length := Dist;
    if MyCar.HasChanged then
      MyCar.SaveChanges;
  finally
    FreeAndNil(MyCar)
  end;

And then, probably years later, somebody thinks that the code above is unnecessarily complex and optimizes it like this:

var
  MyCar: TCar;
begin
  MyCar := TCar.Create;
  try
    MyCar.Length.AssignMetres(4.5);
    if MyCar.HasChanged then
      MyCar.SaveChanges;
  finally
    FreeAndNil(MyCar)
  end;

That’s a reasonable optimization. It looks much cleaner and gets rid of an unnecessary variable declaration. But it does not work!

Why? I’m glad you asked. Let’s have a look at what happens in this line:

MyCar.Length.AssignMetres(4.5);

It first calls the getter for the Length property, returning a copy of the FLenght field. Then it calls the AssignMetres method of that record, which changes the FMillimetres field of the record. Now, the question: What will be the value of the field MyCar.FLength? It will still be 0 (assuming the constructor of TMyCar does not initialize it otherwise), because we changed the value of the copy, not the value of the field. Also, MyCar.HasChanged will still be False, because the setter for Length has never been called.

So, advanced records should not have methods that change the record’s values. In the example, the solution would be something like this:

type
  TDistance = record
  private
    FMillimetres: Int64;
  public
    class function FromMetres(_Value: Double): TDistance; static;
    function InMillimetres: Int64;
    function InMetres: Double;
    function InKm: Double;
  end;

/// ...
class function TDistance.FromMetres(_Value: double): TDistance;
begin
  Result.FMillimetres := Round(_Value * 1000);
end;

So, a class function FromMetres would be used to return a new TDistance variable initialized to the given metres value.

And it would be used like this:

var
  MyCar: TCar;
begin
  MyCar := TCar.Create;
  try
    MyCar.Length := TDistance.FromMetres(4.5);
    if MyCar.HasChanged then
      MyCar.SaveChanges;
  finally
    FreeAndNil(MyCar)
  end;

The setter method for Length gets called does its thing. Everybody is happy. Until, of course, some smart ass like me thinks that an TDistance.AssignMetres would be a great idea …

%d bloggers like this: