After I decided to study computer science, but before I actually started classes, I was scared. I was scared computer science (programming mostly) would be way too hard for me.
Part of that scare came from my total lack of knowledge on the subject. All I knew about EXE files was what I learned opening those in a hex editor. I started thinking programming was about writing “stuff” in hexadecimal, and if aligned properly inside a file, magic happened! Thankfully, it is not the case. If only I had known what a compiler was back then…
Compiler are powerful tools that allows to transform instructions in plain text into machine code. The use of compilers has many advantages. They can hide the details of the hardware. A new CPU comes around with new, better performing instructions for a specific task? As soon as an update to the compiler is available you can simply update the compiler and rebuild your project without altering a single line of code and you can take advantage of the new instructions. There’s more than 1 sequence of instructions that can do what you ask on the hardware level? Well, the compiler knows which one (or should know) performs it best.
But compilers can create quite a few “problems”. One of the problems that arise from compiler technologies is that pretty often, programmers are not 100% aware of what the machine does “behind the scene” given some lines of codes. The Delphi language is especially rich in “implicit” stuff going around. Notably, string/array management.
When I started to work with the Exception class, one of the thing I wondered was, what was the point of its constructor CreateFmt. I was asking myself “What’s the difference between those 2 lines:”
raise Exception.Create(Format(SSomeConstant, [1])); raise Exception.CreateFmt(SSomeConstant, [1]);
It took me many years before I stumbled upon information that allowed me to extrapolate the reason for the existence of CreateFmt. Granted, the difference between the 2 isn’t meaningful for most(99%) intent and purpose.
The reason why the 2 exists is partly because of the compiler doing a lot more than we know about. Lets take an example
procedure CheckValue(AValue : Integer); begin if AValue > 10 then raise Exception.create(Format(SValueTooHigh, [AValue])); end;
what you are really doing is
procedure CheckValue(AValue : Integer); var ImplicitStringVariable : string; begin ImplicitStringVariable := ''; try if AValue > 10 then begin ImplicitStringVariable := Format(SValueTooHigh, [AValue]) raise Exception.create(ImplicitStringVariable); end; finally DecRefCount(ImplicitStringVariable); end; end;
while using Exception.CreateFmt really do only
procedure CheckValue(AValue : Integer); begin if AValue > 10 then raise Exception.createFmt(SValueTooHigh, [AValue]); end;
Of the few tests I’ve made, Using Exception.CreateFmt instead of Exception.Create(format) made the function about 8 times faster when no exceptions where raised. In situation where performance matters, it’s quite a difference. (ok, in situation where performance matters, exceptions wouldn’t be used 😉 )
Moral of the story, the higher level the language, the more things happening implicitly in the background. And those things doesn’t always make sense. This video express it better than I could ever do.