hcOPF supports Delphi 10.4 Sydney

July 25th, 2020

Things have been a little crazy in case you hadn’t noticed.  Between COVID-19, the economic fallout and all the turmoil in our nearest neighbour, I plum forgot to blog about updating hcOPF to support Delphi Sydney 10.4.

It might also be because adding support for a newer compiler version doesn’t take a great deal of effort. I normally just copy the ones from the previous version, and modify them as needed for the new one.  It would of course be a whole lot easier if {$LIBSUFFIX AUTO} was implemented and I could use that as well (or something like it) to set the output folder. Then I wouldn’t even need to make a copy and update the projects!

DevExpress now Supporting FMX

July 24th, 2020

I was so excited when I read Frank’s post today about DevExpress offering a FMX grid control.  So I went to download it, only to discover that in order to get the CTP release, an * “Active VCL Subscription or Express GridPack is required”.

Sadly I don’t personally have an active subscription so looks like I will have to wait until the release to give it a spin.  For those of you lucky enough to meet the requirements, who haven’t already purchased a TMS or Infpower grid for FMX, let me know what you think.  For the VCL, the Quantum Grid has practically been the defacto standard in most applications I’ve worked on, so I’m very interested to see what DexExpress is offering for FMX.

DevExpress took a wait and see position with FMX, so it’s very encouraging for the Delphi ecosystem to see them throw their hat into the ring.

10.4 Good Buddy

May 27th, 2020

For a long time there has been a lot of complaints about Error Insight in Delphi giving more false positives (red swiggly lines) than actual problems. It also slowed down the IDE so much that I for one, simply turned it off to avoid the noise and IDE lag. Code Insight also had it’s fair share of issues in terms of parsing blocking the IDE main thread making it unresponsive. The suggestions were not always relevant either.

Then there were IDE updates distributed as a ZIP file that you had to manually unzip and place the files in the correct installation subfolders. There wasn’t even a way to automatically find out if patches were available. The larger updates even required a re-install.

If you thought EMBT did not hear you, they just responded with a big 10-4; RAD Studio/Delphi 10.4. This is arguably the most significant release since Delphi 7. The new Delphi LSP unifies all the IDE code parsing, providing the compiler’s interpretation of the code. This makes Code and Error Insight much faster without blocking the IDE main thread so you can continue to actually use the IDE to….code. It also makes Code and Error Insight, well… more insightful.

Besides the plethora of fixes, there are other major features like managed records and the unification of all platform memory models, and another personal favourite that caught me by surprise; GetIt can now apply patches.

If you’re not on subscription or haven’t updated your Delphi version for some time, 10.4 is a release worthy of getting to know.

Exit Stage Left

May 8th, 2020

The Exit procedure has been around as long as Goto in Object Pascal.  Goto is considered an antiquated command, but what about about Exit?  When should it be used if at all?

I have often seen guard code at the beginning of a method that performs an Exit if the parameters passed are not appropriate or indicate the method does not need to act on the parameters passed.  This might happen in a method like this:

procedure TSomeForm.CheckHaveID(const AID: string);
begin
  if AID <> EmptyStr then
    Exit;

  ...other code to populate AID if necessary...
end;

In this case the first few lines are a common place to see such guard code. In fact, guard code is usually only in place to throw exceptions to let the developer know prerequisites have not been met. Exit calls may be more applicable depending on the purpose of the method but I would caution that taking this to extreme is the equivalent of making a function that returns a boolean for success and then never checking the result in the calling code. It’s a bad practice that will one day bite you. Pick a strategy, document it, and adhere to it. Consistency is king even if the code is less than ideal. Consistent code is easier to re-factor later.

Anyway, I digress, contrast the first method to the following:

procedure TSomeForm.CheckHaveID(const AID: string);
begin
  if not FIShouldCallCheckHaveID then
    Exit;

  if AID <> EmptyStr then
    Exit;

  ...other code to populate AID if necessary...
end;

