Jump to content
jesu

function: how to return nil

Recommended Posts

Hello. I'm trying to create a function that can return a float or nil:

 

function myfunc(const pa_value:string): double;
begin
  if pa_value = whatever then result := -1
  else result := nil;
end;

to replace many lines like this (simplified code):

if check then
parambyname('myparam').asfloat := -1
else
parambyname('myparam').clear;

What is the right way to do that?

Thanks.

Share this post


Link to post

Using variant?

function myfunc(const pa_value:string): variant;
begin
  if pa_value = whatever then result := -1
  else result := null;
end;

and then

parambyname('myparam').AsVariant := myfunc();

 

Share this post


Link to post

It depends on what your function does, but just like a function returning an integer returns a special value (often -1 or 0) in case of error, your function could return a special value as a double number.

Carefully select the value so that it doesn't collide with expected values in your application. I'm think about a value like 2.12345e-308. Just define a constant to give that value a nice name easy to use and remember.

 

 

 

Share this post


Link to post
30 minutes ago, FPiette said:

Carefully select the value so that it doesn't collide with expected values in your application. I'm think about a value like 2.12345e-308. Just define a constant to give that value a nice name easy to use and remember.

NaN

 

 

  • Like 1

Share this post


Link to post
1 hour ago, Anders Melander said:
2 hours ago, FPiette said:

Carefully select the value so that it doesn't collide with expected values in your application. I'm think about a value like 2.12345e-308. Just define a constant to give that value a nice name easy to use and remember.

NaN

Anders, I'm not sure that NaN is a good choice because this special value is generated in other cases. Something like 2.12345e-308is a better choice and collision risk is much lower.

Share this post


Link to post

I'm pretty sure that anyone asking for help with something as basic as this isn't handling, or even aware of, NaN.

 

Regardless, I personally wouldn't solve the problem with a magic value.

  • Like 1

Share this post


Link to post

It's impossible to suggest a good solution without knowing more about the context. If it's just a case of either setting or clearing a TParam value then just pass the TParam to the function and let the function operate directly on that.

Share this post


Link to post

You're better off doing something like

 

function myfunc(const InVal: String; out OutVal: Double): Boolean;

 

Return True if the value was set and false if not.

 

There are other ways to handle this but the worst way is a magic value in Double. There's no reason to do that when it is so easy to indicate explicitly whether or not the value is valid.

  • Like 7

Share this post


Link to post

I usually prefix functions like this with "Try", so instead of

function GetTheValue(parameters ...): SomeType;

it's

function TryGetTheValue(parameters ...; out Value: SomeType): Boolean;

  • Like 4

Share this post


Link to post
2 hours ago, dummzeuch said:

TryGetTheValue(

Very good suggestion by @dummzeuch Very few people give enough thought to naming of functions and variables. Developing a consistent approach can significantly help with long term program readability / long term support for large projects. Anything that hints at doing that gets my full support !

https://cigolblog.wordpress.com/2023/01/

https://cigolblog.wordpress.com/2019/10/

https://cigolblog.wordpress.com/2017/06/

  • Like 2

Share this post


Link to post

Thanks for the suggestions.

Returning a dummy value would mean rewriting the stored procedures so that's a no-no.

I'm trying to avoid Variants because in the past I've had problems with variants <-> float. I've tried this instead


 

procedure ProcValorDeCombo(var pn_param: TFDParam; pt_Combo: TComboBox);
var
 vn_marca: double;
begin
  case pt_Combo.ItemIndex of
   -1: begin 
         // code ...
         pn_param.AsFloat := vn_marca;
       end;

   // code...
   else pn_param.Clear;    
  end;
end;

but when I try to call it

ProcValorDeCombo(ParamByName('myparam'), mycombo);

I get E2197 Constant object cannot be passed as var parameter

 

Edited by jesu

Share this post


Link to post

Because I'm modifying it inside the procedure

 

Share this post


Link to post

ok, I'm not modifying tparam, but tparam.value

Share this post


Link to post
26 minutes ago, jesu said:

Because I'm modifying it inside the procedure

...

ok, I'm not modifying tparam, but tparam.value

The TFDParam parameter is an object reference (i.e. a pointer). You are not modifying the object reference; You are modifying a property on the object. So drop the var.

 

I would also change the TComboBox parameter to an integer and pass TComboBox.ItemIndex instead; There's no reason to create a dependency on TCombobox when all you need is the ItemIndex.

Share this post


Link to post

Right, I've removed var and it works, but the combobox style is csDropDown so I check both itemindex and text. And there are lots of them, so if I have a problem I use the name to find which one is wrong.

Share this post


Link to post

@jesu I see this was a while back, but I'd encourage you to look up the idea of an Optional type.

 

(One of) the points of an optional type, is that it doesn't reduce the value space of your inhabited type.

Using 'NaN' or '-1' or whatever as a magic value might interfere with the validity of your use of the Double value.

With an optional type , the inhabited type is uncontaminated by any other interpretation or usage.

 

You can look at what it looks like in eg Spring4D, but here's the basic idea :

type
  Maybe_Double = record   // or Optional_Double
      ok : boolean;
      d  : double;
  end;

function Safe_Sqrt( i:integer ) : Maybe_Double;
begin
   if i<0 then 
      result.ok := false
   else begin
      result.ok := true;
      result.d  := sqrt(i);    
   end;       
end;
          
begin
   for var i:= -2 to 2 do begin       
       var ss := Safe_Sqrt(i);    
       if ss.ok then writeln(i, '   ', d)
                else writeln(i, '   ','Undefined');
   end;       
end.          

PS Another feature of an optional type is that all uses of the inhabited type transfer into the "optional world".

// If you have a function 
F( d:double ) : string,

// then you can automatically have an 'identical' function 
OptF( od:maybe_double ) : maybe_string;
begin
  if od.ok then exit( F(od) ) 
           else exit( <the nil case> )
end;           

 

Edited by pmcgee

Share this post


Link to post

Or just "setter" method with original code.

Something like...
 

function SetFloatParameter(const ADataSet: TDataSet; const AParamName: string; const AValueOK: Boolean; const AFloatValue: Double);
begin
  Result := False;

  if AValueOK then
  begin
    ADataSet.ParamByName(AParamName).AsFloat := AFloatValue;
    Result := True;
  end
  else
    ADataSet.ParamByName(AParamName).Clear;
end;

 

Edited by Tommi Prami

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×