Useful Delphi code

Posted by David Glassborow Thu, 04 Jan 2007 11:12:00 GMT

Just realised I haven’t written a blog post for 2.5 months, and I’ve just got back from a lunchtime surfing session, so I’m not really in the mood for real work. I thought I’d post some of my basic Delphi library code I use time and again in the hope it is of some use to other Delphi developers. Do what ever you want with it, I don’t guarantee its without bugs, but most of it has been used in production code for quite a long time.

The full file with all the implementation can be found here: Source

The following are useful for conditional logic for development vs production code, debugging, etc.:

procedure BreakpointInIDE;
function CurrentExceptionMessage: string;
function IsRunningUnderIDE: boolean; inline;

The following function is useful for preventing the unsitely errors that can occur as a Delphi Application throws Exceptions as it shuts down. While ideally this should never happen, previous exceptions can leave forms in a have constructed state, and the TApplication’s attempt to clean up them then causes horrible dialogs frightening users. Even the IDE has been known to do this. The code hooks the various Exception raised pointed in the RTL to silently ignore the errors. If running inside the IDE, it will also post a message to the event log, and cause the IDE to breakpoint to highlight there is an issue.

procedure HookShutdownToHideExceptions;

Helper Classes for TList, TStream and TStrings. In particular I use the ID accessors of TStrings a lot when putting records into UI elements. Makes it very easy to look up the ID of the record in a combo box, for example. It uses the Object field in a list item to store the ID. Helpers require at least Delphi 2006.

TListHelper = class helper for TList
  procedure FreeSelfAndContainedObjects;
end;

TStringsHelper = class helper for TStrings
private
  function GetID(Index: Integer): ID;
  procedure SetID(Index: Integer; const Value: ID);
  function GetDescFromID(ID: ID): string;
  function GetIDFromDesc(const Desc: string): ID;
  function GetObjectByString(text: string): TObject;
public
  function AddPair(const Name, Value: string): integer;
  function AddID(const Desc: string; PrimaryKey: ID): Integer;
  function AddObj( const Name, Value: string; Obj: TObject ): Integer;
  function ContainsKey( const Key: string ): boolean;
  function ContainsString( const Text: string ): boolean;
  function ContainsValue( const Value: string ): boolean;
  procedure FreeSelfAndContainedObjects;
  function IndexOfID( ID: ID ): integer;
  property DescFromID[ID:ID]: string read GetDescFromID;
  property IDFromDesc[const Desc: string]: ID read GetIDFromDesc;
  property IDs[Index: Integer]: ID read GetID write SetID;
  property ObjectByString[ text: string ]: TObject read GetObjectByString;
end;

TStreamHelper = class helper for TStream
public
  function AsString: string;
  function RewindToStart: TStream;                                      // Returns self to allow easy chaining of calls
  procedure WriteString( const Text: string );
  procedure WriteStringAndBreak( const Text: string );
end;

The following two classes are 2 example simple container objects based on records rather than classes. The nice thing is they don’t have to be memory managed like classes do, the compiler will handle all of that for you. They are not as fully featured as string lists, but are useful for simple tasks. They are also reasonabilly efficient, as the dyanmic arrays they use are copy by reference. The only thing to remember is to use the Clone method if you actually want a whole new copy of the data. I have about 4 or 5 different variants of these to store ID => Name, Name => Variant, etc, but you get the idea.

TSimpleStringList = record
private
  FItems: array of string;
  function  GetCount: integer; inline;
  function  GetItem(index: integer): string;
  procedure SetItem(index: integer; const Value: string);
public
  function  Add(const line: string): integer;
  procedure Clear;
  function  Clone: TSimpleStringList;
  function  Contains(const s: string): boolean;
  function  IndexOf(const s: string): integer;
  function  IsMissing(const s: string): boolean;
  property  Count: integer read GetCount;
  property  Items[index: integer]: string read GetItem write SetItem; default;
end;

TSimpleStringListHelper = record helper for TSimpleStringList
public
  function AsNewStringList: TStringList;
  function StringsCommaSeperated: string;
end;

TSimpleDictionary = record
private
  FKeys: array of string;
  FValues: array of string;
  function GetCount: integer; inline;
  function GetKey(index: integer): string;
  function GetValue(const Key: string): string;
  function GetValueByIndex(index: integer): string;
  procedure SetKey(index: integer; const Value: string);
  procedure SetValue(const Key, Value: string);
  procedure SetValueByIndex(index: integer; const Value: string);
public
  function  Add(const Key, Value: string): integer;
  function AsNewStringList: TStringList;
  procedure AddStrings(Strings: TStrings);
  procedure Clear;
  function Clone: TSimpleDictionary;
  function ContainsKey(const Key: string): boolean;
  function IndexOfKey(const Key: string): integer;
  function IndexOfValue(const Value: string): integer;
  property Count: integer read GetCount;
  property Keys[index: integer]: string read GetKey write SetKey;
  property Values[const Name: string]: string read GetValue write SetValue; default;
  property ValueFromIndex[ index: integer ]: string read GetValueByIndex write SetValueByIndex;
end;

