ucDefineFunction

 

See Also: ucDefine, ucDefineOperator, ucArg, ucArgCount, ucArgHandle, ucReturn

 

Defines a function.

 

ucDefineFunction(Definition [, FunctionAddress [, tHandle]])

 

There are three types of function definitions:  1) Self-contained definitions, 2) Native callback definitions, and 3) Non-native callback definitions.  They are referred to in the next section, but explained in more detail further down.  First, here is a breakdown of what you can pass to ucDefineFunction:

 

Parameters

Definition

Required.  This string argument should consist of a function name, followed by parenthesis, which optionally enclose one or more arguments, followed optionally by a return type.  If no return type is specified, then the default type (Double or Extended, depending on the compiler) is implied.  If this is a self-contained definition, then you continue with an equal sign, followed by an expression that represents the body of code that is executed when the function is invoked.  If this were a callback function, then instead of an equal sign followed by an expression, you would provide a function address in the FunctionAddress argument. 

 

Argument parts

            An argument has the following syntax and parts:

                  [ByVal | ByRef | ByHandle | ByExpr] argname [As argtype] [= default] [[,] ...]

 

      ByRef

Optional.  For use only with non-native callbacks.  Indicates that the argument is passed by reference, and the value can be modified by your callback routine.

      ByVal

            Optional.  For use only with non-native callbacks.  Indicates that the argument is passed by value.     

         ByExpr

Optional.  For use only with callbacks (either native or non-native).  Indicates that the value received by the callback is a handle to the expression that is being passed.  This allows an expression to be passed without first being evaluated.  Your callback has the option of either not evaluating the expression at all, or evaluating it one or more times.  For a native routine, the argument's expression handle is retrieved with ucArgHandle.  For a non-native routine, the callback argument must be set up as a 32-bit integer (such as Long or DWORD), which is passed by value, regardless of the actual type of the argument.  This 32-bit handle is to be used with ucEvaluate() or ucEvaluateStr().  See Example B4.

ByHandle

Optional.  For use only with callbacks (either native or non-native).  Indicates that the value being passed is the handle of the argument.  Given the handle of an argument, your callback can retrieve its properties.  For instance, if the argument is a variable, the callback can use the handle to determine the variable's data type, name, address in memory, etc.  For a native routine, an argument handle is retrieved with ucArgHandle.  For a non-native routine, the callback argument must be set up as a 32-bit integer (such as Long or DWORD), which is passed by value, regardless of the actual type of the argument.  See Example B5.

            argname

                  Required.  This is the name of your argument.

            argtype

Optional.  This can be any of the data types supported by uCalc, including: Single, Double, Long, String, WideString, LPCSTR, etc.  If no type is specified, then the default type is used (Extended or Double depending on the compiler, or something else if you have changed the default).

            default

Optional.  Unlike some languages, the word "Optional" is not needed.  An argument is considered optional by virtue of having an equal sign followed by a default value.  Also unlike some languages, the default does not have to be a constant; it can be an expression as well.  See Example A1.

            ...

Optional.  For use only with native callbacks.  Three consecutive dots (...) indicate that the function takes an indefinite number of arguments from that point on.  This is valid only with, or as the last argument.  Your callback should use ucArgCount to retrieve the number of arguments that were passed.  See Example B2.

 

FunctionAddress

Required for callbacks (native or non-native).  Omit FunctionAddress, or set it to 0 for self-contained definitions.  If you are defining a callback function, then this argument represents the address of the function in your source code (or a DLL) that you want to associate with your uCalc definition.  If your function is named MyFunction, then you would pass CodePtr(MyFunction) in PowerBASIC, AddressOf MyFunction in VB Classic, @MyFunction in Delphi, and MyFunction by itself in C++.  VB.NET and C# handle this quite differently, using delegates.  See Example B1.

 

tHandle

Optional.  You can supply the handle of a given thread so that the function definition is effective only in that particular thread.  See Thread Handling.

 

 

Self-contained definitions

 

In a self-contained function definition, the function name and its argument(s), as well as the body of the definition (the code depicting the operations to be performed by the function) are all located within the Definition argument of ucDefineFunction.  This allows the end-users of your application to define functions.

 

Self-contained definitions can be recursive.

 

Self-contained definitions can be bootstrapped.  This means that you can add to the definition of an already defined function (even if the previous definition was not self-contained).  This is done by simply using the same function in the body of the definition, as though it were another function.  A bootstrapped definition is distinguished from a recursive one by prepending "bootstrap ~~" to the definition.

 

The body of the definition may contain multiple statements/expressions, which are separated by a semicolon.  The last one should be an expression that evaluates to a value that matches the return data type of the function.  The value of this last expression is the one that is returned.

 

 

Example A1:  Miscellaneous self-contained definitions

 

This example demonstrates various self-contained definitions.  It features various concepts including overloading (which means several definitions may share the same function name), as demonstrated with the function named Twice().  It also shows an optional argument in the function named g(), recursion in Factorial(), and specified data types.  Where a data type is not specified for the argument or return type, Double or Extended is implied depending on the compiler.

 

