uCalc API Version: 2.1.3-preview.2 Released: 6/16/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
- Generic Functions: Create versatile functions that can handle any data type. The practical example below shows a
Printfunction that can accept and correctly display any number of arguments of any type by inspecting each one. - 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 like5instead of a variable name. - 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
Swapfunction.
💡 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'sByHandleprovides 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 ofTypeandAssemblyobjects.vs. Dynamic Languages (Python/JavaScript): Languages like Python have rich introspection tools (e.g., the
inspectmodule).ByHandlebrings 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 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')");
#include
#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 #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')"); }
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 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
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 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)");
#include
#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 #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)"); }
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 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