Native Extensions
Extensions are dynamically-loaded C++ libraries that add functionality to Quartz at runtime. This is how the entire standard library is implemented, and you can create your own extensions.
Overview
Quartz's extension system gives you:
- Native performance: Write performance-critical code in C++
- Full access: Use any C++ library from Quartz
- Clean integration: Extensions feel like native Quartz modules
- Easy deployment: Just drop a shared library in the extensions folder
Architecture
When Quartz starts, it:
- Scans
build/extensions/for shared libraries (.soon Linux,.dylibon macOS) - Loads each library with
dlopen() - Calls the
init_extension()function in each library - Extensions register their functions with the FunctionRegistry
build/extensions/
├── system_io/
│ └── libsystem_io.so
├── system_math/
│ └── libsystem_math.so
├── system_string/
│ └── libsystem_string.so
└── your_extension/
└── libyour_extension.so
Creating an Extension
Step 1: Create Directory Structure
mkdir extensions/my_extension
cd extensions/my_extension
Step 2: Create CMakeLists.txt
# extensions/my_extension/CMakeLists.txt
file(GLOB SOURCES *.cpp)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH "${CMAKE_BINARY_DIR}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default")
add_library(my_extension SHARED ${SOURCES})
target_include_directories(my_extension PRIVATE
../../include/core
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(my_extension PRIVATE qz-core)
Step 3: Create Extension Source
// extensions/my_extension/my_extension.cpp
#include "function_registry.h"
#include "types.h"
#include <iostream>
#include <cmath>
// Forward declarations
void register_my_functions(FunctionRegistry& reg);
// Entry point - called by Quartz runtime
extern "C" __attribute__((visibility("default")))
void init_extension(FunctionRegistry& reg) {
register_my_functions(reg);
}
void register_my_functions(FunctionRegistry& reg) {
// Simple function with no arguments
auto hello = [](const std::vector<Value>& args) -> Value {
std::cout << "Hello from my extension!" << std::endl;
return Value(); // void return
};
reg.registerFunction("my.hello", hello);
// Function with arguments and return value
auto add = [](const std::vector<Value>& args) -> Value {
if (args.size() < 2) return Value(0);
// Extract integers from arguments
if (std::holds_alternative<int>(args[0]) &&
std::holds_alternative<int>(args[1])) {
int a = std::get<int>(args[0]);
int b = std::get<int>(args[1]);
return Value(a + b);
}
return Value(0);
};
reg.registerFunction("my.add", add);
// Function working with doubles
auto hypot = [](const std::vector<Value>& args) -> Value {
if (args.size() < 2) return Value(0.0);
double a = 0, b = 0;
// Handle both int and double inputs
if (std::holds_alternative<int>(args[0]))
a = std::get<int>(args[0]);
else if (std::holds_alternative<double>(args[0]))
a = std::get<double>(args[0]);
if (std::holds_alternative<int>(args[1]))
b = std::get<int>(args[1]);
else if (std::holds_alternative<double>(args[1]))
b = std::get<double>(args[1]);
return Value(std::sqrt(a*a + b*b));
};
reg.registerFunction("my.hypot", hypot);
// Function working with strings
auto greet = [](const std::vector<Value>& args) -> Value {
if (args.empty()) return Value(std::string("Hello!"));
if (std::holds_alternative<std::string>(args[0])) {
std::string name = std::get<std::string>(args[0]);
return Value("Hello, " + name + "!");
}
return Value(std::string("Hello!"));
};
reg.registerFunction("my.greet", greet);
}
Step 4: Build the Extension
# From the project root
bash rebuild_core.sh
# Or build just extensions
cd build && make
Step 5: Use in Quartz
import system.io as io;
// Use your extension functions
my.hello(); // "Hello from my extension!"
let sum = my.add(5, 3);
io.out.println("5 + 3 =", sum); // 8
let h = my.hypot(3.0, 4.0);
io.out.println("Hypotenuse:", h); // 5.0
let greeting = my.greet("World");
io.out.println(greeting); // "Hello, World!"
The Value Type
Quartz uses std::variant to represent values:
using Value = std::variant<
int, // Integer
double, // Floating point
std::string, // String
bool // Boolean
>;
Working with Values
// Check type
if (std::holds_alternative<int>(value)) {
int i = std::get<int>(value);
}
// Create values
Value intVal(42);
Value doubleVal(3.14);
Value stringVal(std::string("hello"));
Value boolVal(true);
// Return void (empty value)
return Value();
Function Registry API
// Register a function
reg.registerFunction("namespace.functionName",
[](const std::vector<Value>& args) -> Value {
// Implementation
return Value();
}
);
// Function signature
// std::function<Value(const std::vector<Value>&)>
Best Practices
Naming Conventions
- Use dot notation for namespacing:
myext.function - Keep names lowercase with underscores:
my_ext.get_value - Be descriptive:
myext.calculate_distancenotmyext.cd
Error Handling
auto safeDivide = [](const std::vector<Value>& args) -> Value {
if (args.size() < 2) {
std::cerr << "Error: divide requires 2 arguments" << std::endl;
return Value(0.0);
}
double b = std::get<double>(args[1]);
if (b == 0) {
std::cerr << "Error: division by zero" << std::endl;
return Value(0.0);
}
double a = std::get<double>(args[0]);
return Value(a / b);
};
Type Flexibility
// Accept both int and double
double getNumericArg(const Value& v) {
if (std::holds_alternative<int>(v))
return static_cast<double>(std::get<int>(v));
if (std::holds_alternative<double>(v))
return std::get<double>(v);
return 0.0;
}
Advanced Topics
Using External Libraries
// Link against external libraries in CMakeLists.txt
find_package(OpenSSL REQUIRED)
target_link_libraries(my_extension PRIVATE OpenSSL::SSL)
Thread Safety
#include <mutex>
static std::mutex g_mutex;
static int g_counter = 0;
auto incrementCounter = [](const std::vector<Value>& args) -> Value {
std::lock_guard<std::mutex> lock(g_mutex);
return Value(++g_counter);
};
Metadata File
{
"name": "my_extension",
"version": "1.0.0",
"description": "My custom Quartz extension",
"author": "Your Name",
"functions": [
{
"name": "my.hello",
"description": "Prints a greeting",
"params": [],
"returns": "void"
},
{
"name": "my.add",
"description": "Adds two integers",
"params": ["int a", "int b"],
"returns": "int"
}
]
}
Example Extensions
Study the built-in extensions for more examples:
extensions/system_io/- Console I/Oextensions/system_math/- Math functionsextensions/system_string/- String manipulationextensions/system_convert/- Type conversions