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.
Memory Management
Product:
Class:
Explains uCalc's memory management model and best practices for object lifetime to prevent resource leaks.
Remarks
⚙️ Memory Management
A uCalc instance is more than just an object; it's a complete, sandboxed parsing and evaluation engine. Because the core engine is written in high-performance C++, proper management of its memory and lifetime is a critical aspect of using the library correctly.
This guide explains why lifetime management is necessary and covers the recommended strategies for preventing resource leaks.
1. The Core Concept: Managed Handle vs. Unmanaged Engine
When you create a uCalc object in a managed language like C# or VB.NET, the variable you hold is a lightweight handle. This handle points to the substantial, underlying C++ engine instance where the real work happens. The garbage collector (GC) in .NET only knows about the handle, not the unmanaged C++ engine.
If you simply let a uCalc handle go out of scope, the GC will clean up the handle, but the C++ engine instance will remain in memory, causing a resource leak.
Therefore, you must explicitly tell the engine when an instance is no longer needed.
2. Automatic Lifetime Management (Recommended)
The best practice is to tie the lifetime of a uCalc instance to a specific lexical scope. This is achieved using language-idiomatic patterns that guarantee cleanup when the object is no longer needed. This pattern is often referred to as RAII (Resource Acquisition Is Initialization).
💎 For C# and VB.NET: The using Statement
All major uCalc objects implement the IDisposable interface, making them compatible with the using statement. This is the safest and most recommended pattern in .NET.
using (var tempCalc = new uCalc()) { Console.WriteLine("Inside scope, instance is active.");} // tempCalc is automatically released here.Console.WriteLine("Outside scope, instance has been released.");For more language-specific details, see the C# and VB topics.
💎 For C++: RAII and the Owned() Method
C++ developers should leverage the RAII pattern by creating uCalc objects on the stack. Calling the Owned() method instructs the object's destructor to automatically call Release() when it goes out of scope.
For more details, see the C++ language topic.
3. Manual Lifetime Management with Release()
If an object's lifetime is not tied to a specific scope (e.g., a long-lived global or application-wide instance), you must manage its lifecycle manually by calling the Release() method when you are finished with it.
// Create a new instance.var myInstance = uc.Clone();// ... use the instance throughout the application ...// Manually release it during application shutdown to prevent a leak.myInstance.Release();Console.WriteLine("Instance has been manually released.");Forgetting to call Release() on objects that are not managed by a using block or RAII is the most common cause of resource leaks.
Examples
Quick Start
using uCalcSoftware;
var uc = new uCalc();
// Create a new instance
using (var calc = new uCalc()) {
// Evaluate an expression
Console.WriteLine(calc.Eval("2 + 3"));
}
5 using uCalcSoftware; var uc = new uCalc(); // Create a new instance using (var calc = new uCalc()) { // Evaluate an expression Console.WriteLine(calc.Eval("2 + 3")); }
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
int main() {
uCalc uc;
// Create a new instance
{
uCalc calc;
calc.Owned(); // Causes calc to be released when it goes out of scope
// Evaluate an expression
cout << calc.Eval("2 + 3") << endl;
}
}
5 #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; int main() { uCalc uc; // Create a new instance { uCalc calc; calc.Owned(); // Causes calc to be released when it goes out of scope // Evaluate an expression cout << calc.Eval("2 + 3") << endl; } }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub Main()
Dim uc As New uCalc()
'// Create a new instance
Using calc As New uCalc()
'// Evaluate an expression
Console.WriteLine(calc.Eval("2 + 3"))
End Using
End Sub
End Module
5 Imports System Imports uCalcSoftware Public Module Program Public Sub Main() Dim uc As New uCalc() '// Create a new instance Using calc As New uCalc() '// Evaluate an expression Console.WriteLine(calc.Eval("2 + 3")) End Using End Sub End Module
Creating isolated evaluation contexts.
using uCalcSoftware;
var uc = new uCalc();
var main = new uCalc();
main.DefineVariable("rate = 0.05");
var scenarioA = main.Clone();
var scenarioB = main.Clone();
scenarioA.DefineVariable("rate = 0.10");
scenarioB.DefineVariable("rate = 0.20");
Console.WriteLine($"A: {scenarioA.Eval("1000 * rate")}");
Console.WriteLine($"B: {scenarioB.Eval("1000 * rate")}");
Console.WriteLine($"Main: {main.Eval("1000 * rate")}");
main.Release();
scenarioA.Release();
scenarioB.Release();
A: 100
B: 200
Main: 50 using uCalcSoftware; var uc = new uCalc(); var main = new uCalc(); main.DefineVariable("rate = 0.05"); var scenarioA = main.Clone(); var scenarioB = main.Clone(); scenarioA.DefineVariable("rate = 0.10"); scenarioB.DefineVariable("rate = 0.20"); Console.WriteLine($"A: {scenarioA.Eval("1000 * rate")}"); Console.WriteLine($"B: {scenarioB.Eval("1000 * rate")}"); Console.WriteLine($"Main: {main.Eval("1000 * rate")}"); main.Release(); scenarioA.Release(); scenarioB.Release();
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
int main() {
uCalc uc;
uCalc main;
main.DefineVariable("rate = 0.05");
auto scenarioA = main.Clone();
auto scenarioB = main.Clone();
scenarioA.DefineVariable("rate = 0.10");
scenarioB.DefineVariable("rate = 0.20");
cout << "A: " << scenarioA.Eval("1000 * rate") << endl;
cout << "B: " << scenarioB.Eval("1000 * rate") << endl;
cout << "Main: " << main.Eval("1000 * rate") << endl;
main.Release();
scenarioA.Release();
scenarioB.Release();
}
A: 100
B: 200
Main: 50 #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; int main() { uCalc uc; uCalc main; main.DefineVariable("rate = 0.05"); auto scenarioA = main.Clone(); auto scenarioB = main.Clone(); scenarioA.DefineVariable("rate = 0.10"); scenarioB.DefineVariable("rate = 0.20"); cout << "A: " << scenarioA.Eval("1000 * rate") << endl; cout << "B: " << scenarioB.Eval("1000 * rate") << endl; cout << "Main: " << main.Eval("1000 * rate") << endl; main.Release(); scenarioA.Release(); scenarioB.Release(); }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub Main()
Dim uc As New uCalc()
Dim main As New uCalc()
main.DefineVariable("rate = 0.05")
Dim scenarioA = main.Clone()
Dim scenarioB = main.Clone()
scenarioA.DefineVariable("rate = 0.10")
scenarioB.DefineVariable("rate = 0.20")
Console.WriteLine($"A: {scenarioA.Eval("1000 * rate")}")
Console.WriteLine($"B: {scenarioB.Eval("1000 * rate")}")
Console.WriteLine($"Main: {main.Eval("1000 * rate")}")
main.Release()
scenarioA.Release()
scenarioB.Release()
End Sub
End Module
A: 100
B: 200
Main: 50 Imports System Imports uCalcSoftware Public Module Program Public Sub Main() Dim uc As New uCalc() Dim main As New uCalc() main.DefineVariable("rate = 0.05") Dim scenarioA = main.Clone() Dim scenarioB = main.Clone() scenarioA.DefineVariable("rate = 0.10") scenarioB.DefineVariable("rate = 0.20") Console.WriteLine($"A: {scenarioA.Eval("1000 * rate")}") Console.WriteLine($"B: {scenarioB.Eval("1000 * rate")}") Console.WriteLine($"Main: {main.Eval("1000 * rate")}") main.Release() scenarioA.Release() scenarioB.Release() End Sub End Module
Illustrates the core relationship between the uCalc engine, an Item (variable), and a compiled Expression.
using uCalcSoftware;
var uc = new uCalc();
var VariableX = uc.DefineVariable("x");
var Expression = "x^2 * 10"; // Replace this with your expression
Console.WriteLine("--- Efficient: Parse() once, then Evaluate() in a loop ---");
var ParsedExpr = uc.Parse(Expression);
for (double x = 1; x <= 10; x++) {
VariableX.Value(x);
Console.WriteLine(ParsedExpr.Evaluate());
}
ParsedExpr.Release();
VariableX.Release();
--- Efficient: Parse() once, then Evaluate() in a loop ---
10
40
90
160
250
360
490
640
810
1000 using uCalcSoftware; var uc = new uCalc(); var VariableX = uc.DefineVariable("x"); var Expression = "x^2 * 10"; // Replace this with your expression Console.WriteLine("--- Efficient: Parse() once, then Evaluate() in a loop ---"); var ParsedExpr = uc.Parse(Expression); for (double x = 1; x <= 10; x++) { VariableX.Value(x); Console.WriteLine(ParsedExpr.Evaluate()); } ParsedExpr.Release(); VariableX.Release();
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
int main() {
uCalc uc;
auto VariableX = uc.DefineVariable("x");
auto Expression = "x^2 * 10"; // Replace this with your expression
cout << "--- Efficient: Parse() once, then Evaluate() in a loop ---" << endl;
auto ParsedExpr = uc.Parse(Expression);
for (double x = 1; x <= 10; x++) {
VariableX.Value(x);
cout << ParsedExpr.Evaluate() << endl;
}
ParsedExpr.Release();
VariableX.Release();
}
--- Efficient: Parse() once, then Evaluate() in a loop ---
10
40
90
160
250
360
490
640
810
1000 #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; int main() { uCalc uc; auto VariableX = uc.DefineVariable("x"); auto Expression = "x^2 * 10"; // Replace this with your expression cout << "--- Efficient: Parse() once, then Evaluate() in a loop ---" << endl; auto ParsedExpr = uc.Parse(Expression); for (double x = 1; x <= 10; x++) { VariableX.Value(x); cout << ParsedExpr.Evaluate() << endl; } ParsedExpr.Release(); VariableX.Release(); }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub Main()
Dim uc As New uCalc()
Dim VariableX = uc.DefineVariable("x")
Dim Expression = "x^2 * 10" '// Replace this with your expression
Console.WriteLine("--- Efficient: Parse() once, then Evaluate() in a loop ---")
Dim ParsedExpr = uc.Parse(Expression)
For x As Double = 1 To 10
VariableX.Value(x)
Console.WriteLine(ParsedExpr.Evaluate())
Next
ParsedExpr.Release()
VariableX.Release()
End Sub
End Module
--- Efficient: Parse() once, then Evaluate() in a loop ---
10
40
90
160
250
360
490
640
810
1000 Imports System Imports uCalcSoftware Public Module Program Public Sub Main() Dim uc As New uCalc() Dim VariableX = uc.DefineVariable("x") Dim Expression = "x^2 * 10" '// Replace this with your expression Console.WriteLine("--- Efficient: Parse() once, then Evaluate() in a loop ---") Dim ParsedExpr = uc.Parse(Expression) For x As Double = 1 To 10 VariableX.Value(x) Console.WriteLine(ParsedExpr.Evaluate()) Next ParsedExpr.Release() VariableX.Release() End Sub End Module
Shows how the constructor's context (default vs. specific instance) affects variable resolution during parsing.
using uCalcSoftware;
var uc = new uCalc();
// Set up two different uCalc contexts with the same variable name 'x'.
uCalc.DefaultInstance.DefineVariable("x = 1.2");
uc.DefineVariable("x = 10");
Console.WriteLine("--- Testing Expression Contexts ---");
// 1. Expression created in the *default* context uses x = 1.2
using (var exprDefault = new uCalc.Expression("x + 5")) {
Console.WriteLine($"Default context (x=1.2): {exprDefault.Evaluate()}");
// 2. Expression created in a *specific* context ('uc') uses x = 10
using (var exprSpecific = new uCalc.Expression(uc, "x + 5")) {
Console.WriteLine($"Specific context (x=10): {exprSpecific.Evaluate()}");
// 3. Create empty, then parse later (uses default context for the Parse call)
using (var exprEmpty = new uCalc.Expression()) {
exprEmpty.Parse("x * 10");
Console.WriteLine($"Empty then parsed (x=1.2):{exprEmpty.Evaluate()}");
}
}
}
--- Testing Expression Contexts ---
Default context (x=1.2): 6.2
Specific context (x=10): 15
Empty then parsed (x=1.2):12 using uCalcSoftware; var uc = new uCalc(); // Set up two different uCalc contexts with the same variable name 'x'. uCalc.DefaultInstance.DefineVariable("x = 1.2"); uc.DefineVariable("x = 10"); Console.WriteLine("--- Testing Expression Contexts ---"); // 1. Expression created in the *default* context uses x = 1.2 using (var exprDefault = new uCalc.Expression("x + 5")) { Console.WriteLine($"Default context (x=1.2): {exprDefault.Evaluate()}"); // 2. Expression created in a *specific* context ('uc') uses x = 10 using (var exprSpecific = new uCalc.Expression(uc, "x + 5")) { Console.WriteLine($"Specific context (x=10): {exprSpecific.Evaluate()}"); // 3. Create empty, then parse later (uses default context for the Parse call) using (var exprEmpty = new uCalc.Expression()) { exprEmpty.Parse("x * 10"); Console.WriteLine($"Empty then parsed (x=1.2):{exprEmpty.Evaluate()}"); } } }
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
int main() {
uCalc uc;
// Set up two different uCalc contexts with the same variable name 'x'.
uCalc::DefaultInstance().DefineVariable("x = 1.2");
uc.DefineVariable("x = 10");
cout << "--- Testing Expression Contexts ---" << endl;
// 1. Expression created in the *default* context uses x = 1.2
{
uCalc::Expression exprDefault("x + 5");
exprDefault.Owned(); // Causes exprDefault to be released when it goes out of scope
cout << "Default context (x=1.2): " << exprDefault.Evaluate() << endl;
// 2. Expression created in a *specific* context ('uc') uses x = 10
{
uCalc::Expression exprSpecific(uc, "x + 5");
exprSpecific.Owned(); // Causes exprSpecific to be released when it goes out of scope
cout << "Specific context (x=10): " << exprSpecific.Evaluate() << endl;
// 3. Create empty, then parse later (uses default context for the Parse call)
{
uCalc::Expression exprEmpty;
exprEmpty.Owned(); // Causes exprEmpty to be released when it goes out of scope
exprEmpty.Parse("x * 10");
cout << "Empty then parsed (x=1.2):" << exprEmpty.Evaluate() << endl;
}
}
}
}
--- Testing Expression Contexts ---
Default context (x=1.2): 6.2
Specific context (x=10): 15
Empty then parsed (x=1.2):12 #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; int main() { uCalc uc; // Set up two different uCalc contexts with the same variable name 'x'. uCalc::DefaultInstance().DefineVariable("x = 1.2"); uc.DefineVariable("x = 10"); cout << "--- Testing Expression Contexts ---" << endl; // 1. Expression created in the *default* context uses x = 1.2 { uCalc::Expression exprDefault("x + 5"); exprDefault.Owned(); // Causes exprDefault to be released when it goes out of scope cout << "Default context (x=1.2): " << exprDefault.Evaluate() << endl; // 2. Expression created in a *specific* context ('uc') uses x = 10 { uCalc::Expression exprSpecific(uc, "x + 5"); exprSpecific.Owned(); // Causes exprSpecific to be released when it goes out of scope cout << "Specific context (x=10): " << exprSpecific.Evaluate() << endl; // 3. Create empty, then parse later (uses default context for the Parse call) { uCalc::Expression exprEmpty; exprEmpty.Owned(); // Causes exprEmpty to be released when it goes out of scope exprEmpty.Parse("x * 10"); cout << "Empty then parsed (x=1.2):" << exprEmpty.Evaluate() << endl; } } } }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub Main()
Dim uc As New uCalc()
'// Set up two different uCalc contexts with the same variable name 'x'.
uCalc.DefaultInstance.DefineVariable("x = 1.2")
uc.DefineVariable("x = 10")
Console.WriteLine("--- Testing Expression Contexts ---")
'// 1. Expression created in the *default* context uses x = 1.2
Using exprDefault As New uCalc.Expression("x + 5")
Console.WriteLine($"Default context (x=1.2): {exprDefault.Evaluate()}")
'// 2. Expression created in a *specific* context ('uc') uses x = 10
Using exprSpecific As New uCalc.Expression(uc, "x + 5")
Console.WriteLine($"Specific context (x=10): {exprSpecific.Evaluate()}")
'// 3. Create empty, then parse later (uses default context for the Parse call)
Using exprEmpty As New uCalc.Expression()
exprEmpty.Parse("x * 10")
Console.WriteLine($"Empty then parsed (x=1.2):{exprEmpty.Evaluate()}")
End Using
End Using
End Using
End Sub
End Module
--- Testing Expression Contexts ---
Default context (x=1.2): 6.2
Specific context (x=10): 15
Empty then parsed (x=1.2):12 Imports System Imports uCalcSoftware Public Module Program Public Sub Main() Dim uc As New uCalc() '// Set up two different uCalc contexts with the same variable name 'x'. uCalc.DefaultInstance.DefineVariable("x = 1.2") uc.DefineVariable("x = 10") Console.WriteLine("--- Testing Expression Contexts ---") '// 1. Expression created in the *default* context uses x = 1.2 Using exprDefault As New uCalc.Expression("x + 5") Console.WriteLine($"Default context (x=1.2): {exprDefault.Evaluate()}") '// 2. Expression created in a *specific* context ('uc') uses x = 10 Using exprSpecific As New uCalc.Expression(uc, "x + 5") Console.WriteLine($"Specific context (x=10): {exprSpecific.Evaluate()}") '// 3. Create empty, then parse later (uses default context for the Parse call) Using exprEmpty As New uCalc.Expression() exprEmpty.Parse("x * 10") Console.WriteLine($"Empty then parsed (x=1.2):{exprEmpty.Evaluate()}") End Using End Using End Using End Sub End Module
A simple find-and-replace transformation to replace all occurrences of a word.
using uCalcSoftware;
var uc = new uCalc();
using (var t = new uCalc.Transformer()) {
// Define a simple replacement rule
t.FromTo("Hello", "Greetings");
// Execute the transformation on an input string
Console.WriteLine(t.Transform("Hello World, and Hello again."));
}
Greetings World, and Greetings again. using uCalcSoftware; var uc = new uCalc(); using (var t = new uCalc.Transformer()) { // Define a simple replacement rule t.FromTo("Hello", "Greetings"); // Execute the transformation on an input string Console.WriteLine(t.Transform("Hello World, and Hello again.")); }
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
int main() {
uCalc uc;
{
uCalc::Transformer t;
t.Owned(); // Causes t to be released when it goes out of scope
// Define a simple replacement rule
t.FromTo("Hello", "Greetings");
// Execute the transformation on an input string
cout << t.Transform("Hello World, and Hello again.") << endl;
}
}
Greetings World, and Greetings again. #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; int main() { uCalc uc; { uCalc::Transformer t; t.Owned(); // Causes t to be released when it goes out of scope // Define a simple replacement rule t.FromTo("Hello", "Greetings"); // Execute the transformation on an input string cout << t.Transform("Hello World, and Hello again.") << endl; } }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub Main()
Dim uc As New uCalc()
Using t As New uCalc.Transformer()
'// Define a simple replacement rule
t.FromTo("Hello", "Greetings")
'// Execute the transformation on an input string
Console.WriteLine(t.Transform("Hello World, and Hello again."))
End Using
End Sub
End Module
Greetings World, and Greetings again. Imports System Imports uCalcSoftware Public Module Program Public Sub Main() Dim uc As New uCalc() Using t As New uCalc.Transformer() '// Define a simple replacement rule t.FromTo("Hello", "Greetings") '// Execute the transformation on an input string Console.WriteLine(t.Transform("Hello World, and Hello again.")) End Using End Sub End Module