Visual Basic

ucDefineFunction "f(x) = x^2"

ucDefineFunction "g(x, y=5) = x + y"

ucDefineFunction "Twice(value) = value + value"

ucDefineFunction "Twice(value As String) As String = value + value"

ucDefineFunction "ToLong(value As Long) = value"

ucDefineFunction "ToByte(value) As Byte = value"

ucDefineFunction "Factorial(x) = iif(x>1, x*Factorial(x-1), 1)"

 

Print ucEvalStr("f(5)")            ' returns 25

Print ucEvalStr("g(10)")           ' returns 15

Print ucEvalStr("g(10, 20)")       ' returns 30

Print ucEvalStr("Twice(35)")       ' returns 70

Print ucEvalStr("Twice('Bye ')")   ' returns Bye Bye

Print ucEvalStr("ToLong(123.456)") ' returns 123
Print ucEvalStr("ToByte(-1)")      ' returns 255

Print ucEvalStr("Factorial(5)")    ' returns 120

 

 

Example A2:  Bootstrapping

 

This example demonstrates a bootstrapped definition.  The original Cos() function expects arguments to be in radians.  The definition below uses the original Cos() function in order to create a new Cos() function that expects arguments in degrees.  This example is only for demonstration.  There is a ucTrigMode routine for changing the trig mode.  Note that the original Cos() definition is not actually modified.  Instead, the new definition takes precedence.  However, in the final block of code, the new definition is deleted with ucReleaseItem(NewDef), and the original Cos() definition is now back in effect.  Note: In version 3.0 the Bootstrap keyword must precede the rest of the definition (in version 2.96, the example placed it at the end).

 

Visual Basic

Dim NewDef As Long

 

ucDefine "Const: pi = Atan(1)*4"

Print ucEvalStr("Cos(pi)")  ' returns -1

Print ucEvalStr("Cos(180)") ' returns -0.59846...

 

NewDef = ucDefineFunction("Bootstrap ~~ Cos(x) = Cos(x*pi/180)")

Print ucEvalStr("Cos(pi)")  ' returns  0.99849...

Print ucEvalStr("Cos(180)") ' returns -1

 

ucReleaseItem NewDef

' After deleting the new definition, we are back to the old Cos()

Print ucEvalStr("Cos(pi)")  ' returns -1

Print ucEvalStr("Cos(180)") ' returns -0.59846...

 

 

Example A3:  Redefining with Overwrite

 

This example demonstrates redefining a function with the Overwrite keyword.  When such a function is redefined, other functions that call it will return a result based on the redefined version.  This is useful in spreadsheet-like situations.  If the Overwrite keyword were omitted, modifying f(x) would not affect g(x).  g(x) would continue to return a result based on the original definition of f(x).

 

Visual Basic

ucDefine "Overwrite ~~ func: f(x) = 10 * x"

ucDefine "func: g(x) = f(x) + 1)

 

Print ucEvalStr("f(5)") ' returns 50

Print ucEvalStr("g(5)") ' returns 51

 

ucDefine "Overwrite ~~ func: f(x) = 100 * x"

Print ucEvalStr("f(5)") ' returns 500

Print ucEvalStr("g(5)") ' returns 501

 

 

Callback definitions (native and non-native)

 

In addition to self-contained function definitions, uCalc FMP allows you to link a function definition to the address of a routine found in a DLL, or a routine in your source code that is written in the language of your host compiler.  Both native and non-native definitions have their advantages and disadvantages.  Native definitions do not use extra overhead, and are therefore considerably faster than non-native definitions.  However, a native callback requires uCalc code.  So to link directly to an API routine that wasn't written for uCalc, your definition would need to be non-native.

 

 

Native callbacks

 