Here a private field variable is accessed within the called method to determine if the called method should in fact have been called. Seems a little “ass backwards” to me. You don’t go to Vegas and then ask yourself “should I be in Vegas?”. You think before you act. If the method should not have been called, then the caller should make that determination and simply not call it. There is overhead with every method call.

What has also happened now, is that the CheckHaveID method is coupled to the form, making refactoring harder. This is the problem with business logic being tied to a form. Business logic needs to be applied throughout the application but it can’t do that if some variation is embedded in all related forms. Let’s say I want to pull the CheckHaveID method out into another unit as a classless or static class method so it can be shared in another form. Now I have to deal with changing the calling logic to account for the FIShouldCallCheckHaveID condition. If this had been done in the first place the risk of breaking code would have been non-existent.

The argument can be made that if you have 5 early Exits these tests should not be duplicated in every place the method is called, that it just doesn’t make sense. I would respond to such a statement that code duplication is a primary code smell that should result in re-factoring, In this case, all 5 instances of the calling code would be extracted into another method where the decision would be made. Just because putting the logic test in guard code eliminates duplication doesn’t mean you’re putting it in the right place.

In my opinion conditional flow logic should be implemented in the caller. In keeping with the Single Responsibility Principle, a method should do one and only one thing and it’s name should reflect exactly what it does. This keeps code readable and understandable. That was the whole purpose behind step-wise refinement I was taught in Computer Science. Embedding business logic (execution flow) in a method that is named DoX means it should not also do Y. Such code requires minimal documentation because it’s name says it all (well almost).

Structured programming purists would say never use Exits, but more modern engineers like Martin Fowler are advocate smaller methods with multiple Exits. The keyword here is smaller methods. In legacy code I’ve seen methods spanning several hundred lines. Try to find an Exit call in that code!

Not using Exit calls can certainly increase the level of nested if..then..else statements which can also be hard to follow and lead to bugs if begin/end blocks are not used. Again size does matter (method size).

I think as with most things in life, there is a balance, and at a certain point it becomes a personal choice but I think in at least this case, I have made an argument for avoiding the code shown.

Delphi Basics site sums it up quite nicely:

Warning : use with caution - jumping is a concept at odds with structured coding - it makes code maintenance difficult.

Making Sure Your App Doesn’t Leak like a Sieve

April 19th, 2020

If you have been following the Delphi Roadmap, you know that a Unified Memory model across all supported platforms is coming.  That memory model is Delphi’s standard ownership model coupled with optional interface refcounting.  IOW, it’s completely up to the developer to ensure proper memory management just as it has always been.  Once this is the case, memory management, especially on memory “constrained” mobile devices will be very important.

FastMM4 provides memory leak reporting on shutdown, and since everyone has 80% or better unit test coverage using this feature, they probably don’t need have another tool in their toolbox ;-).  Just in case you don’t I would consider adding DeLeaker to your toolbox.  It does far more than just check for memory leaks, and it does so at run-time!

DeLeaker comes as both an external EXE and a very similar UI integrated into RAD Studio under it’s own menu.  The documentation is well written and tutorials are available to get you up to speed quickly.

One of the things I like most about DeLeaker is the ability for it to monitor resource consumption live.  As you use the application you can see the memory utilization and spot potential leaks as memory consumption grows, confirm them using DeLeaker’s detection and go to the relevant code with a double click.

It also supports taking snapshots as the application runs, and comparing them.  Did I mention it monitors not only memory, but also GDI, handles and other resources?

You might think I have AQTime included with Delphi, and I would ask where?  It hasn’t been provided with Delphi for a number of releases.  I have used AQTime and it’s a great product, but I recently discovered that the standard edition doesn’t even support 64 bit EXEs.  Smartbear seems more focused on it’s new web product portfolio so perhaps choosing a smaller vendor committed to supporting RAD Studio customers would be prudent.  At $599 $799 USD for AQTime Pro compared with $399 USD for Deleaker it’s a good value equation if you don’t need the additional features of AQTime Pro.

