uCalc API Version: 2.1.3-preview.2 Released: 6/17/2026

Warning

uCalc API Preview Release Notice:The documentation describes the intended behavior of the API. The current preview build contains incomplete features, unoptimized performance, and is subject to breaking changes.

Metaprogramming in Callbacks (ByHandle)

Product: 

Class: 

Explains how the ByHandle parameter modifier enables metaprogramming in callbacks by passing an argument's underlying metadata object.

Remarks

🔬 Metaprogramming in Callbacks: The Power of ByHandle

While most functions operate on values, some of the most powerful programming techniques involve operating on the code itself. This is called metaprogramming. In uCalc, the ByHandle parameter modifier is the key that unlocks this capability, allowing your callbacks to inspect the properties of the arguments they receive, not just their final values.

The Three Flavors of Argument Passing

To understand ByHandle, it's useful to compare it with the other argument modifiers:

  • ByVal (Default): Passes the argument's final, computed value. This is standard behavior.
  • ByExpr: Passes the argument as an unevaluated Expression object, representing the code itself. This is used for lazy evaluation.
  • ByHandle: Passes the argument's underlying Item object, representing its metadata. This is used for introspection.

What is an Item Handle?

When a parameter is marked with ByHandle, your callback doesn't receive a simple number or string. Instead, it receives a handle to the argument's internal Item object, which you can retrieve with cb.ArgItem(). This object is a rich container of metadata, allowing you to ask questions about the argument, such as:

  • What is its name? (item.Name())
  • What is its data type? (item.DataType())
  • Was it a literal constant or a variable? (item.IsProperty(ItemIs::Variable))
  • What is its value? (item.ValueStr())
  • Where is it in memory? (item.ValueAddr())

This ability to inspect an argument before deciding what to do with it is the essence of metaprogramming in uCalc.

Core Use Cases

  1. Generic Functions: Create versatile functions that can handle any data type. The practical example below shows a Print function that can accept and correctly display any number of arguments of any type by inspecting each one.
  2. Validation & Assertions: Write functions that check properties of their inputs. For example, an AssertIsVariable(arg) function could raise an error if the user passes a literal value like 5 instead of a variable name.
  3. Modifying Caller State: By getting a variable's memory address, a function can modify its value directly in the caller's scope, as demonstrated in the internal test's Swap function.