Functions and operators that come with uCalc FMP, such as Sin(), Cos(), Exp(), Sqr(), +, -, *, etc., are defined using a method that is native to the way uCalc operates.  Such native callback routines use little overhead, making them fast.  uCalc allows you to create native routines as well.  The function name in the definition should be preceded with "Native:", as in the examples below.  The exception to this is .NET (C# and VB.NET), where because of the use of delegates, native callbacks are the only supported callbacks.  So in C# or VB.NET, whether or not you use the word Native:, the definition will be the same.

 

A shortcut for ucDefineFunction "Native: ..." is ucDefine "Func:: ...".

 

Regardless of the number of arguments, and the data type of return value, the routine's outer structure is always the same.  It must always be a routine that doesn't return a value (in other words, a void in C++ or C#, a procedure in Delphi, or a Sub in Visual Basic and PowerBASIC).  The callback always receives exactly one argument, which is a 32-bit integer (it doesn't matter if it's a long, DWORD, or an equivalent).  Although you may give the argument any name, it is best to name it Expr for consistency with this help file.  This is a handle for the expression.

 

Your callback should use ucArg (see further down for another alternative) to retrieve numeric arguments, ucArgStr for string arguments, or ucArgHandle to retrieve the handle of the argument if it was passed ByHandle or ByExpr.  ucArgCount returns the number of arguments that are being passed.  The return value of your callback function is set with ucReturn (for numbers) or ucReturnStr (for strings).

 

If your function is named MyFunction, then the first line(s) of your native callback should always look almost exactly like one of the following, with the only variation being based on the name of the function and the compiler you are using:

 

Borland C++ Builder (always remember to use _stdcall here)

void _stdcall MyArea(DWORD Expr)

 

C#

public uCalc.ucCallback d_MyArea = new uCalc.ucCallback(MyArea);

static void MyArea(int Expr)

 

Delphi (always remember to use stdcall here)

procedure MyArea(Expr: Longword); stdcall;

 

PowerBASIC

Sub MyArea(ByVal Expr As ucExpression)

 

Visual Basic

Sub MyArea(ByVal Expr As Long)

 

Visual Basic.NET

Sub MyArea(ByVal Expr As Integer)

 

Visual C++ (always remember to use _stdcall here)

void _stdcall MyArea(DWORD Expr)

 

Note:  If you are using C++ or Delphi, you should always remember to use the stdcall directive.  uCalc has no way of detecting whether or not you remembered to do this.  C++ or Delphi callbacks without stdcall may cause the callback to malfunction in subtle (and sometimes not-so-subtle) ways.

 

 

Example B1:  A native callback with two numeric arguments

 

This example creates a native function named MyArea, which returns the product of the two arguments.  If either of the arguments is a negative number, then ucSetErrorMesage raises an error using the error message string that is supplied to it.  Once defined, MyArea(3, 4) would return 12.  And if you are using ucEvalStr(), then MyArea(-5, 3) would return the message:  Length cannot be negative  instead of a numeric value.

 

Borland C++ Builder

// The following line can be placed in TForm1::FormCreate()

ucDefineFunction("Native: MyArea(Length, Width)", MyArea);

 

// The following callback routine can be placed just before TForm1::FormCreate()

// IMPORTANT: Always remember to use  stdcall  for uCalc callbacks (in C++)

void _stdcall MyArea(DWORD Expr)

{

   long double MyLength, MyWidth;  

 

   MyLength = ucArg(Expr, 1);

   MyWidth = ucArg(Expr, 2);  

  

   if(MyLength < 0) ucRaiseErrorMessage(Expr, "Length cannot be negative");

   if(MyWidth  < 0) ucRaiseErrorMessage(Expr, "Width cannot be negative");

 

   ucReturn(Expr, MyLength * MyWidth);

}

 

C#

// The following line can be placed in Form1_Load()

uc.ucDefineFunction("Native: MyArea(Length, Width)", d_MyArea);

 

// The following callback routine can be placed just before Form1_Load()

public uCalc.ucCallback d_MyArea = new uCalc.ucCallback(MyArea);

static void MyArea(int Expr)

{

   double MyLength, MyWidth;  

 

   MyLength = uCalc.ucArg(Expr, 1);

   MyWidth = uCalc.ucArg(Expr, 2);  

  

   if(MyLength < 0) uCalc.ucRaiseErrorMessage(Expr, "Length cannot be negative");

   if(MyWidth  < 0) uCalc.ucRaiseErrorMessage(Expr, "Width cannot be negative");

 

   uCalc.ucReturn(Expr, MyLength * MyWidth);

}

 

Delphi

// The following line can be placed in TForm1.FormCreate()

ucDefineFunction('Native: MyArea(Length, Width)', @MyArea);

 

// The following code can be placed just before TForm1.FormCreate()

// IMPORTANT: Always remember to use  stdcall  for uCalc callbacks

procedure MyArea(Expr: Longword); stdcall;

var

   MyLength, MyWidth: Extended;

begin

   MyLength := ucArg(Expr, 1);

   MyWidth := ucArg(Expr, 2);  

  

   if MyLength < 0 then ucRaiseErrorMessage(Expr, 'Length cannot be negative');

   if MyWidth  < 0 then ucRaiseErrorMessage(Expr, 'Width cannot be negative');

 

   ucReturn(Expr, MyLength * MyWidth);

end;

 

PowerBASIC

' The following line can be placed in PBMain()

ucDefineFunction "Native: MyArea(Length, Width)", CodePtr(MyArea)

 

' The following callback routine can be placed just before PBMain()

Sub MyArea(ByVal Expr As ucExpression)

   Dim MyLength As Extended, MyWidth As Extended

 

   MyLength = ucArg(Expr, 1)

   MyWidth = ucArg(Expr, 2)  

  

   If MyLength < 0 Then ucRaiseErrorMessage Expr, "Length cannot be negative"

   If MyWidth  < 0 Then ucRaiseErrorMessage Expr, "Width cannot be negative"

 

   ucReturn(Expr, MyLength * MyWidth)

End Sub

 

Visual Basic

' The following line can be placed in Form_Load()

ucDefineFunction "Native: MyArea(Length, Width)", AddressOf MyArea

 

' The following callback routine goes in a separate module, such as DemoVB.Bas

Sub MyArea(ByVal Expr As Long)

   Dim MyLength As Double, MyWidth As Double

 

   MyLength = ucArg(Expr, 1)

   MyWidth = ucArg(Expr, 2)  

  

   If MyLength < 0 Then ucRaiseErrorMessage Expr, "Length cannot be negative"

   If MyWidth  < 0 Then ucRaiseErrorMessage Expr, "Width cannot be negative"

 

   ucReturn Expr, MyLength * MyWidth

End Sub

 

Visual Basic .NET

' The following lines can be placed in Form1_Load()

Static d_MyArea As ucCallback = New ucCallback(AddressOf MyArea)

ucDefineFunction("Native: MyArea(Length, Width)", d_MyArea)

 

' The following callback routine goes in a separate module, such as DemoNET.vb

Sub MyArea(ByVal Expr As Integer)

   Dim MyLength As Double, MyWidth As Double

 

   MyLength = ucArg(Expr, 1)

   MyWidth = ucArg(Expr, 2)

 

   If MyLength < 0 Then ucRaiseErrorMessage(Expr, "Length cannot be negative")

   If MyWidth  < 0 Then ucRaiseErrorMessage(Expr, "Width cannot be negative")

 

   ucReturn(Expr, MyLength * MyWidth)

End Sub

 

 

Example B2:  A native callback with any number of arguments

 

This example features a function with an indefinite number of arguments.  Based on the way it is defined, MyAverage() takes at least one numeric argument.  Beyond that, it can accept any additional number of numeric arguments.  Once defined, MyAverage(3, 4) would return 3.5, and  MyAverage(3, 4, 5, 6) would return 4.5.  MyAverage(3, 4, "5") would return an error, because the last argument, which is a string is not compatible with the first argument.  In the previous example the keyword Native: was used in all the examples.  In the example below, it was not used in C# and VB.NET.  That's because the presence or absence of Native: has no effect in those two compilers, since because of their use of delegates for callbacks, they only support native callbacks.

 

Borland C++ Builder

// The following line can be placed in TForm1::FormCreate()

ucDefineFunction("Native: MyAverage(x ...)", MyAverage);

 

// The following callback routine can be placed just before TForm1::FormCreate()

// IMPORTANT: Always remember to use  stdcall  for uCalc callbacks (in C++)

void _stdcall MyAverage(DWORD Expr)

{

   long x;

   double Total = 0;

 

   for(x = 1; x <= ucArgCount(Expr); x++)

      Total = Total + ucArg(Expr, x);

 

   ucReturn(Expr, Total / ucArgCount(Expr));

}

 

C#

// The following line can be placed in Form1_Load()

uc.ucDefineFunction("Native: MyAverage(x ...)", d_MyAverage);

 

// The following callback routine can be placed just before Form1_Load()

public uCalc.ucCallback d_MyAverage = new uCalc.ucCallback(MyAverage);

static void MyAverage(int Expr)

{

   int x;

   double Total = 0;

 

   for(x=1; x <= uCalc.ucArgCount(Expr); x++)

      Total = Total + uCalc.ucArg(Expr, x);       

 

   uCalc.ucReturn(Expr, Total / uCalc.ucArgCount(Expr));

}

 

Delphi

// The following line can be placed in TForm1.FormCreate()

ucDefineFunction('Native: MyAverage(x ...)', @MyAverage);

 

// The following code can be placed just before TForm1.FormCreate()

// IMPORTANT: Always remember to use  stdcall  for uCalc callbacks

procedure MyAverage(Expr: Longword); stdcall;

var

   x: Longint;

   Total: Extended;

begin

   Total := 0;

   for x := 1 to ucArgCount(Expr) do Total := Total + ucArg(Expr, x);

   ucReturn(Expr, Total / ucArgCount(Expr));

end;

 

PowerBASIC

' The following line can be placed in PBMain()

ucDefineFunction "Native: MyAverage(x ...)", CodePtr(MyAverage)   

 

' The following callback routine can be placed just before PBMain()

Sub MyAverage(ByVal Expr As ucExpression)

   Dim x As Long, Total As Extended

 

   For x = 1 To ucArgCount(Expr)

      Total = Total + ucArg(Expr, x)

   Next

 

   ucReturn(Expr, Total / ucArgCount(Expr))

End Sub

 

Visual Basic

' The following line can be placed in Form_Load()

ucDefineFunction "Native: MyAverage(x ...)", AddressOf MyAverage

 

' The following callback routine goes in a separate module, such as DemoVB.Bas

Sub MyAverage(ByVal Expr As Long)

   Dim x As Long, Total As Double

 

   For x = 1 To ucArgCount(Expr)

      Total = Total + ucArg(Expr, x)

   Next

 

   ucReturn Expr, Total / ucArgCount(Expr)

End Sub

 

Visual Basic .NET

' The following lines can be placed in Form1_Load()

Static d_MyAverage As ucCallback = New ucCallback(AddressOf MyAverage)

ucDefineFunction("Native: MyAverage(x ...)", d_MyAverage)

 

' The following callback routine goes in a separate module, such as DemoNET.vb

Sub MyAverage(ByVal Expr As Integer)

   Dim x As Integer, Total As Double

 

   For x = 1 To ucArgCount(Expr)

      Total = Total + ucArg(Expr, x)

   Next

 

   ucReturn(Expr, Total / ucArgCount(Expr))

End Sub

 

Note: In version 2.96, the keyword “Native:” was not required for some compilers.  Now it’s required equally by all compilers.

 

Example B3:  Native callback string functions

 

The callback examples above dealt only with numeric values so far.  This example here demonstrates a function that has a string argument and also returns a string.  The main conceptual difference between example B1 and this one is that we have a string argument that is retrieved with ucArgStr instead of ucArg, and the function's string result is returned with ucReturnStr instead of ucReturn.  The rest of it works much the same.  This function returns the left-most characters of a string, which is supplied as the first argument.  The second argument determines the number of characters to return.  For instance MyLeft("Hello World", 2) would return "He", and MyLeft("Hello World", 7) would return "Hello W".

 

C++ Builder

// The following line can be placed in TForm1::FormCreate()

ucDefineFunction("Native: MyLeft(Text As String, Count) As String", MyLeft);

 

// The following callback routine can be placed just before TForm1::FormCreate()

// IMPORTANT: Remember to use _stdcall for your uCalc callbacks

void _stdcall MyLeft(DWORD Expr)

{

  AnsiString MyString;

  MyString = ucArgStr(Expr, 1);

  // Notice that ".c_str()" is appended to the end of the returned AnsiString variable

  ucReturnStr(Expr, MyString.SubString(1, ucArg(Expr, 2)).c_str());

}

 

C#

// The following line can be placed in Form1_Load()

// The keyword Native: was omitted.  It has no effect in C#

uc.ucDefineFunction("MyLeft(Text As String, Count) As String", d_MyLeft);

 

// The following callback routine can be placed just before Form1_Load()

public uCalc.ucCallback d_MyLeft = new uCalc.ucCallback(MyLeft);

static void MyLeft(int Expr)

{          

   uCalc.ucReturnStr(Expr,

   Microsoft.VisualBasic.Strings.Left(uCalc.ucArgStr(Expr, 1),

   (int)uCalc.ucArg(Expr, 2)));

}

 

Delphi

// The following line can be placed in TForm1.FormCreate()

ucDefineFunction('Native: MyLeft(Text As String, Count) As String', @MyLeft);

 

// The following code can be placed just before TForm1.FormCreate()

// IMPORTANT: Always remember to use  stdcall  for uCalc callbacks

procedure MyLeft(Expr: Longword); stdcall;

begin

   ucReturnStr(Expr, copy(ucArgStr(Expr, 1), 1, Trunc(ucArg(Expr, 2))));

end;

 

PowerBASIC

' The following line can be placed in PBMain()

ucDefineFunction "Native: MyLeft(Text As String, Count) As String", CodePtr(MyLeft)   

 

' The following callback routine can be placed just before PBMain()

Sub MyLeft(ByVal Expr As ucExpression)

    ucReturnStr(Expr, Left$(ucArgStr(Expr, 1), ucArg(Expr, 2)))

End Sub

 

Visual Basic (classic)

' The following line can be placed in Form_Load()

ucDefineFunction "Native: MyLeft(Text As String, Count) As String", AddressOf MyLeft

 

' The following callback routine goes in a separate module, such as DemoVB.Bas

Sub MyLeft(ByVal Expr As Long)

    ucReturnStr Expr, Left$(ucArgStr(Expr, 1), ucArg(Expr, 2))

End Sub

 

Visual Basic .NET

' The following lines can be placed in Form1_Load()

' The keyword Native: was omitted.  It has no effect in VB.NET

Static d_MyLeft As ucCallback = New ucCallback(AddressOf MyLeft)

ucDefineFunction("MyLeft(Text As String, Count) As String", d_MyLeft)

 

' The following callback routine goes in a separate module, such as DemoNET.vb

Sub MyLeft(ByVal Expr As Integer)

   ucReturnStr(Expr, Left$(ucArgStr(Expr, 1), ucArg(Expr, 2)))

End Sub

 

Visual C++

// The following line can be placed in a main or startup function

ucDefineFunction("Native: MyLeft(Text As String, Count) As String", MyLeft);

 

// IMPORTANT: Remember to use _stdcall for your uCalc callbacks

void _stdcall MyLeft(DWORD Expr)

{

  CString MyString;

  MyString = ucArgStr(Expr, 1);

 

  ucReturnStr(Expr, MyString.Left(ucArg(Expr, 2)));

}

 

 

Example B4:  Native callback ByExpr arguments

 

This example demonstrates arguments passed ByExpr.  If the first argument of MyIIF is non-zero, then MyIIF returns the result of evaluating the second argument, otherwise it returns the result of evaluating the 3rd argument.  So, MyIIF(1, 2, 3) would return 2.  MyIIF(0, 2, 3) would return 3.  uCalc allows several definitions for the same function name, as long as each definition has a different number of arguments, or the arguments in the definitions are not of the same data types.  So a string version of MyIIF is also defined.  MyIIF(1, "True", "False") would return "True", while MyIIF(0, "True", "False") would return "False".  If MyIIF() were defined using ordinary arguments, such as those of example B1, then MyIIF(1, 2, 1 / 0) would raise an error (assuming the FPU setting for division by 0 is not masked), instead of returning a 2, even though the last argument is irrelevant when the first argument is non-zero.  With the second and third arguments passed ByExpr, the callback is able to select which of the two arguments to evaluate, instead of evaluating both.

 

Visual Basic

' The following two lines can be placed in Form_Load()

ucDefineFunction "Native: MyIIf(cond, ByExpr TruePart, ByExpr FalsePart)", AddressOf MyIIF_Numeric

ucDefineFunction "Native: MyIIf(cond, ByExpr TruePart As String, ByExpr FalsePart As String) As String", AddressOf MyIIF_String

 

' The following callback routines go in a separate module, such as DemoVB.Bas

Sub MyIIF_Numeric(ByVal Expr As Long)

   Dim Condition As Long, TruePart As Long, FalsePart As Long

  

   Condition = ucArg(Expr, 1)

   TruePart = ucArgHandle(Expr, 2)

   FalsePart = ucArgHandle(Expr, 3)

  

   If Condition <> 0 _

   Then ucReturn Expr, ucEvaluate(TruePart) _

   Else ucReturn Expr, ucEvaluate(FalsePart)

End Sub

 

Sub MyIIF_String(ByVal Expr As Long)

   Dim Condition As Long, TruePart As Long, FalsePart As Long

  

   Condition = ucArg(Expr, 1)

   TruePart = ucArgHandle(Expr, 2)

   FalsePart = ucArgHandle(Expr, 3)

  

   If Condition <> 0 _

   Then ucReturnStr Expr, ucEvaluateStr(TruePart) _

   Else ucReturnStr Expr, ucEvaluateStr(FalsePart)

End Sub

 

 

Example B5:  Native callback ByHandle argument

 

The following example demonstrates the use of an argument that is passed ByHandle.  The TypeName() function defined below takes the handle of the argument, and returns the name of its data type.  The argument can be an item, such as a function or variable, or it can be any expression.

 

Note: uCalc() and uCalcStr() are not supported as part of the math parser itself.

 

Visual Basic

' The following line can be placed in Form_Load()

ucDefineFunction "Native: TypeName(ByHandle x As AnyType) As String", AddressOf Func_TypeName

 

' Create a command button named Command1 and place the following in Command1_Click()

ucDefineVariable "x As Long"

ucDefineVariable "y As Byte"

 

Print ucEvalStr("TypeName(x)") ' Returns "Long"

Print ucEvalStr("TypeName(y)") ' Returns "Byte"

Print ucEvalStr("TypeName('Hello World')") ' Returns "String"

Print ucEvalStr("TypeName(x + y)") ' Returns "Double" (some compilers will return "Extended")

' x + y is converted to double because there are no native integer operators.

' Native operators of any type, however, can be created if necessary.

 

' The following callback routine goes in a separate module, such as DemoVB.Bas

Sub Func_TypeName(ByVal Expr As Long)

   Dim ArgHandle As Long

   Dim TypeHandle As Long

   Dim TypeName As String

 

   ArgHandle  = ucArgHandle(Expr, 1)

   TypeHandle = uCalc(uc_GetItemData, "", ArgHandle, uc_DataType)

   TypeName   = uCalcStr(uc_GetItemData, "", TypeHandle, uc_SymbolName)

 

   ucReturnStr Expr, TypeName

End Sub

 

See the following examples as well:

Local variable, and ByHandle argument (Summation)

Local variable, and ByHandle argument (Equation Solver)

 

 

Non-native callbacks

 

The main advantage of non-native callbacks is that you are not required to adapt the callback in any way for it to work with uCalc.  This means, for instance, that you can attach a uCalc function definition directly to the address of a Windows API routine, or a routine in a pre-existing DLL that was not created with uCalc in mind.  Or you can attach it to ordinary routines in your source code without having to modify them or add any uCalc code to it.  The disadvantage is that this requires some additional overhead, which makes it slower than native routines.  Also, non-native callbacks can only handle functions with a fixed number of arguments.  Since C# and VB.NET use delegates for callbacks, those two compilers cannot use non-native uCalc callbacks (at least not directly).

 

The actual callback function can be defined in almost any way.  The main considerations are that arguments that are defined ByRef should be received by reference in your callback, and those that are ByVal, should be received by value.  These are not interchangeable.  In the uCalc definition, the data types for the function arguments, as well as the function's return value, must match those of the callback routine.  Unlike errors in a self-contained definition, which uCalc can detect, uCalc has absolutely no way of knowing whether a function's arguments and return type in your definition correctly match those found in your callback, and whether you remembered to use stdcall (if you're using Delphi or C++).  A mismatch may cause the callback to malfunction in subtle (and sometimes not-so-subtle) ways.

 

If you define a uCalc argument with ByExpr, or ByHandle, the callback should receive a 32-bit integer (Long, Dword, or equivalent) by value for the handle (regardless of the actual type of the argument).

 

 

Example C1:  A non-native callback

 

This example demonstrates a non-native function that has a mix of arguments of various numeric types, and that are passed ByVal or ByRef.  NonNativeFunc() simply adds up the three numeric arguments and returns a result.  So NonNativeFunc(3, 4, 5) returns 12.

 

Borland C++ Builder

// The following line can be placed in TForm1::FormCreate()

ucDefineFunction("NonNativeFunc(ByVal a As Double, ByRef b As Long, ByVal c As Byte) As Extended", NonNativeFunc);

 

// The following callback routine can be placed just before TForm1::FormCreate()

// IMPORTANT: Always remember to use  stdcall  for uCalc callbacks (in C++)

long double _stdcall NonNativeFunc(double a, long& b, byte c)

{

   return a + b + c;

}

 

C#

You cannot create non-native callbacks with C#.

 

Delphi

// The following line can be placed in TForm1.FormCreate()

ucDefineFunction('NonNativeFunc(ByVal a As Double, ByRef b As Long, ByVal c As Byte) As Extended', @NonNativeFunc);

 

// The following code can be placed just before TForm1.FormCreate()

// IMPORTANT: Always remember to use  stdcall  for uCalc callbacks

function NonNativeFunc(a: Double; var b: Longint; c: Byte): Extended; stdcall;

begin

   NonNativeFunc := a + b + c;

end;

 

PowerBASIC

' The following line can be placed in PBMain()

ucDefineFunction "NonNativeFunc(ByVal a As Double, ByRef b As Long, ByVal c As Byte) As Extended", CodePtr(NonNativeFunc)

 

' The following callback routine can be placed just before PBMain()

Function NonNativeFunc(ByVal a As Double, ByRef b As Long, ByVal c As Byte) As Extended

   NonNativeFunc = a + b + c

End Function

 

Visual Basic

' The following line can be placed in Form_Load()

ucDefineFunction "NonNativeFunc(ByVal a As Double, ByRef b As Long, ByVal c As Byte) As Double", AddressOf NonNativeFunc

 

' The following callback routine goes in a separate module, such as DemoVB.Bas

Function NonNativeFunc(ByVal a As Double, ByRef b As Long, ByVal c As Byte) As Double

   NonNativeFunc = a + b + c

End Function

 

Visual Basic .NET

You cannot create non-native callbacks with VB.NET.

 

Visual C++

// IMPORTANT: Remember to use _stdcall for your uCalc callbacks

long double _stdcall NonNativeFunc(double a, long& b, byte c)

{

   return a + b + c;

}

 

// The following can be placed in main()

ucDefineFunction("NonNativeFunc(ByVal a As Double, ByRef b As Long, ByVal c As Byte) As Extended", NonNativeFunc);

 

 

Example C2:  Strings in non-native functions

 

Dealing with numeric types in non-native routines is pretty straightforward.  Dealing with strings in non-native routines, however, can be pretty tricky.  A variable of given numeric type typically has a pre-set size, which stays the same, and remains in the same memory location.  On the other hand, some strings types can have a different but fixed size for different variables.  Or another string type might allow the same variable to change size.  The string data may be at a fixed memory location, or it may change when the data changes.  A Double usually represents the same numeric type throughout the various supported compilers, whereas a String in one compiler might be different from a String in another compiler.  A string might take one byte per character, or it might take several.  Special consideration must be given for the lifespan of the return string, etc.  For this reason, when possible, it may be best to deal with native callbacks instead, especially when strings are involved.  If you must use strings in non-native callbacks, special care should be taken.

 

This example demonstrates a function in which one of the arguments is a string.  This function also returns a string.  Note how different compilers use different kinds of strings.  For instance, String in uCalc matches String in PowerBASIC, while WideString in uCalc is what matches String in Visual Basic.  uCalc's String arg corresponds with Delphi's AnsiString, while the return value uses PChar.  In VC++ you’re dealing with const char* and LPTCSR.  Special string-related keywords to take note of are highlighted in color.  StringRepeat(MyString, n) repeats MyString n number of times.  So StringRepeat("Ha ", 3) would return "Ha Ha Ha ".

 

Borland C++ Builder

// The following line can be placed in TForm1::FormCreate()

ucDefineFunction("StringRepeat(ByRef MyString As String, ByVal Count As Long) As String", StringRepeat);

 

// The following callback routine can be placed just before TForm1::FormCreate()

// IMPORTANT: Always remember to use  stdcall  for uCalc callbacks (in C++)

char* _stdcall StringRepeat(AnsiString& MyString, long Count)

{

   long x;

   static AnsiString TotalString;

 

   TotalString = "";

 

   for(x = 1; x <= Count; x++)

      TotalString = TotalString + MyString;

 

   return TotalString.c_str();

}

 

C#

You cannot create non-native callbacks with C#.

 

Delphi

// The following line can be placed in TForm1.FormCreate()

ucDefineFunction('StringRepeat(ByRef MyString As String, ByVal Count As Long) As String', @StringRepeat);

 

// The following code can be placed just before TForm1.FormCreate()

// IMPORTANT: Always remember to use  stdcall  for uCalc callbacks

var TotalString: AnsiString;

function StringRepeat(var MyString: AnsiString; Count: Longint): PChar; stdcall;

var

   x: Longint;

begin

   TotalString := '';

   for x := 1 To Count do TotalString := TotalString + MyString;

   StringRepeat := PChar(TotalString);

end;

 

PowerBASIC

' The following line can be placed in PBMain()

ucDefineFunction "StringRepeat(ByRef MyString As String, ByVal Count As Long) As String", CodePtr(StringRepeat)

 

' The following callback routine can be placed just before PBMain()

Function StringRepeat(ByRef MyString As String, ByVal Count As Long) As String

   Dim x As Long, TotalString As String

 

   For x = 1 To Count

      TotalString = TotalString + MyString

   Next

 

   StringRepeat = TotalString

End Function

 

Visual Basic

' The following line can be placed in Form_Load()

' Notice here that uCalc's WideString is used instead of String

ucDefineFunction "StringRepeat(ByRef MyString As WideString, ByVal Count As Long) As WideString", AddressOf StringRepeat

 

' The following callback routine goes in a separate module, such as DemoVB.Bas

Function StringRepeat(ByRef MyString As String, ByVal Count As Long) As String

   Dim x As Long, TotalString As String

 

   For x = 1 To Count

      TotalString = TotalString + MyString

   Next

 

   StringRepeat = TotalString

End Function

 

Visual Basic .NET

You cannot create non-native callbacks with VB.NET.

 

Visual C++

// IMPORTANT: Remember to use _stdcall for your uCalc callbacks

char* _stdcall StringRepeat(const char* MyString, long Count)

{

   long x;

   static CString TotalString;

 

   TotalString = "";

  

   for(x = 1; x <= Count; x++)

         TotalString = TotalString + MyString;     

 

   return TotalString.GetBuffer(80);  

}

 

// The following can be placed in main()

ucDefineFunction("StringRepeat(ByRef MyString As LPCSTR, ByVal Count As Long) As String", StringRepeat);

 

 

Example C3:  Non-native callback with ByExpr arguments

 

This example is a non-native version of the string IIF function from Example B4.  nn_MyIIf(1, "T", "F") would return "T".  nn_MyIIf(0, "T", "F") would return "F".  Notice that although the last two arguments are strings, the callback must receive them as 32-bit values, since they are passed ByExpr.  The same is true for arguments that are passed ByHandle.

 

Non-native callbacks are supported in order to accommodate callback routines that were designed without uCalc in mind.  Since ByExpr and ByHandle are unique to uCalc, it may make better sense to use those with native callbacks instead.

 

Visual Basic

' The following line can be placed in Form_Load()

' Notice here that uCalc's WideString is used instead of String for the return value

ucDefineFunction "nn_MyIIf(ByVal cond As Long, ByExpr TruePart As String, ByExpr FalsePart As String) As WideString", AddressOf nn_MyIIFStr

 

' The following callback routine goes in a separate module, such as DemoVB.Bas

Function nn_MyIIFStr(ByVal cond As Long, ByVal TruePart As Long, ByVal FalsePart As Long) As String

   If cond Then nn_MyIIFStr = ucEvaluateStr(TruePart) Else nn_MyIIFStr = ucEvaluateStr(FalsePart)

End Function

 

 

Remarks

 

New or enhanced in version 3.0

 

Note:

·         The previous help file showed the Bootstrap keyword at the end of the definition.  In 3.0, Bootstrap must precede the rest of the definition.

·         uCalc() and uCalcStr() used in example B5 are no longer supported as part of the math parser.

·         Note: In version 2.96, the keyword Native: was not required for some compilers when defining native callbacks.  Now it’s required equally by all compilers.

 

 

New or enhanced in version 2.96

 

Note: ucParam, ucParamStr, and ucParamCount were renamed ucArg, ucArgStr, and ucArgCount in version 3.0.

 

Issues related to version 2.96

 

New or enhanced in version 2.9+

 

Issues for users migrating from version 2.0

 

ucDefineFunction "FunctionName(ArgCount)", CodeAddress

 

Functions defined this way were faster than regular callback definitions.  However, you were limited to two arguments; and they could only be numeric.  Now, use native callbacks for speed.