Why doesn't code from "The Tomes of Delphi" compile? - delphi

I'm trying to use the TDRecLst and TDSplyCm units from the code included with The Tomes of Delphi, but I get a compiler error in TDBasics.pas:
I get a similar error in TDStrRes.inc:
What's wrong, and how do I fix it?
The code is available from the author.

You're evidently using a Delphi version that's newer than Delphi 6. Despite being updated in 2005, the code from that book only detects up to that version of Delphi. TDDefine.inc defines a number of compiler symbols based on the version it detects, but when the version you're using isn't anything it recognizes, it defines no symbols. That eventually leads to problems later when the compiler encounters code like this in TDBasics.pas;
implementation
uses
{$IFDEF Delphi1}
WinTypes, WinProcs;
{$ENDIF}
{$IFDEF Delphi2Plus}
Windows;
{$ENDIF}
{$IFDEF Kylix1Plus}
Types, Libc;
{$ENDIF}
{$IFDEF Delphi1}
{$R TDStrRes.r16}
{$ENDIF}
{$IFDEF Delphi2Plus}
{$R TDStrRes.r32}
{$ENDIF}
{$IFDEF Kylix1Plus}
{$R TDStrRes.r32}
{$ENDIF}
const
UnitName = 'TDBasics';
Since none of Delphi1, Delphi2Plus, or Kylix1Plus is defined, the uses clause is empty. When we ignore all the compiler directives and inactive code blocks, the compiler ultimately sees code like this:
implementation
uses
const
UnitName = 'TDBasics';
That's why the compiler complains about expecting an identifier instead of const.
To fix it, you need to teach TDDefine.inc to recognize your version of Delphi. Easier, though, might be to ignore all the version-detection code and hard-code all the symbols that apply to the version you're using. As long as you never use any version older than Delphi 6, all the symbols will apply to all your versions.
Find the following block of code in TDDefine.pas:
{$IFDEF VER140}
{$DEFINE Delphi6}
{$DEFINE Delphi1Plus}
{$DEFINE Delphi2Plus}
{$DEFINE Delphi3Plus}
{$DEFINE Delphi4Plus}
{$DEFINE Delphi5Plus}
{$DEFINE Delphi6Plus}
{$DEFINE HasAssert}
{$ENDIF}
Remove the first and last lines so that the remaining $DEFINE instructions are processed unconditionally.

Related

Compiler directive is not set in Delphi 10.2

I have the following unit implementation in my delphi probject.
uses
{$IFDEF Ver270} JSON, {$ELSE} DBXJSON, {$ENDIF}
In Delphi XE4 DBXJSON will be implemented - that's fine.
In Delphi XE6 JSON will be implemented - that's fine too.
But in Delphi 10.2, DBXJSON will be implemented - not JSON. Why? Is this a bug in Delphi 10.2?
This is not a bug, it is by design. Each version has exactly one VERXXX definition. VER270 is defined in XE6 and XE6 only. For version 10.2 VER320 is defined.
In your scenario it is much simpler to use code like this:
uses
{$IF RTLVersion >= 27} JSON, {$ELSE} DBXJSON, {$IFEND}
Another option is to use a standard include file like jedi.inc. This takes the pain out of such conditional statements. If you use jedi.inc then you can code it like this:
uses
{$IFDEF DELPHIXE6_UP} JSON, {$ELSE} DBXJSON, {$ENDIF}

Delphi CompilerVersion directive issues

I have a library code file which is shared between Delphi 5 and DelphiXE2. I am attempting to add anonymous method functionality to the code file, but only for DelphiXE2 projects (since Delphi 5 doesn't support anonymous methods). It seemed I should be able to use the CompilerVersion (Note: I don't want to limit it to DelphiXE2, just in case we ever upgrade).
{$IF CompilerVersion >= 23}
{$DEFINE AnonymousAvail}
{$IFEND}
This worked nicely in XE2, but it turns out, Delphi 5 doesn't support the $IF directive. I decided to wrap it in an $IFDEF. This worked nicely in Delphi 5, but XE2 also doesn't seem to have CompilerVersion defined, so AnonymousAvail is not defined.
{$IFDEF CompilerVersion}
{$IF CompilerVersion >= 23}
{$DEFINE AnonymousAvail}
{$IFEND}
{$ENDIF}
Any help would be appreciated.
Note: I cannot move anonymous method code to a different code file.
Do what the documentation says:
{$IFDEF ConditionalExpressions}
{$IF CompilerVersion >= 23.0}
{$DEFINE AnonymousAvailable}
{$IFEND}
{$ENDIF}
Be sure that the outer condition is as shown (and closed with ENDIF) and you can use CompilerVersion and other constants and expressions inside.
You can also use
{$IF defined(BLAH)}
or, one of my favourites:
{$IF declared(AnsiString)}
etc...
FWIW, I noticed that the example in the link comes, almost verbatim, from my Console.pas unit.