I have numerous tools in my toolbox, and I am always looking for new or better ones.  I am now adding DeLeaker to the list.

Happy Birthday Delphi!

February 13th, 2020

February 14th is Delphi’s 25th Birthday.  Yes Delphi was introduced on Valentine’s Day, so no wonder I love it!  Anders Hejlsberg must be a proud papa!  Any tool that lasts 25 years in the software business is a great success.

I was a Turbo Pascal fan in school and graduated to using Delphi as my primary dev tool when version 2 shipped.  It was just better than anything else at the time!  VB didn’t have the speed of compiled code and I hated the language, but not quite as much as I hated C/C++.  Pascal was just so much more readable and elegant.

Not a day goes by that I regret not being a member of the M$ herd and dealing with all the churn in their APIs.  Delphi kept moving forward with an eye on backwards compatibility.  It says a lot when you can compile 25 year old source essentially unchanged.  This is one of the reasons I have seen, and helped build many industry leading applications using Delphi.

A lot of things have changed in the development space in the last 25 years, and Delphi has grown up during that time.  It now supports multiple operating systems, has a cross platform UI library, and is reaching for the clouds.  There is little you cannot do today with Object Pascal!

If you think Delphi is an old outdated technology, then think again.  Check out all the #Delphi25th celebrations. Give it a try…you never know it just might be your cup of tea!

IDE Generation of Interface Methods

December 29th, 2019

Just a note to myself so I don’t forget, it’s possible to generate the methods for an interface in a new class that implements it, by pressing Ctrl+Space within the new class declaration.  A list of methods will appear, and you can multi-select them, press Enter and the method declarations will be placed in the class definition as public methods.  Using Class Completion (Ctrl+Shift+C) will the generate the implementation method bodies.

Not sure if or where this is documented as it only took 20 years before I came to know it via others on DelphiPraxis.

hcOPFCodeGen

November 30th, 2019

hcOPF was germinated around the time frame of Delphi 7 when I realized that dataset based applications of any complexity really became a maintenance nightmare over time.  I found that data was often stale and inconsistent in a database application because each view was tied to a different dataset.  There was no way to know when the dataset had to be refreshed, so unnecessary refreshes were often performed causing the applications to be very chatty with the database server.  Also the validation logic tended to be implemented in the UI forms and so over time, as the business rules changed, it became difficult to ensure consistent, correct validation.  There was no separation of concerns, UI code was intermixed with business and database logic.  Delphi RAD apps became spaghetti code or big balls of mud. Using objects to model the real world is much more intuitive.

Many developers using Java and other technologies were using ORMs so I started working on one myself and along the way borrowed ideas from other ORMs.  I wanted a framework that provided the encapsulation of data in objects, making applications easier to maintain over time.  Encapsulation has the advantage that to add a new attribute to a business object you only have to modify the class and that attribute becomes available throughout the application, wherever an instance of the class is used.  Contrast that to a dataset based application where you would have to modify practically every form and query/dataset where the new column should appear. Missing places, or not providing validation when editable was easy to do.

In 2009 I released hcOPF as an open source project on SourceForge.  hcOPF is different than modern ORMs that use PODOs.  Such frameworks use Delphi’s newer RTTI to access attributes.  Back when I started the framework the extended RTTI wasn’t available, so I created an ancestor class from which all persistable objects must descend.  To define a business object the framework requires you to create mapping MetaData to describe the object in terms of tables and columns.  An Object can be from a single table or the result of multiple tables joined together.

Without knowing the framework, it can be a little daunting to create object definitions, and even when you know the framework, creating object MetaData can be a tedious error prone affair.  I used the copy/paste/modify approach but that doesn’t take into account the datatype the data access layer uses for a column.  Different DALs can map columns to different field types so this resulted in lots of additional changes needed that were only discovered at run-time. For these reasons I have been working on a Code Generator tool; hcOPFCodeGen.

