Verity Stob and Me
04 Feb
Well, I think it is safe to say that you’ve arrived in the development business when the (in)famous Verity Stob writes a whole article about something you posted on your Developer Network. And a four page article, to boot! I’m famous!
Anyway, interesting comments from the Stobber — and clearly a response is in order!
Thoughts/comments/responses:
- The compiler will not slow down. I simply will not allow it. (Right, Barry?)
- I dont’ see the interface/implementation sections going away. This is big advantage in my view, and I know I’m not alone on this. In fact, it is a distinct advantage of Delphi. You can get a clean, unencumbered view of the declaration of your class. (And by the way, the interface section is one of the reasons that the compiler is so fast…..) In addition, Class Completion handles doing the declarations with a quick CTRL+SHIFT+C, and I’m shocked Verity doesn’t know about CTRL+SHIFT+UpArrow/DownArrow to easily and immediately moving between the two. No scrolling required!
- Declaring variables anywhere will slow the compiler. I personally don’t like it, but I’m open to it. If the compiler guys tell me we can do this, we can consider it,I guess. I personally like having to declare things at the top of the routine myself.
- You can have case insensitivity when you pry it from my cold dead fingers.
- Ahh — with. Now here is an area of stark agreement. I hate with. I never use it. It has cost me hours of time over the years. I’ve seen some unbelievably hideous code written because with was available. As far as I’m concerned, it is the Spawn of Satan. I’d love to see it banned. But alas, there are millions of lines of code out there that use it, and so it can’t go away. Heck, it’s even in the VCL. So alas, it must stay. Now we may be able to updated the syntax to make it less offensive and horrendous. That might happen. And I love the caution_stupid_programmer_with syntsyntax. Count me in for that feature.
- I agree that Delphi needs better RTTI. That is part of the plan.
- We currently don’t have Garbage Collection on the plate. But if and when it gets there, it will be optional. We won’t force it on you.
- Andreas Hausladen’s value to the Delphi community is immeasurable. We work very closely with Andreas, and Andreas has a very special relationship with our R&D team. Andreas is a pretty humble guy, so I won’t say more, but I think the community has the impression that Andreas is an "outsider", and that isn’t entirely true.
Just a few musings from a guy that has clearly now arrived. ![]()