TurboPower B-Tree Filer and Delphi XE2 - Anyone done it?

I might be the last guy on the planet relying on B-Tree Filer but I made the jump from Delphi 2007 to XE2.
After getting over all the AnsiChar and PAnsiChar issues, the code now crashes with a Range Check with zero items on a page.
Is anyone successfully running B-Tree Filer under Delphi XE2? If so, how'd ya do it? :)
Update Range check error here:
procedure IsamUnPack(var Page : IsamPage; KeyL : Word);
var
I, K, S : Word;
P : Array [0..0] Of Byte absolute Page; {Real bounds [0..65535]}
begin
K := KeyL + 9;
S := Pred (Page.ItemsOnPage) * K + 6;
if KeyL <> MaxKeyLen then begin
for I := Page.ItemsOnPage downto 2 do begin
Move(P[S], Page.ItemArray[I], K); // Range Check error in Warren P's suggestion
S := S - K;
end;
end;
end;
While Page.ItemsOnPage should never be zero (the Range Check error is valid here) it may have been caused by data alignment issues. This code, added to BTDEFINE.INC seems to be doing the trick...
{$IFDEF VER230}
{$DEFINE UsingDelphi}
{$ENDIF}
{$IFDEF VER230} {Delphi XE2}
{$A-} {align data on byte boundaries}
{$B-} {short circuit boolean evaluation}
{$H+} {long string support}
{$I-} {suppress I/O checking}
{$J+} {writeable typed constants}
{$P-} {do not allow open string parameters}
{$Q-} {overflow checking off}
{$R-} {range checking off}
{$T-} {no type checked pointers with #}
{$V-} {no var string checking}
{$X+} {extended syntax on}
{$DEFINE Delphi2006}
{$DEFINE Delphi1Plus}
{$DEFINE Delphi2Plus}
{$DEFINE Delphi3Plus}
{$DEFINE Delphi4Plus}
{$DEFINE Delphi5Plus}
{$DEFINE Delphi6Plus}
{$DEFINE Delphi7Plus}
{$DEFINE Delphi2005Plus}
{$DEFINE Delphi2006Plus}
{$ENDIF}
I did a quick port, and I currently have it basically working, well enough for the included Delphi demo to work. My first try failed when I overlooked some string -> ansistring changes in the DEMO CODE, which caused the demo code function PadCH to malfunction. After I fixed that, the demo, and underlying library appears functional, at least for reading, but I did not test writing, modifying, and creating files yet. The above file in the demo was created in an earlier version, so at least it's binary-read compatible. I wouldn't be surprised if there were lots of bugs, data corruption issues, and so on, so please do not use this code in production, or if you do, you do so at your own risk.
My work is here: hosted at microsoft skydrive (4.3 megs, ZIP)
(filename tpbtreefiler_xe2_v2.zip)
Update Function IsamUnpack is in ISAMWORK.INC.
Update2 It appears that the OP has discovered now that adding some ifdef-version-constant support causes the {$R-} and some alignment flags to be switched on which are also required, for the library to work properly. May I suggest the following different way of declaring in BTDEFINE.INC, that gets around a classic Delphi "break every time we change Delphi compiler versions" by using a comparison that won't break on the next delphi release:
{$IF CompilerVersion > 20.0 }
{ Keep working from Delphi 2009 UP}
{$DEFINE UsingDelphi}
{$A-} {align data on byte boundaries}
{$B-} {short circuit boolean evaluation}
{$H+} {long string support}
{$I-} {suppress I/O checking}
{$J+} {writeable typed constants}
{$P-} {do not allow open string parameters}
{$Q-} {overflow checking off}
{$R-} {range checking off}
{$T-} {no type checked pointers with #}
{$V-} {no var string checking}
{$X+} {extended syntax on}
{$DEFINE Delphi2006}
{$DEFINE Delphi1Plus}
{$DEFINE Delphi2Plus}
{$DEFINE Delphi3Plus}
{$DEFINE Delphi4Plus}
{$DEFINE Delphi5Plus}
{$DEFINE Delphi6Plus}
{$DEFINE Delphi7Plus}
{$DEFINE Delphi2005Plus}
{$DEFINE Delphi2006Plus}
{$ENDIF}
Update 3 I suspect there are still porting issues in the code, that could cause data loss and data file corruption. Here's an example where the number of records (which should be a number in the range around 50 in my demo app) is being reported as a number > 1 million, which is clearly invalid.
Two more items to check
change "string" to "AnsiString"
If target build is 64 bits, your changes won't make it ready yet
Cheers
After I had it all working, I found that re-indexing from a XE2 app broke the B-TreeFiler (BTreeFiler) tables with a isam error #10122 ("The page size for the file block is greater then the MaxPageSize"). Here is the fix:
This one needs to be changed in Filer.pas for reindexing to work:
IsamInfoRec = packed Record
InfoRec : IsamSmallInfoRec;
DummyFill : AnsiChar; <<<<<<<< Here!
KeysUsed : LongInt;{Must start on an even offset for C-compatibility}
PageSizeUsed : Word; {!!.42}
End;
I hope Warren (above) updated his work with this additional fix. I have also posted this one on the TurboPower BTreeFiler SourceForge location.

How can i detect specific RTL features at compile time?

For sake of example lets check for infamous TStrings.StrictDelimiter:
{$IF Declared(TStrings.StrictDelimiter)}
{$MESSAGE WARN 'Beware of TStrings.StrictDelimiter which is False by default!'}
{$IFEND}
However, Declared compiler intrinsic reports syntax error on conditional line: E2029 ')' expected but '.' found. (tested on XE)
For Delphi XE2 I'm using this :
{$IFDEF BDS9}
Result.VersionString := 'Delphi XE2 ' +
{$IF NOT DECLARED(Consts.SStyleFeatureNotSupported)}
'(original release version)'
{$ELSE} {$IF NOT DECLARED(FireMonkeyVersion)} // D2010 chokes when scope (FMX.Types) is mentioned!
'Update 1'
{$ELSE} {$IF NOT DECLARED(System.TestSSE)}
'Update 2'
{$ELSE}
'Update 3'
// TODO : Update this for any following update!
{$IFEND} {$IFEND} {$IFEND}
;
{$ELSE}
{$IFDEF BDS7}
Result.VersionString := 'Delphi 2010';
{$ELSE}
{$MESSAGE ERROR 'Extend this!'}
{$ENDIF}
{$ENDIF}
In other words : I test for the existence of symbols that are introduced since any particular delphi-version. The same construct can be used to set a variable or constant or compiler define, so further code can use these instead.
Note : I keep a backup around of the Source folder for all Delphi versions that I've had installed in the past few years. Putting these folders through a tool like BeyondCompare and browsing through the differencing files, will quickly give you a few symbols that you can test for....
Unfortunately this kind of expressions aren't supported, you have to know in which RTL / compiler version some feature was introduced and then use predefined conditional symbols like VER<nnn>, RTLVersion, CompilerVersion etc.

Are conditional expressions broken within packages?

Consider the following snippet:
requires
designide,
rtl,
vcl,
{$IF RTLVersion < 19.0} // E2026 Constant expression expected
//{$IF CompilerVersion = 22.0} // same as above
vcljpg;
{$ELSE}
vclimg;
{$IFEND}
It seems to be absolutely syntactically correct. However, the compiler chokes on it and reports Constant expression expected. What really happens here?
Technical: currently tested on XE (15.0.3953.35171) only.
Of course, workaround suggestions are welcome too.
I found the same issue in the past even with delphi 2007. As workaround, I use a inc file with the conditional defines and then use {$IFDEF} instead of {$IF}
something like so
{$I MyDefines.INC}
requires
designide,
rtl,
vcl,
{$IFDEF DELPHI_XE_UP} //the DELPHI_XE_UP is defineed inside of MyDefines.INC
uNewlib;
{$ELSE}
uOldLib;
{$ENDIF}
package modules are different from program and library modules. They do not contain executable code and you cannot use units. Therefore, symbols like RTLVersion are simply not visible from a package file. Your only option is to use $IFDEF.
I'm convinced what i just found the cause. Consider the following:
{$IF not Declared(RTLVersion)}
{$MESSAGE WARN 'There is no RTL'}
{$IFEND}
{$IF not Declared(CompilerVersion)}
{$MESSAGE WARN 'nor are compiler intrinsics at all'}
{$IFEND}
{$IF not Declared(System)}
{$MESSAGE ERROR 'Because package not uses System implicitly'}
{$IFEND}
So, it appears to be what compiler behaves correctly, but issues a rather misleading (if not erroneous) message about symbol not being a constant expression, while symbol in question actually is undeclared!

Resources