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.


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
				
					#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;
   }
}
				
			
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
				
			
5
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
				
					#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();

}
				
			
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
				
			
A: 100
B: 200
Main: 50
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
				
					#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();
}
				
			
--- 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
				
			
--- Efficient: Parse() once, then Evaluate() in a loop ---
10
40
90
160
250
360
490
640
810
1000
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
				
					#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;
         }
      }
   }
}
				
			
--- 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
				
			
--- Testing Expression Contexts ---
Default context (x=1.2): 6.2
Specific context (x=10):  15
Empty then parsed (x=1.2):12
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.
				
					#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;
   }
}
				
			
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
				
			
Greetings World, and Greetings again.