I hope you mean:
"You can have case sensitivity when you pry it from my cold dead fingers."
Although it would be nice to get an optional compiler warning on case variations.
February 4th, 2009 at 2:26 amHow about fixing name scoping rules? I would love it if the compiler forced me to use the full name of types when they happen to exist in multiple used units. (i.e. Bitmap in Graphics and Windows) Begone with the old resolution method of last unit listed wins, please!
February 4th, 2009 at 3:11 amAt the risk of also sounding ignorant (oh, what the heck)… What is the ctrl-uparrow/downarrow shortcut? I tried it in the editor and it simply moved up or down a line. Is this supposed to jump between the prototype and the body of the method?
February 4th, 2009 at 3:13 amGraham,
Ah, Brilliant! I think Nick’s original post is missing the "shift". I usually resort to an external editor (like Visual Slickedit) to quickly navigate between methods and declarations, but this will be great when I’m stuck in the Delphi editor.
February 4th, 2009 at 3:49 amNick: Verity Stob has written about you? I am not worthy
I won’t comment on her proposals only to note in passing that interface/implementation is truly the Spawn of Satan. kthxbai
Cheers, Julian
February 4th, 2009 at 5:08 amCraig –
I’d answer, but I don’t remember where I said that.
Nick
February 4th, 2009 at 7:21 amNick, excellent. I was really pleased to read your position on these points. Well, with the exception of WITH. WITH is an excellent piece of syntax that used judiciously vastly improves the implementation and presentation of cumbersome code blocks. If you can’t get the nails pounded all the way in, and the wall/house falls down because you were using your saw as a hammer, it doesn’t mean you should throw your saw away as useless - I hear they ‘cut’ wood really well.
February 4th, 2009 at 7:53 amDeclaring variables anywhere will slow the compiler.
I don’t understand this. Why would it make a measurable difference?
February 4th, 2009 at 8:49 amSpeaking of WITH, you know what would be slick is if you could set a compiler warning to "deprecate" certain language features. That way a team can say "no with and no goto" and then all the developers will get a compiler warning on new code to remind them not to do it, and old code so they know where to fix it. A co-worker just found some old code with a "with x, y do" Luckily he refactored that out.
February 4th, 2009 at 8:50 am"Declaring variables anywhere will slow the compiler."
I agree with that, and I like all the variables definitionon the top, organized… not floating on the code..
"interface/implementation"
The interface/implementation is one of the things I like most, I hate to see those Java codes… with this section we can have a clear view of the class structure
"Garbage Colletion"
February 4th, 2009 at 9:09 amI don´t like garbage colletion, for me, this feature is for lazy programmers…
He makes a few good points, and a lot of really bad ones. I have to agree with your response, for the most part.
I would really like seeing the WITH syntax changed to require a dot before it like VB, though. It’ll break some code. So what? It’s *bad* code that’ll get broken, and this will improve it.
Another VB improvement that Delphi could really benefit from: Control Arrays. I loved the logical structuring that it allowed back in VB6, and it was one of the only things I really, *really* missed when I dropped VB for Delphi. (And apparently a lot of people really miss it in Visual Fred.) Any good reason why Delphi couldn’t support VB6-style control arrays?
One last thing, and this one’s always been a bit of a sore spot for me. Objects are basically old-school records with some extra features attached. They’re designed to look a lot like records to the coder. They have properties–a wonderful thing, BTW–to allow transparent data access while preserving encapsulation. When you’re writing code, public properties look like you’re accessing data members directly, when in reality you’re either getting redirected to the internal fields, or running function calls, and it’s all completely transparent. It’s a beautiful system.
And then you try to do something completely ordinary that would work on a real data member, and it all falls apart, and I don’t understand the logic behind it. Give me one good reason why you should ever be unable to pass a read-write property to a var parameter on a function call. If they read and write directly to the same internal field, it’s trivial. If not, have the compiler retrieve the read value, make a stack-allocated copy, pass it, then retrieve the output and send it to the write value. Why has this not been done yet? Why wasn’t it done from the beginning?
As for Andreas Hausladen, I’ve wondered about that one a bit myself, ever since I saw his name listed on that one easter egg…
February 4th, 2009 at 9:10 amCraig Stuntz: It would make a difference because it would mean more things the compiler has to be watching for when parsing the body of a function. Look through the code of a Pascal compiler sometime (RemObjects PascalScript, for example) and look at the way it parses different sections of the file, and you’ll get an example of the complexity involved.
Parsing the function body is basically a big long if..then..else if..else if..else if… chain. Adding an extra rule to the parser, and an extra "else if" block would add extra code to the inner loop that gets executed multiple times for every single line of code. Also, if the extra variables had local scope, (like an index variable for a FOR loop that’s not valid outside the loop,) that’s one more thing the compiler would have to keep track of.
The first rule of optimizing is to optimize your most commonly-executed loops first, because that’s where you see the biggest speed gain. Adding support for inline variable declarations there would not only enable some really ugly and confusing syntax (IMHO), it would add extra logic to one of the innermost loops of the compiler. That could make for a very noticeable speed hit on something like the project I maintain at work, which weighs in at ~3.2M lines of code. It currently builds in about 3 minutes on my workstation and I’d like to keep it that way, TYVM.
February 4th, 2009 at 9:25 am@Craig: From what I understand of compiler theory, the interface section in units, and the fact all the variables are declared at the top of the method is what allows Delphi (and Pascal) to be a single pass compiler. Unfortunately being single pass also makes type inference much more difficult.
Now there are certain things that create sub-blocks of code (another Begin, a Try, etc.) Maybe if we added variable deceleration at the top of those blocks too. That would meet the needs of the "late deceleration for smaller scope" camp, while still keeping the compiler fast. Obviously Barry and team have a much greater understanding of this then I do.
February 4th, 2009 at 9:53 amJ.D. Mullin,
It’s Ctrl-Shift-Up/Down, and it’s utterly essential!
By the way, does anyone else have trouble when they create the implementation for a method, press Ctrl-Shift-C to automagically create the interface, and then swear and curse because it is always created as a private method? Not really Delphi’s fault, but God knows how many times I have hit a compile error because of that.
February 4th, 2009 at 9:56 amMason, I don’t buy that. Variable declarations are dead simple to parse. Your argument is an adding any syntax to the language, at all. It doesn’t seem to be informed by having actually implemented a compiler.
Worse, it’s premature optimization. The first rule of optimization is most certainly *not* to optimize commonly executed loops first; it’s to profile before you begin to optimize, at all.
Jim, you are correct about forward declaration of public methods, but not about variable declarations. As long as the variable is declared before it is used (which no one has proposed changing), you’re not requiring a new pass.
February 4th, 2009 at 10:16 amTo Jim McKeeth who wrote "Speaking of WITH, you know what would be slick is if you could set a compiler warning to deprecate certain language features."
I would take that one step further: Make unit profiles, which cause warnings on certain language features. For instance, if a unit should be cross platform, it should cause warnings on PChar, pointers etc. If a unit should be normal modern code, you would put warnings on "with" etc. If a unit contains legacy code, all old stuff must be enabled.
And if this is done, it would be cool to have a statistics functionality, which shows how many units there are of each kind, how many lines of code they contain etc. This way, you can easily see how many units you need to convert from legacy to non-legacy profiles. Let’s call this "managed source code"
February 4th, 2009 at 10:19 amGraham: I’ve never had a problem with that, because I always do it the other way around: write the interface first, then CTRL-SHIFT-C to have it create the method for me. I actually didn’t know you could do it the way you’re describing.
Craig: More or less. Adding any new *type of* syntax to the language would introduce the same performance hit. We didn’t really see that with Generics because the new syntax class (type declarations in angle brackets) isn’t actually used within function bodies.
Not sure about what impact closures had. I haven’t had any reason to use them yet. But it’s probably excusable because it actually adds functionality to the language. And since most programs are going to use them very infrequently, if at all, you can minimize the impact by putting the check for it at the end of the chain. If you put in inline variable declaration, on the other hand, you’d immediately have a ton of C++ coders cluttering up their code with new variables all over the place. Most of us use Delphi specifically because it works better than C++. We don’t want *that* sort of syntactic diarrhea in our language. It doesn’t add new or improved functionality. There’s nothing you can write with inline variable declaration that you wouldn’t have been able to write just as well without it simply by moving the declaration to the top of the function where it belongs.
And yes, I’ll freely admit that I’ve never written my own Delphi compiler. I have spent a lot of time poking around in PascalScript, and most of was spent it in this inner loop we’re talking about, trying to track down bugs. I know how it works, OK? And I assume Delphi uses a similar loop.
As for your comments regarding optimization, yes, you’re technically correct, in theory. But in actual practice, when you profile, you almost invariably find that most of your time is being spent on three things: big, long methods that only get run once, (initializations, for example,) I/O, and inner loops. Of the three, inner loops tend to be by far the easiest (and most productive) to optimize. At least, that’s consistently been my experience when using profilers. Maybe yours is different?
Besides, I can only assume that the Delphi team has spent a fair amount of time profiling and optimizing the compiler, and this is what they’ve come up with. If they say that adding a whole new syntactical class to parse for would slow the compiler down, they probably know what they’re talking about. And it fits with my experimental knowledge, so I’m willing to accept the claim at face value.
February 4th, 2009 at 11:49 am> I dont’ see the interface/implementation sections going away
Probably all Delphi programmers would feel lost without the interface/implementation section. However the requirement for us to manually update both the interface and implementation sections is ANTIQUATED AND UNPRODICTIVE!
There is NO reason why the IDE cannot automatically keep these in sync for us.
February 4th, 2009 at 12:04 pmEr, obviously I meant unproductive rather than unproDICtive, though it is pretty dicky…
February 4th, 2009 at 12:05 pmKiwi:
CTRL-SHIFT-C is your friend. It’s been mentioned a few times already. Try writing up an interface, then press this magic key combination and see what happens.
February 4th, 2009 at 12:16 pmWITH can be a nigthmare if it’s nested or duplicated name exists but I guess (I don’t know anything about compiler) it can introduce some optimization because a property don’t have to evaluated 10 times when the getter is a complex function:
with AnObject.APropertyFromFunction do begin
for x := 1 to 10 do begin
ASubProperty := x;
end;
end;
… or I’m wrong?
February 4th, 2009 at 12:25 pm@Mason
Yes, i do use Ctrl+C and more frequently the excellent MMX tools. But that doesn’t change the fact that it is pretty ridiculous that we need to manually maintain two instances of the same data (DRY anyone?). Personally I’d like to see the interface section be completely virtual.
Just declare the visibility of each method in the implementation and the IDE would take care of the rest:
procedure TMyClass.ThisIsAPrivateMethod; private;
February 4th, 2009 at 12:48 pm@Graham
You need MMX:
http://www.modelmakertools.com/modelmaker/index.html
February 4th, 2009 at 12:58 pmDaniel: Yeah, you can do that. It does help, especially if you have to go through more than one nested getter to get at the value. But you can achieve the same result with a local variable.
var
optimizer: TMyObject;
x: integer;
begin
optimizer := AnObject.APropertyFromFunction.SubProperty;
for x := 1 to 10 do
begin
optimizer.AProperty := x;
end;
end;
If I understand correctly, that’s basically how WITH works; it just does it behind the scenes.
Nick:
One other thing I’d really like to see is a minor modification to the behavior of the Debug Inspector and Evaluate/Modify window. Let’s say I’m in an event handler whose first parameter is Sender: TObject.
If I click on that and try to inspect/evaluate it, it brings up a TObject (worthless), and I have to manually typecast it. If I’m not aware of the object’s class type, I have to retrieve it from Evaluate with Sender.ClassType, then typecast it. But here’s the thing: that works. The information is already there! So when I try to open any object, I’d be ever-so-happy if it would automatically do that for me, and display the object as its actual type instead of its currently-declared type.
Especially when I’m working with databases and features specific to a certain dataset or field type, having to use multiple nested typecasts can get really old really fast.
Of course, there could be some people who are used to this and prefer it that way. Probably better to make it an option. But i’d definitely like to see the option available.
February 4th, 2009 at 1:05 pmOTOH, I completely agree with Nick about Var’s having their own section. It makes the code much cleaner. Given that you can use the rename refactor (Shift+Ctrl+V) it is not distracting to declare it as you code anyway.
February 4th, 2009 at 1:08 pmMason, if you believe this:
We didn’t really see that with Generics because the new syntax class (type declarations in angle brackets) isn’t actually used within function bodies.
…then you can’t possibly have ever invoked a generic method in Delphi at all.
I really am interested in hearing from Nick why he made this statement. It’s not that I doubt him; I know him, and I don’t think he just made it up. It’s that he didn’t explain why it’s so, and I’d like to hear.
February 4th, 2009 at 1:39 pmCraig:
Hang on. Now that I think about it… yeah. They do show up in the main body, or at least they can. My mistake. I just never do it that way, by personal preference. I prefer to declare generic types with their own names outside function bodies, like:
type
TMyObjectList = TObjectList;
…and then just reference the defined name in the code itself. I find it makes for more readable code to write "myList := TMyObjectList.Create" than "myList := TObjectList.Create", for example. But you’re right about that one. The syntax is available in function bodies.
So maybe that’s not the reason, or at least not the whole reason. Maybe it has something to do with having to do extra work to manage the scope of the new variables or something like that.
February 4th, 2009 at 1:57 pm(Argh! WordPress ate the angle brackets in my example!)
Anyone know how to escape those so it knows you’re talking about literals instead of reading them as bad HTML tags?
February 4th, 2009 at 2:06 pm@Craig You are correct, I realized that after I posted it.
February 4th, 2009 at 2:36 pmI need a variable highlight function of IDE like shift-F8 of SourceInsight. It is useful when reading the code and debuging.
Thanks.
February 4th, 2009 at 3:51 pmHow about fixing the dreaded circular unit reference problem. I have found this to be a real bear when deveoping large and complex frameworks since it makes it hard to partition code into separate units due to dependency issues. The compiler is none too helpul when this problem occurs. I would also like to have a class annotation feature like in Java. I know Delphi.net has a similar feature, why not native code as well.
February 4th, 2009 at 4:34 pmWITH: In Turbo Pascal for DOS, debugging a WITH resolved variable showed an error message ("Cannot display this variable", or words to that effect).
In Delphi, the mouse-over displays ANY variable of the same name it finds in the procedure!
This is very, very misleading. At the very least, the debugger should just display an explanatory error message. This should REALLY be fixed!
Having said that, I would prefer a fixed implementation of WITH; something the compiler can differentiate from the legacy implementation. I have always found WITH very useful, I won’t list the ways here. Sure, you can do silly things. But I’m an adult now. You can let go my hand when I cross the road.
February 4th, 2009 at 6:13 pmXepol Says:
"Case insensitivity forever. The compiler should work more like my mind, not the other way around."
I NOTICE YOU DID NOT WRITE YOUR POST LIKE THIS.
nor did you write post it like this.
So clearly, your mind DOES work case sensitive. And since we are talking about programming languages, I would like to express myself in the same way.
But it would break code and there’s more important things needing attention.
February 4th, 2009 at 6:21 pmMy only use for with is inside little class functions.
class function TAnyForm.Execute: Boolean;
February 4th, 2009 at 6:27 pmbegin
with TAnyForm.Create(nil) do
try
…
Result := ShowModal = mrOk;
finally
Free;
end;
end;
Nice blog Nick,
>>Declaring variables anywhere will slow the compiler.
Don’t allow it for anywhere, if you just add it the for loops, it will be very helpful
February 4th, 2009 at 9:16 pmOne one side:
it’s good to be critical in reviewing past, present and future ObjectPascal and compiler features.
On the other side:
We’d better not kidding ourselves. Any improvement will have its pros and cons.
And it’s gullible to think that "everything" will become perfect (in other case, I would go out of work prety soon
While True do With Embarcadero, Delphi do Begin
.DoImprove(.Compiler);
.VCL := .VCL + AddedValue;
.Version := .Version + 1;
If (.HappinessState(DevelopersArray) in [hs_good, hsGreat])
and Check_ROI(Investments + R_and_D) then
MsgContent := ‘Great job Nick’
else
MsgContent := ‘well … next time should be better’;
ShowMessage( MsgContent );
End;
End;
By the way:
- I do compile from the scratch (Shift+F9) 350 Kilo lines of code using Delphi 7 within 2 to 3.5 seconds. Thank you Andy !
- I sometimes do rely on WITH. Though I don’t think the "." (DotInFront) syntax would improve a lot. Worse, it could be bias prone when combined with the "parenthesis brackets":
With MyObjectList do .Items(..ItemIndex.).Tag := 0;
Ughhh !
February 4th, 2009 at 10:09 pm@Nick:
> I dont’ see the interface/implementation sections going away. This is big advantage in my view, and I know I’m not alone on this. In fact, it is a distinct advantage of Delphi.
Absolutely. This is one of my biggest irritations with C#. I always liked this feature before, but the more C# I have to read, the more I like Delphi.
February 4th, 2009 at 10:14 pm@Mason
> CTRL-SHIFT-C is your friend.
Unless you have read-only properties. Then it is your enemy.
February 4th, 2009 at 10:15 pmQC’ed.
My 2 ct:
>>The compiler will not slow down. I simply will not allow it.
–Everything else would be, well… bad!
>>I dont’ see the interface/implementation sections going away.
–Great! Not only do they help the compiler, they help making (especially) class definitions readable, too.
>>Declaring variables anywhere will slow the compiler. I personally don’t like it (…).
–Me too. Please stay away from it.
>>You can have case insensitivity when you pry it from my cold dead fingers.
Agreed! (The Idea with optional compiler warnings is worth considering, though)
–lol
>>I hate with.
Used with caution "with" can make code more readable. Nested withs are certainly an "no no". You might disallow them.
–I don’t (Maybe I am that stupid programmer mentioned in the article
Go Delphi!
February 4th, 2009 at 10:39 pmMohammed:
February 4th, 2009 at 11:21 pmI second that. More often than not when people talk about declaring variables ‘anywhere’, what they really want is to avoid having to declare ‘for loop’ variables at the top.
It would be great to get a new loop syntax that allowed us to (OPTIONALLY) specify the loop variable as part of the ‘for loop’ syntax. Surely this would put less stress on the compiler than being able to handle arbitrary declarations anywhere in the code
Verity makes one negative point in his/her wish list: Please don’t screw it up, Codegear, and I can sympathize.
However, the whole thing about removing interface/implementation: PLEASE don’t listen to people who don’t understand Pascal/Modula2/Wirthian language thinking. The interface/implementation section replaces the horriffic .C/.H split where API declaration goes in .H and implementation in .C. Keeping those files separate in C/C++ makes the pain double.
I never never never never worry about keeping them in sync. I type in the interface section, I use code completion, I type in my method body. I type the code-completion character and I get (a) the clean separation of interface and implementation, and (b) no extra work.
There is no such thing in C#, and Java. Instead, you COULD semi-automagically extract an Interface Section using a pre-compiler if you needed a declaration list.
I think instead, maybe useful features could be added, instead of random shuffling and imports of stuff that is handy in C# or Java contexts.
I trust CodeGear understands their own baby much more than poor Verity Stob does.
Warren
February 5th, 2009 at 12:27 amI don’t like that Ctrl+Shift+C works only for class methods. It will be nice to do the same thing with local procedures\functions that declared in interface block of program.
February 5th, 2009 at 12:29 amWhat’s with this Stub? I think he’s arguments are rather lame. Someone suggested that the "with" allowed alias like exceptions: "on e: exception". This would be "with m: TfrmMain do". I could go for that. Finally I noticed that he used the try-finally (and that was where I stopped reading; waste of time) wrong. He created a and b inside the try-finally where they should be outside. That’s a beginners error! BTW. He’ll have try pry casesensitivness
February 5th, 2009 at 2:16 amHenrik,
It’s Ms Stob and she has quite a reputation. Apparently you are ignorant of this.
As for try finally she got it right. This is a common trick when you want to avoid having lots of nested try finally blocks. By assigning the refs to nil before the try you know that the subsequent calls free will succeed. Without assigning the refs to nil you get stack junk in your refs.
February 5th, 2009 at 2:30 amMason, you are confusing generic methods and generic types. Generic methods have type parameters distinct from their containing methods. You cannot invoke them without specifying the type parameters, in Delphi 2009, ever. Nor can you alias them. Again, see my CodeRage presentation for examples.
February 5th, 2009 at 3:09 amJust a correction for your blog. You mentioned Ctrl+Up/Down to move between interface and implementation. It is Ctrl+>shift<+Up/Down
February 5th, 2009 at 4:05 am@Brad:
>> CTRL-SHIFT-C is your friend.
>
>Unless you have read-only properties. Then it is your enemy.
>QC’ed.
Really? What’s the problem? I know it screws up write-only properties, but I’ve never seen any problem with Code Completion and read-only properties…
@agorbachenko:
>I don’t like that Ctrl+Shift+C works only for class methods. It will be nice to do the same thing with local procedures\functions that declared in interface block of program.
Agreed! I’d like to see that too.
@Craig:
>Mason, you are confusing generic methods and generic types.
Oh, I see. Yeah, I’ve never had the need to use those yet. I basically just use generics for the collections, since they work so much better than TList.
February 5th, 2009 at 6:02 amMost of times I’m very critic of things I don’t like about Delphi and/or CodeGear. But now I must say. I agree with you 200% (well, not so much, I like "with"
)
Best regards
February 5th, 2009 at 6:06 amOne more thing that must be fixed (in my opinion) is refactoring. Now it is almost unusable. For example:
1. Unit renaming affects only unit name in project file, uses of the unit are not affected.
2. Some refactorings working only for methods of class not for local functions.
3. Change params refactoring is not flexible and sometimes does not work at all.
4. Extract method refactoring is really stupid - unusable.
5. Find unit refactoring (Ctrl+Shift+A) can operate only delphi untis and units that included in project. I’d like to find units from my project search paths. Other find actions should do the same.
And so on, and so on…
You should review refactoring to make it usable (like refactoring provided by IDEA Java IDE)! Please, help developers to save their time!
February 5th, 2009 at 3:14 pmthanx about info about Andreas,
February 5th, 2009 at 5:27 pmit’s good to know that he is not "outsider"
By the way…
The code sample from Ms Stob regarding try..finally blocks is weird !
[Citation]
A := nil; B := nil;
try
A := TSomething.Create;
B := TOther.Create;
// Lines of code using A and B
…
finally
FreeAndNil(A);
// But forgot B - easily done so far away from the Create
end;
[/Citation]
In Fact, the calls to the "create" constructors should take place outside of the try..finally block.
3 major (!) reasons for this:
1) the constructor, if it fails, should raise an exception that isn’t catched by this try…finally block
2) the call(s) to FreeAndNil(…) doesn’t make any sense if the constructor(s) has failed.
3) the try..finally block should make the "business handling" of the created object safe i.e. should ensure that if the "business" part has failed, the existing (!) objects will be freed correctly.
In case the constructor has failed, the surrounding code or the calling method should handle the exception, not the internal try..finally.
Hence the example should read:
A:= TSomething.Create; // try to create "A"
Try
A.DoWhatever; // using "A" is safe here
Finally
FreeAdnNil(A); // freeing "A" is safe too
End;
or to be exhaustive:
A:= TSomething.Create;
try
B:= TAnotherObject.Create;
try
A.DoWhatever;
B.DoMore;
finally
freeandnil(B);
End;
finally
FreeAndNil(A);
End;
it doesn’t look really neat… but it’s safe.
February 5th, 2009 at 9:37 pmFurthermore, it could be improved with extra exception handling using "raise" and similar…
"David Taylor Says:
How about fixing the dreaded circular unit reference problem."
If we’re talking about the same problem, I solve it with separate USES clauses in Interface and Implementation.
February 6th, 2009 at 2:32 amIn my opinion circular unit reference problem is developer’s mistake. This is the signal that your code logic is build incorrectly. When two units have circular references this mean that you have piece of your code that can be extracted to separate unit.
February 6th, 2009 at 3:15 amLoïs - it’s not weird at all. Setting the variables to nil before the ‘try’ allows using only one try/finally pair. Think about it - if an exception is raised in a constructor, the variable in the calling code isn’t assigned anything, staying nil as a result - and it is safe to call Free on a nil’ed var (indeed, that’s the whole point of Free). The only issue is if the first destructor in the ‘finally’ block raises an exception, but then destructors shouldn’t do such a thing.
February 6th, 2009 at 4:05 amLoïs Bégué:
The assign nil, try, create, finally, free pattern is common when you have multiple objects that live and dies inside a single method. Ms Stob does know what she is talking about and she has got it right. I think you need to study it a bit more.
Actually the problem with forgetting to destroy and object and leaking memory is easily handled by using a memory manager that counts blocks out and then back in again. FastMM does this, but it’s easy to write your own simple counter. Actually FastMM does a great job of this because it tells you what class of object you forgot to free.
February 6th, 2009 at 4:44 am@Ken, agorbachenko:
I agree. The "dreaded circular unit reference problem" is that way by design. It lies at the heart of what Jim McKeeth jokingly calls "the worst thing about Delphi", its insanely fast compile time. It’s only possible for me to run a full build of about 3.2 million lines of code in under 3 minutes at work because Delphi strictly enforces a single-pass compilation system.
Occasionally that causes some inconveniences, especially if you’re used to C++ or Java. But allowing you to put USES in the Implementation section clears up most of the problems, since the single-pass rule only forbids circular Interface references. You just have to think about your object tree structure a bit and maybe move some classes around, and you usually end up with cleaner code.
February 6th, 2009 at 8:11 am@CR @David
I meant "weird", not "wrong"
I got Ms Stob point, though I prefer to handle exceptions due to failed object creation from outside.
e.g.
procedure TestFramework;
Begin
A:= TSpecialObject.create; // "object creation" risk
Try
A.BusinessWork; // "business" risk
finally
FreeAndNil(A); // avoid memory leaks anyway
End;
End;
This allow me to better differentiate "object creation" exceptions from "business" exceptions.
Hence, it’s not the role (if you prefer: I don’t want it) of the "TestFramework" procedure to handle "object creation" exceptions. It’s the role of the method which has called this procedure.
In fact, "object creation" should mostly not raise exceptions
February 8th, 2009 at 10:10 pmThat’s also the reason why I prefer to take the constructor out-of-scope of the try…finally:
- The constructor should not fail.
- if it has failed, then the application have a big (memory management) problem, not issued (but revealed) by the "TestFramework" procedure itself…
Loïs:
We all agree that the way you describe is the norm. That’s what I do 99% of the time. However, the point is that if you have a lot of objects to create in one routine you get a large nesting of Try Finally blocks that can obscure the true intent of the code. That’s when you might use the pattern Ms. Stob described. You probably wouldn’t bother for just two objects but three might be enough to tip you over to the other way. That’s personal choice.
The code Ms Stob showed is perfectly normal, that’s my point. I don’t think the point she was making was terribly well illustrated by this though. I personally think object lifetime management is just hard. Try Finally works well almost all the time. GC has its own set of problems, but again works well for a majority of use cases. Automatic reference counted management like Ms Stob suggests agains works well a lot of the time but has some of the same issues as GC (it can get hard to know when your objects die and sometimes that matters).
February 8th, 2009 at 11:23 pmGreat to hear these debates going on. Since I stopped working for the man and started working for myself Delphi has been a godsend, Delphi 2007 still compiles all my legacy code and long may future versions continue to do so.
All my (minor) gripes are really with the IDE: the look and feel from D7 looked more professional but lost a bit of the usefulness of the separate form and code windows (which with multiple 22" monitors would be great now!) the help has also gone downhill and takes 20 or 30 secs from pressing F1 first time in the IDE to displaying anything, it has also crashed a few times while debugging dodgy code something D7 never did on me. I also remember *vaguely* once that when I pressed pause it used to stop on the line of code executing these days it always pauses inside a win32 dll. Like I say minor and certainly not worth moving for.
Not sure I really needed to upgrade to D2007, D7 did and probably still would do everything I wanted but I’ve sorta getting used to it now. I will probably skip 2009 but if improvements and quality get back on track, which they certainly look like they are being done I will certainly be buying again.
For me also its the first I’ve heard about CTRL-SHIFT-UP/DN, what a time-saver! any other gems like that out there ?
May 10th, 2009 at 5:30 pmctrl-Shift-i = block indent
ctrl-shift-u = block unindent
shift-alt with cursor keys let you do a kind of ‘free-form selection’
June 7th, 2010 at 4:58 am