I often use code along the lines of:
function GetNumber(Handle : THandle) : Integer;
begin
FLock.BeginRead;
try
if FMap.TryGetValue(Handle, Object) then
raise EArgumentException.Create('Invalid handle');
Result := Object.Number;
finally
FLock.EndRead;
end;
end;
Unfortunately the compiler gives me a warning for all these methods:
[DCC Warning] Unit.pas(1012): W1035 Return value of function 'GetNumber' might be undefined
I know this warning, but in this case I can't see any reason for it at all. Or is there a scenario that I am missing that would result in an undefined result value? I understand the warning in the case of try..except but for try..finally it does not make sense to me.
Questions:
Is there any reason for the warning?
How can I get rid of it (moving the Result := Object.Number line out of the lock is not an option, and I want to avoid writing an completely unnecessary Result := 0 line at the top of each function)
Thanks!
Is there any reason for the warning?
I can't see one but it is there because of the raise
How can I get rid of it (moving the Result := Object.Name line out of
the lock is not an option, and I want
to avoid writing an completely
unncessary Result := 0 line at the top
of each function)
Move the raise statement to a procedure of it's own.
function GetNumber(Handle : THandle) : Integer;
procedure InvHandle;
begin
raise EArgumentException.Create('Invalid handle');
end;
begin
FLock.BeginRead;
try
if FMap.TryGetValue(Handle, Object) then
InvHandle;
Result := Object.Number;
finally
FLock.EndRead;
end;
end;
That is a compiler bug. If the try/finally is removed then the warning is not emitted. The compiler has long been able to recognise that a raise absolves the coder of the obligation to assign the return value. For whatever reason the try/finally appears to confuse its analysis.
On second thoughts perhaps it's not a compiler bug. What if the code in the finally block stopped the exception propagating? Clearly there's not an except handler, but I rather suspect that one of the exception support routines in the System or SysUtils unit may be able to stop the exception progressing further.
An assignment
Result := ...;
in the finally block is missing.
Related
I have a little problem with compilation under Delphi:
function T_QS2ProcessMailbox.PutRec<T>(const aID: T_Barcode; var aRec: T;const aTxt: String): Boolean;
var
FA: T_FahrauftragRec absolute aRec;
LP: T_LagerpackungRec absolute aRec;
begin
init_Rec;
Rec.ID := aID;
Rec.EventTime := Now;
Rec.Text := aTxt;
if TypeInfo(T_LagerpackungRec) = TypeInfo(T) then
begin
Rec.RecType := C_QS_TYPE_TLAGERPACKUNGREC;
Rec.FA := FA;
end
else
if Typeinfo(T) = Typeinfo(T_LagerpackungRec) then
begin
Rec.RecType := C_QS_TYPE_TFAHRAUFTRAGREC;
Rec.LP := LP;
end
else
Rec.RecType := C_QS_TYPE_TEXT;
Send_TraceMsg(ClassName + '.PutRec Type=' + IntToStr(Rec.RecType));
Result := PutRec(Rec);
end;
It compiles fine without errors, messages, or hints. But it is compiled without if statements. You can look at it in the picture - this code without compilations marker
I do not understand why.
Can somebody explain to me what I am doing incorrectly?
Those if statements can be resolved at compile time, so only ever 1 of them will be actually compiled for any given value of T. (In other word, the compiled code will never execute any if for this function).
I can imagine 2 reasons for seeing only 1 compilation marker. Either your application will only ever use 1 of the if statements, or the IDE will map the compilation marker of all the if statements to the same line (I find this last one unlikely, but I've seen stranger things in the IDE).
Another possibility is that your 2nd if should read
if Typeinfo(T) = Typeinfo(T_FahrauftragRec) then
instead of
if Typeinfo(T) = Typeinfo(T_LagerpackungRec) then
Typeinfo() is a compiler intrinsic function in XE7 and later, and thus is available for the compiler to evaluate at compile-time 1. And since the type of T is a Generic that is also known to the compiler, the compiler can directly evaluate your ifs and any blocks that evaluate to False and would never be executed at runtime are simply omitted from the final executable altogether. That is why you don't see any debugger points on them.
1: but only in the specific case where TypeInfo(T) is used in an if TypeInfo(T) = TypeInfo(X) statement inside a Generic method. Other uses of TypeInfo() are not similarly inlined at compile-time.
This is normal behavior, and is what you WANT to have happened, as it produces slimmer and more efficient runtime code.
When your other code calls PutRec<T_FahrauftragRec>(...) then T will be T_FahrauftragRec and thus TypeInfo(T_LagerpackungRec) = TypeInfo(T_FahrauftragRec) will evaluate as False.
Likewise, when calling PutRec<T_LagerpackungRec>(...) then T will be T_LagerpackungRec and thus TypeInfo(T_FahrauftragRec) = TypeInfo(T_LagerpackungRec) will evaluate as False.
And so on for any other type you pass to T.
Also, you have a bug in your code. Your second if statement:
if Typeinfo(T) = Typeinfo(T_LagerpackungRec) then
Should be this instead:
if Typeinfo(T) = Typeinfo(T_FahrauftragRec) then
In addition to this question I have made some tests and researches on the docwiki. My conclusion is that this kind of code should work without memory leaks:
function testResultObject: TClassA;
begin
Result := TClassA.Create;
Result.DoSomething;
end;
And then somewhere I can call the above code in this manner:
var k: TClassA;
begin
k := testResultObject;
try
//code code code
finally
k.Free;
end;
end;
As Remy suggested in the answer it's better to avoid this way of doing things and instead use something like testResultObject(x: TClassA): boolean. In this case the return true/false can tell me if everything went fine and I am passing an object already created.
Look at this code:
function testResultObject: TClassA;
begin
Result := TClassA.Create;
try
Result.DoSomething;
except
Result.Free;
end;
end;
The problem with the first version above of the function is that DoSomething could raise an exception and if so I'll leak memory. Can the second implementation with try-except be a solution? For sure later I'll have to check if the result is assigned or nil.
I agree that (as already said above) the testResultObject(x: TClassA): boolean would be better. I was just wondering if the return-a-class function way could be fixed as I've written.
Your code has serious problems. In case of an error, it swallows the exception, and returns an invalid object reference.
This is easy to fix. The canonical way is as follows:
function testResultObject: TClassA;
begin
Result := TClassA.Create;
try
Result.DoSomething;
except
Result.Free;
raise;
end;
end;
Either the function succeeds and returns a new object. Or it fails, cleans up after itself, and raises an exception.
In other words, this function looks and behaves just like a constructor. You consume it in the same way:
obj := testResultObject;
try
// do things with obj
finally
obj.Free;
end;
Your second approach works, but has 2 serious problems.
By swallowing all exceptions, (as J pointed out) you'll hide the fact that something went wrong.
There's no indication to the caller that you've created an object that the caller is responsible for destroying. This makes using the function more error prone; and easier to cause memory leaks.
I would recommend the following improvement on your second approach:
{Name has a clue that caller should take ownership of a new object returned}
function CreateObjectA: TClassA;
begin
{Once object is successfully created, internal resource protection is required:
- if no error, it is callers responsibility to destroy the returned object
- if error, caller must assume creation *failed* so must destroy object here
Also, by assigning Result of successful Create before *try*:
The object (reference) is returned
**if-and-only-if**
This function returns 'normally' (i.e. no exception state)}
Result := TClassA.Create;
try
Result.DoSomething; {that could fail}
except
{Cleanup only if something goes wrong:
caller should not be responsible for errors *within* this method}
Result.Free;
{Re-raise the exception to notify caller:
exception state means caller does not "receive" Result...
code jumps to next finally or except block}
raise;
end;
end;
The most important benefit of the above create function is that: as far as any caller/client code is concerned, it behaves exactly like a normal TObject.Create.
And so the correct usage pattern is exactly the same.
Note that I'm not keen on J's FreeAndNil suggestion because if calling code doesn't check if the result was assigned: it is likely to AV. And code that does check the result correctly will be a little messy:
var k: TClassA;
begin
k := testResultObject; {assuming nil result on failed create, next/similar is *required*}
if Assigned(k) then {Note how this differs from normal try finally pattern}
try
//code using k
finally
k.Free;
end;
end;
NB: It's important to note that you cannot ever have your caller simply ignore memory management; which brings me to the next section.
All the above aside, there is much less chance of making careless mistakes if your testResultObject takes an input object that you require the caller to create and manage its lifetime as needed. I'm not sure why you're resisting that approach so much? You cannot get simpler than the following without resorting to a different memory model.
var k: TClassA;
begin
k := TClassA.Create;
try
testResultObject(k); {Where this is simply implemented as k.DoSomething;}
//more code using k
finally
k.Free;
end;
end;
The only problem with this :
function testResultObject: TClassA;
begin
Result := TClassA.Create;
try
Result.DoSomething;
except
Result.Free;
end;
end;
Is that you have no way of knowing whether the function was successful. Freeing an object does not alter the reference; the variable will still point to the (now) invalid memory location where the object used to exist. You must explicitly set the reference to nil if you want the consumer to be able to test if the reference is valid. If you want to use this pattern (having the consumer test for nil) then you would need to do :
try
Result.DoSomething;
except
FreeAndNil(Result);
end;
This way the caller can test the result for nil (using Assigned or otherwise) as you intended. This still isn't a very clean approach, however, since you're still swallowing exceptions. Another solution might be to simply introduce a new constructor or alter the existing one. For example
TFoo = class
public
constructor Create(ADoSomething : boolean = false);
procedure DoSomething;
end;
constructor TClassA.Create(ADoSomething: Boolean = False);
begin
inherited Create;
if ADoSomething then DoSomething;
end;
procedure TClassA.DoSomething;
begin
//
end;
This way you can get rid of all of the exception handling and just call this as :
function testResultObject: TClassA;
begin
Result := TClassA.Create(true);
end;
Since you've now pushed the DoSomething execution into the constructor any exceptions will naturally automatically call the destructor and your memory management problems go away. The other answers also have good solutions.
I have encountered an access violation error [Access violation at address 5005F6E in module 'rtl170'. Read of address 0000000A] using the IInterfaceList.
My code looks something like this:
if not Assigned(FoRoutingCodes) then Exit;
for i := 0 to FoRoutingCodes.nCount - 1 do
begin
...
end;
the declaration of FoRoutingCodes is:
FoRoutingCodes : IRoutingCodeList;
and the definition of IRoutingCodeList
IRoutingCodeList = interface
function GetCount: Integer;
...
property nCount: Integer read GetCount;
end;
the implementation of nCount property is:
function TRoutingCodeList.GetCount: Integer;
begin
Result := 0;
if Assigned(FoItems) then
Result := FoItems.Count; // here i get the access violation error
end;
where TRoutingCodeList is the implementation of IRoutingCodeList and FoItems is declared as:
FoItems: IInterfaceList;
I have fixed this using
FoItems: TList<IRoutingCode>;
instead of
FoItems: IInterfaceList;
I am new to delphi, can anyone help me understand what was wrong with previous approach.
I don't know if this is relevant because there are many other changes is our product, but this issue appeared only after we moved from Delphi XE to Delphi XE3.
Thanks in advance.
Update: in response to Uwe Raabe
I have changed FoItems initialisation from
constructor TRoutingCodeList.Create;
begin
FoItems := TInterfaceList.Create;
end;
to
constructor TRoutingCodeList.Create;
begin
FoItems := TList<IRoutingCode>.Create;
end;
Considering only the code you posted so far, I can´t see the problem. However, since you know the exact location of the error, my advice is to set a breakpoint at that line in code and evaluate (CTRL+F7) the member FoItems to see what is it.
Usually, when I suspect I have a bad object reference, I evalute it´s ClassName (in your case, FoItems.ClassName). If the reference is invalid, this method usually raises an exception of returns a strange name.
Also, set a breakpoint in the constructor in order to be sure that FoItems is indeed being instantiated.
I Hope this helps!
Suppose I have the following code:
function DoSomething:Boolean;
var obj : TMyObject;
i : Integer;
begin
Result := False; //We haven't executed GetValue() correctly yet
obj := TMyObject.Create();
try
//perform some code that may produce an exception
i := obj.GetValue();
//Set the return to True as we executed GetValue() successfully
Result := True;
finally
//do some cleanup
obj.Free;
end;
end;
The Delphi compiler is complaining that the value assigned to Result is never used in the first line.
I'm probably missing something obvious, but i don't see why the compiler would optimize this out (if optimization's are on).
I have always been taught to explicitly set my variables so as no confusion what their values are. On top of that, should the GetValue() function generate an exception, the Result := True; line will never execute. So we are at the mercy of whatever Delphi initialized the variable to be.
So is this safe/acceptable code? Should i simply remove the first line of the method, which would make it a lot harder to read? Failing that I would have to turn the specific compiler warning off, but I'm reluctant to do that as this warning message can give useful information.
The compiler is correct. The assignment of False to Result is futile and the only value your function can return is True.
The two possible execution paths are:
The function does not raise an exception and returns True.
The function does raise an exception, and therefore does not return a result value at all.
The solution is simple, remove the line of code that sets Result to False. At which point it becomes completely clear that the return value serves no purpose and you can simply turn the function into a procedure.
Your function has only two outcomes. Either it returns True or it will raise an exception so you could turn it into a procedure instead to make the warning go away.
If you want the result of the function to be False when GetValue() raises an exception you have to capture that exception in DoSomething and set the return value to False. In that case you should start your function initializing the return value to True.
Something like this:
function DoSomething:Boolean;
var
obj : TMyObject;
i: Integer;
begin
Result := True;
obj := TMyObject.Create();
try
try
i := obj.GetValue();
except
Result := False;
end;
finally
obj.Free;
end;
end;
I have put together this code for viewing Routing Table in My delphi App.
PMIB_IPFORWARDROW = ^TMibIpForwardRow;
TMibIpForwardRow=packed record
dwForwardDest:DWORD;
dwForwardMask:DWORD;
dwForwardPolicy:DWORD;
dwForwardNextHop:DWORD;
dwForwardIfIndex:DWORD;
dwForwardType:DWORD;
dwForwardProto:DWORD;
dwForwardAge:DWORD;
dwForwardNextHopAS:DWORD;
dwForwardMetric1:DWORD;
dwForwardMetric2:DWORD;
dwForwardMetric3:DWORD;
dwForwardMetric4:DWORD;
dwForwardMetric5:DWORD;
end;
PMIB_IPFORWARDTABLE=^TMibIpForwardTable ;
TMibIpForwardTable=packed record
dwNumEntries:DWORD;
table:array [0..ANY_SIZE-1] of TMibIpForwardRow;
end;
The Declaration of the Function is :
getIpForwardTable: function (pIpForwardTable:PMIB_IPFORWARDTABLE; pdwSize:PULONG; bOrder:BOOL):DWORD; stdcall;
And the Function for Implementation is:
procedure GetRouteTable;
var
Error:DWORD;
pRouteTable:PMIB_IPFORWARDTABLE ;
dwSize:ULONG;
//dwSize:ULONG;
TableSize:Integer;
begin
try
dwSize:=0;
GetMem(pRouteTable,SizeOf(TMibIpForwardTable));
Error:=GetIpForwardTable(pRouteTable,#dwSize,FALSE);// Error at this line
if Error=NO_ERROR then
begin
ShowMessage(IntToStr(Error));
end
else
ShowMessage(IntToStr(Error));
except
on E:Exception do
ShowMessage(E.ClassName+':'+E.Message);
end;
end;
I am getting Error code=122. Please help in correcting this Error.
Thanks in Advance
edit
I have tried this combo too but it didnt work
Error:=GetIpForwardTable(nil,#dwSize,FALSE);
if Error=ERROR_INSUFFICIENT_BUFFER then
ShowMessage(IntToStr(Error))
else
begin
GetMem(pRouteTable,dwSize);
Error:=getIpForwardTable(pRouteTable,#dwSize,False);
if Error=NO_ERROR then
begin
ShowMessage(IntToStr(Error));
end
else
ShowMessage(IntToStr(Error));
Error code 122 is ERROR_INSUFFICIENT_BUFFER. The documentation for GetIpForwardTable states that this error code is returned when the following occurs.
The buffer pointed to by the pIpForwardTable parameter is not large enough. The required size is returned in the DWORD variable pointed to by the pdwSize parameter.
The solution is to read the value of dwSize that is returned, and call again with a buffer of that size.
The other problem with your code is that you do not pass in a valid buffer. Notice that the variable pRouteTable is never assigned to. In your edit, you do call GetMem to allocate it, but in the wrong place. You need something like this:
Error := GetIpForwardTable(nil, #dwSize, False);
if Error<>ERROR_INSUFFICIENT_BUFFER then
RaiseLastOSError(Error);
GetMem(pRouteTable,dwSize);
Error := GetIpForwardTable(pRouteTable, #dwSize, False);
if Error<>ERROR_SUCCESS then
RaiseLastOSError(Error);