The code generator will currently scaffold an object definition, but it needs many improvements to support all the permutations in defining an object.  I am still working out all the use cases.  For now, it should help save time by generating a basic definition that can be further customized as needed. I have removed all commercial dependencies.  The code generator now only requires the JVCL and VirtualTreeView.  The code can be found here.

The beauty of Delphi is that it allows you to leverage your code investment with minimal churn.  It’s quite easy to migrate legacy code to later versions of Delphi with small incremental changes.  hcOPF can be used with Delphi 7 to Delphi Rio.  You may encounter some compilation issues because I have not employed a CI server to maintain hcOPF, but I plan to in the near future as time permits.

hcOPF Dialog Validation

September 30th, 2019

Reminder to self, when presenting a dialog for editing an hcOPF object a generic OK button click or action Execute handler is:

procedure TfrmObjectDialog.btOKClick(Sender: TObject);
var
  ValidationErrorList: ThcValidationErrorList;
begin
  //switch focus to another TWinControl to ensure the current focused editor
  //updates it's Subject
  SelectNext(ActiveControl as TWinControl,True,True);

  ValidationErrorList := ThcValidationErrorList.Create();
  try
    if hcUIObjectBinder.BoundObject.IsValid(ValidationErrorList) then
    begin
      hcUIObjectBinder.BoundObject.Write(osRDBMS,False);
      hcUIObjectBinder.BoundObject := nil;
      ModalResult := mrOk;
      Close;
    end
    else
    begin
      MessageDlg(Format('Please Correct the Following Error(s)'#13#10#13#10'%s',
        [ValidationErrorList.Text]),mtWarning,[mbOk],0);

        //focus editor for first invalid attribute
        if (ValidationErrorList.Count > 0) and
         (assigned(ValidationErrorList.Items[0].Attribute)) then
        hcUIObjectBinder.FocusControlForAttribute(
           ValidationErrorList.Items[0].Attribute
        );

     ModalResult := mrNone;
    end;
  finally
    ValidationErrorList.Free;
  end;
end;

And for the Cancel button:

procedure TfrmObjectDialog.btCancelClick(Sender: TObject);
begin
  hcUIObjectBinder.BoundObject.UndoChanges;
  hcUIObjectBinder.BoundObject := nil;
  ModalResult := mrCancel;
  Close;
end;

or if you are using the VCL you can Inherit or Copy the hcDialog object found in the \Source\UI\VCL folder. I would suggest adding it to the Object Repository to make it easier to do so. There are comments in the unit suggesting how to use either design-time or run-time bindings.

Doubling Down - Fixing Min/Max() for Doubles

September 17th, 2019

Today I discovered that the built-in overload for Min() and Max() double in the Math unit do not always work as expected when you don’t care about a great deal of precision.  If you search on how to compare floating point types there are many StackOverflow answers, none of which have been adopted over the years by the built-in library.  I have come across this issue of the comparison of floating type values more than once, but keep forgetting not to use built-in functions.  I discovered that the code I was using to determine the range of values was failing on

MyX := Max(MinDouble,0);

MyX was not 0 as I expected because the value is used for a measure of distance for which I only cared about 3 decimal places, so I created a new Math unit with functions that return the expected result.  Hopefully this helps someone else or prevents me from falling into the same hole again.  Naturally these methods should also have overloads for the other floating point types.

unit unMath;

interface

uses
  Math,
  Types;

function Max(const Value1, Value2 :double) :double;
function Min(const Value1, Value2 :double) :double;

implementation

function Max(const Value1, Value2 :double) :double;
var
  relationship :TValueRelationShip;
begin
  relationship := CompareValue(Value1,Value2);
  if (relationship = GreaterThanValue) then
    Result := Value1
  else
    Result := Value2;
end;

function Min(const Value1, Value2 :double) :double;
var
  relationship :TValueRelationShip;
begin
  relationship := CompareValue(Value1,Value2);
  if (relationship = LessThanValue) then
    Result := Value1
  else
    Result := Value2;
end;

end.