💡 Why uCalc? (Comparative Analysis)

  • vs. C# Reflection (MethodInfo, ParameterInfo): While C# has powerful reflection capabilities, they are often complex and operate on compile-time constructs. uCalc's ByHandle provides a lightweight, runtime-focused alternative that is deeply integrated into the callback model. Retrieving an argument's metadata is a simple method call, not a complex traversal of Type and Assembly objects.

  • vs. Dynamic Languages (Python/JavaScript): Languages like Python have rich introspection tools (e.g., the inspect module). ByHandle brings this dynamic power into a strongly-typed host environment (C#/C++), offering a unique combination of flexibility and performance. It allows you to build functions that are as dynamic as a Python script but run with the speed of compiled C++ code.

Examples

A simple `Describe` function that uses `ByHandle` to inspect an argument's name and data type.
				
					using uCalcSoftware;

var uc = new uCalc();

static void DescribeArg(uCalc.Callback cb) {
   // Retrieve the Item object for the first argument.
   var item = cb.ArgItem(1);

   // Inspect the item's metadata.
   var name = item.Name;
   if (name == "") {
      name = "(literal)";
   }

   Console.WriteLine($"  - Name: {name}, Type: {item.DataType.Name}");
}

uc.DefineFunction("Describe(ByHandle arg As AnyType)", DescribeArg);
uc.DefineVariable("my_var = 100");

Console.WriteLine("Inspecting a variable:");
uc.Eval("Describe(my_var)");

Console.WriteLine("Inspecting a literal value:");
uc.Eval("Describe(123.45)");

Console.WriteLine("Inspecting a string value:");
uc.EvalStr("Describe('abc xyz')");
				
			
Inspecting a variable:
  - Name: my_var, Type: double
Inspecting a literal value:
  - Name: (literal), Type: double
Inspecting a string value:
  - Name: (literal), Type: string
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

void ucalc_call DescribeArg(uCalcBase::Callback cb) {
   // Retrieve the Item object for the first argument.
   auto item = cb.ArgItem(1);

   // Inspect the item's metadata.
   auto name = item.Name();
   if (name == "") {
      name = "(literal)";
   }

   cout << "  - Name: " << name << ", Type: " << item.DataType().Name() << endl;
}
int main() {
   uCalc uc;
   uc.DefineFunction("Describe(ByHandle arg As AnyType)", DescribeArg);
   uc.DefineVariable("my_var = 100");

   cout << "Inspecting a variable:" << endl;
   uc.Eval("Describe(my_var)");

   cout << "Inspecting a literal value:" << endl;
   uc.Eval("Describe(123.45)");

   cout << "Inspecting a string value:" << endl;
   uc.EvalStr("Describe('abc xyz')");
}
				
			
Inspecting a variable:
  - Name: my_var, Type: double
Inspecting a literal value:
  - Name: (literal), Type: double
Inspecting a string value:
  - Name: (literal), Type: string
				
					Imports System
Imports uCalcSoftware
Public Module Program
   
   Public Sub DescribeArg(ByVal cb As uCalc.Callback)
      '// Retrieve the Item object for the first argument.
      Dim item = cb.ArgItem(1)
      
      '// Inspect the item's metadata.
      Dim name = item.Name
      If Len(name) = 0 Then
         name = "(literal)"
      End If
      
      Console.WriteLine($"  - Name: {name}, Type: {item.DataType.Name}")
   End Sub
   Public Sub Main()
      Dim uc As New uCalc()
      uc.DefineFunction("Describe(ByHandle arg As AnyType)", AddressOf DescribeArg)
      uc.DefineVariable("my_var = 100")
      
      Console.WriteLine("Inspecting a variable:")
      uc.Eval("Describe(my_var)")
      
      Console.WriteLine("Inspecting a literal value:")
      uc.Eval("Describe(123.45)")
      
      Console.WriteLine("Inspecting a string value:")
      uc.EvalStr("Describe('abc xyz')")
   End Sub
End Module
				
			
Inspecting a variable:
  - Name: my_var, Type: double
Inspecting a literal value:
  - Name: (literal), Type: double
Inspecting a string value:
  - Name: (literal), Type: string
A practical, generic `Print` function that can accept any number of arguments of any type and display them in a formatted string.
				
					using uCalcSoftware;

var uc = new uCalc();

static void PrintGeneric(uCalc.Callback cb) {
   string output = "";
   var i = 0;
   for ( i = 1; i <= cb.ArgCount(); i++) {
      // Get the item and retrieve its value as a string.
      var item = cb.ArgItem(i);
      output = output + item.ValueStr();
      if (i < cb.ArgCount()) {
         output = output + ", ";
      }
   }
   Console.WriteLine(output);
}

// Define a variadic function that accepts any number of arguments ByHandle.
uc.DefineFunction("Print(ByHandle args As AnyType...)", PrintGeneric);

uc.Eval("Print('User:', 'Alice', 'ID:', 101, 'Status:', true)");
				
			
User:, Alice, ID:, 101, Status:, true
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

void ucalc_call PrintGeneric(uCalcBase::Callback cb) {
   string output = "";
   auto i = 0;
   for ( i = 1; i <= cb.ArgCount(); i++) {
      // Get the item and retrieve its value as a string.
      auto item = cb.ArgItem(i);
      output = output + item.ValueStr();
      if (i < cb.ArgCount()) {
         output = output + ", ";
      }
   }
   cout << output << endl;
}
int main() {
   uCalc uc;
   // Define a variadic function that accepts any number of arguments ByHandle.
   uc.DefineFunction("Print(ByHandle args As AnyType...)", PrintGeneric);

   uc.Eval("Print('User:', 'Alice', 'ID:', 101, 'Status:', true)");
}
				
			
User:, Alice, ID:, 101, Status:, true
				
					Imports System
Imports uCalcSoftware
Public Module Program
   
   Public Sub PrintGeneric(ByVal cb As uCalc.Callback)
      Dim output As String = ""
      Dim i = 0
      For i  = 1 To cb.ArgCount()
         '// Get the item and retrieve its value as a string.
         Dim item = cb.ArgItem(i)
         output = output + item.ValueStr()
         If i < cb.ArgCount() Then
            output = output + ", "
         End If
      Next
      Console.WriteLine(output)
   End Sub
   Public Sub Main()
      Dim uc As New uCalc()
      '// Define a variadic function that accepts any number of arguments ByHandle.
      uc.DefineFunction("Print(ByHandle args As AnyType...)", AddressOf PrintGeneric)
      
      uc.Eval("Print('User:', 'Alice', 'ID:', 101, 'Status:', true)")
   End Sub
End Module
				
			
User:, Alice, ID:, 101, Status:, true