TSimpleDictionaryHelper = record helper for TSimpleDictionary
public
  function KeysCommaSeperated: string;
  function SemicolonDelimitedString: string;
  procedure LoadFromSemiColonDelimitedString( Text: string );
  procedure LoadFromStringList( StringList: TStringList );
  function Text: string;
  function ValueOrDefault( const Key: string; const Default: string ): string;
end;

Here is a simple subclass of Exception to allow Assertions on any Exception derived object. I tried to do this via a helper class, but the ‘self’ object in a class level helper called on a class does not appear to be a valid reference, so I couldn’t find a way to get it to work. Looking through the code to Indy inspired this approach to using Exceptions, I never use the raise statement anymore.

EBase = class( Exception )
  class procedure Assert( Test: boolean; const Msg: string );
  class procedure Throw( const Msg: string ); virtual;
end;

The last function is function ShowHourglass: IUnknown; This can be called in a UI function to make a form show a cursor, and when the function exits (normally or after an exception) be sure the cursor is returned to its previous state. The return value (IUnknwon) does not need to be stored, behind the scenes Delphi will store it anyway and make sure the compiler puts a hidden Try ... Finally in at the end to make sure it is cleaned up. The cleaning up of the interface is what we use to reset the cursor.

Anyway hopefully this of some use to somebody. Happy New Year to everyone, here’s to a good year from CodeGear.


ATOM / RSS and Comments

Posted by David Glassborow Tue, 17 Oct 2006 09:00:00 GMT

After all the effort of changing Weblogs, I’ve realised that Typo ‘out of the box’ does not support comments added to its syndication streams. I’ve hacked around in the code and managed to get something working. Rails XML helpers make this stuff pretty easy.

Weirdly there does not seem to be an agreed format for doing comments in ATOM at all, where as RSS has various different comment tags. I’ve used wfs:commentsRss and slash:comments nodes in both formats, with the comments just in Rss 2. The Feed Validator seems happy.

Anyway the following work well with my aggregator, Rss Bandit, as well as IE 7. If anyone has any problems with other aggregators, can you let me know. Thanks.

Local feeds:


Devco Wish List

Posted by David Glassborow Mon, 16 Oct 2006 12:26:00 GMT

This is my wishlist for Delphi when the new company running it is planning the next version. My requests are not large things (except maybe the last one), and are in order of priority.

Summary:

  • Language construct: return
  • . operators on basic types
  • Include all source code (midas, dbExpress)
  • Ruby

Return

Readable concise code is something we all try to acheive when coding. I really miss the ‘return’ statement from C, because it makes code more concise. I’m bored of writing:

if blah then
begin
  Result := -1;
  exit;
end;

I want to be able to write just

if blah then Return(-1);

Shorter, and the intention of the statement is much more obvious. I would imagine it would be fairly easy to add to Delphi as a pseudo function (like ‘continue’ and ‘break’).

Dot Opeators

I’ve been doing a fair amount of Ruby recently, as well as some C#. Having the dot operator available on all basic types is lovely, and makes code much more readable. I’d much prefer

address.length

over

length(address)

Less visual clutter, much more OO. Even if behind the scenes is just syntactic sugar to call the length procedure, that would be a huge win for the readability of Object Pascal in these Object Orientated times.

Source Code

At Concept First we currently use the following third party libraries:

  • Devexpress: Quantim Grid, Quantum Bars
  • TChart charting component
  • CoreLabs dbExpress driver for SQL Server

We will only buy a component if it comes with full source code. Why ?

  1. Fixing bugs when we find them
  2. Adding new features
  3. Tracking their changes under source control

We’ve made changes to all of the components we use, and I have all their source code under Subversion, so I can see what they have changed and see what possible impact it could have on my code.

One of the great strengths of Delphi has always been that the VCL comes with full sources. The two exceptions are the TClientDataset (midaslib.pas) and the dbExpress drivers. Its very annoying not to be able to debug into their code. I know Midas is in C++, but I have the full BDS with C++ support so that is not an issue. I use TClientDataset all over the place, and there are a few easy extensions that could make life much easier.

Ruby

As mentioned before, i’ve been playing around with Ruby and Ruby on Rails recently, and the first thing I had to do with is knock up a quick syntax highlighting editor using the SynEdit control. Having Ruby (and in an ideal world Javascript) in Delphi, with code complete, would be great, something I would be happy to pay for). Web development seems to be the way of the future, and having a nice IDE supporting Ruby could be a big win for Delphi / Devco.

Summary

I don’t know if my use of Delphi is similar to the majority of other users, but my priorities from it are:

  • Fast to develop in
  • Looks nice (generally easy thanks to DevExpress)
  • Source code so I don’t get held up by bugs
  • Reliable and fast IDE
  • Small self contained EXEs, without frameworks to install.

I use delphi for Win32 client applications. I don’t use it for web applications. I now use either Ruby on Rails ( my personal choice ) or Cold Fusion ( trusted in Enterprise environments ).

I’m not interested in:

  • Delphi.net
  • 64 Bit support
  • Mobile development (be nice if Delphi supported, but such a small % of our work I’d be happy enough to use Visual Studio).

Anyway if I could have the requested changes made by the end of next week, that would be great DevCo ;-)

Pretty Please ?


Older posts: 1 2 3 4 5 6