Skip to content

ljaaskela/velk

Repository files navigation

License: MIT C++17

Linux Windows macOS CodeQL


Velk

Velk is a C++17 component object model library with interface-based polymorphism, typed properties with change notifications, events, compile-time metadata with runtime introspection, and a plugin system for modular extension via shared libraries.

Velk includes dense, cache-friendly object storage via hives, promise/future pairs with .then() chaining, and deferred execution for batching property writes and function calls.

Velk is designed to be built as a shared library (DLL on Windows, .so on Linux). All runtime implementations live inside the shared library, while consumers only depend on public headers containing abstract interfaces and header-only templates. This means the internal implementation can evolve without recompiling consumer code, multiple modules can share a single type registry and object factory, and ABI compatibility is maintained through stable virtual interfaces.

// Define an interface
class IWidget : public Interface<IWidget> {
public:
    VELK_INTERFACE(
        (PROP, float, width, 100.f),
        (EVT, on_clicked),
        (FN, void, reset)
    )
};

// Implement, register, create, use
class Widget : public ext::Object<Widget, IWidget> {
    void fn_reset() override { width().set_value(0.f); }
};

// Register type
auto& instance = velk::instance();
instance.type_registry().register_type<Widget>();

// Instantiate and use
auto w = instance.create<IWidget>(Widget::static_class_id());
w->width().set_value(42.f);
w->width().on_changed().add_handler([](){ /*...*/ });
w->on_clicked().add_handler([]() { /*...*/ });
velk::invoke_function(w->reset());

Note: Claude Code skill for Velk usage can be found in docs/claude-skill/velk. Copy the files under .claude/skills/velk to enable /velk command in Claude CLI.


Table of contents


Features

Feature Description
ABI stable Interface-based contracts with properties, events, and functions. Consumers depend only on public headers; internals can change without recompilation
Zero dependencies Pure C++17 with platform headers only
Compile-time metadata

Declare properties, events, and functions with VELK_INTERFACE, introspect at compile time or runtime

Typed properties with change notifications, property bindings, multicast events, virtual functions with typed parameters and return values, promise/future chaining, and deferred execution

Extensible

Plugin registry for inline or DLL-based plugins with declarative dependencies and multi-plugin bundles

Type registry for UID-based creation and runtime introspection

Attachments for injecting capabilities (decorators, custom behaviors) into objects at runtime

External hierarchy that manages parent/child trees without per-object overhead

Hive storage Dense, cache-friendly containers with slot reuse, zombie lifecycle, and automatic page management
Performance-focused

Inline state structs, lazy instantiation, single-indirect-call dispatch, and direct state access with zero overhead

No RTTI or exceptions, builds with /GR- /EHs-c- (MSVC) or -fno-rtti -fno-exceptions (GCC/Clang)

Scriptable Bindings, functions, event handlers optionally in JavaScript.
C API Flat C API (velk_c.dll) with opaque typed handles for FFI consumption from any language

Documentation

Document Description
Quick start Define an interface, implement it, create objects, and use typed accessors
Guide How to declare interfaces, work with properties, functions, events, and futures
Architecture How the four layers fit together, type hierarchy, and ABI stability
Hive Storing objects in dense, cache-friendly containers
Plugins Extending Velk with inline or DLL-based plugins
Performance Benchmark numbers, memory layout, and object sizes
Resources URI-based resource access with pluggable protocol handlers
Advanced Any types and value chains, writing metadata by hand, shared_ptr internals
C API Flat C API for FFI: handles, ref counting, property access, events
Built-in plugins
Importer JSON scene import with extensible collections
Animator Implicit animations and transitions for properties
Scripting JavaScript scripting via QuickJS-ng: expression bindings and event handlers
Tracy Tracy profiler integration via the perf sink interface

Project structure

velk/
  CMakeLists.txt
  README.md               This file
  benchmark/              Benchmarks (Google Benchmark)
  demo/                   Feature demonstration
  docs/                   Documentation
    architecture.md       Layers, headers, type hierarchy, key types
    guide.md              Extended usage guide
    hive.md               Dense object storage and hive registry
    performance.md        Performance and memory usage
    plugins.md            Plugin system: writing, loading, dependencies, bundles
    resources.md          URI-based resource access with pluggable protocols
    advanced.md           Any types, value chains, manual metadata, shared_ptr internals
    plugins/
      importer.md         Importer plugin: JSON import, extensions, format reference
      animator.md         Animator plugin: transitions, easing, interpolators
      scripting.md        Scripting plugin: JavaScript expression bindings, event handlers
      tracy.md            Tracy plugin: profiler integration via the perf sink
  velk/
    include/              Public API (consumers depend only on these headers)
      interface/          Abstract interfaces (ABI contracts)
      ext/                CRTP helpers and template implementations
      api/                User-facing typed wrappers
    src/                  DLL internals (compiled into velk.dll)
    c_api/                Flat C API (compiled into velk_c.dll)
    plugins/              Built-in plugins (compiled into separate dlls)
  test/                   Unit tests (GoogleTest)

Building

Requires CMake 3.14+ and a C++17 compiler with a C compiler (for QuickJS). Tested with MSVC 2019.

cmake -B build
cmake --build build --config Release

Output:

  • build/bin/Release/velk.dll (shared library)
  • build/bin/Release/velk_c.dll (C API shared library)
  • build/bin/Release/velk_importer.dll (shared library)
  • build/bin/Release/velk_animator.dll (shared library)
  • build/bin/Release/velk_js.dll (shared library)
  • build/bin/Release/demo.exe (demo)
  • build/bin/Release/tests.exe (unit tests)
  • build/bin/Release/benchmarks.exe (benchmarks)

Testing

Unit tests use GoogleTest 1.14.0 (vendored in test/third_party/). Benchmarks use Google Benchmark 1.9.1 (vendored in benchmark/third_party/).

Both are extracted into the build directory automatically during CMake configuration.

Quick start

Define an interface

Use VELK_INTERFACE to declare properties, events, and functions. This generates both a static constexpr metadata array and typed accessor methods.

#include <interface/intf_metadata.h>

class IMyWidget : public Interface<IMyWidget>
{
public:
    // Static interface metadata:
    VELK_INTERFACE(
        (PROP, float, width, 100.f),                // Read-write float property, default value 100.f
        (PROP, float, height, 100.f),
        (RPROP, int, id, 0),                        // Read-only int property, default value 0
        (ARR, uint32_t, layers, 1u, 2u, 3u),        // Array uint32_t property (array of values), default value {1u, 2u, 3u}
        (EVT, on_clicked),                          // Event
        (FN, void, reset),                          // Function without arguments
        (FN, void, resize, (float, w), (float, h))  // Function with 2 arguments
    )

    // Regular pure virtual function, part of the interface but not metadata.
    virtual void paint() = 0; 
};

Implement with Object

Object automatically collects metadata from all listed interfaces and provides the IMetadata implementation. Override fn_<Name> to provide function logic.

#include <velk/ext/object.h>

class MyWidget : public ext::Object<MyWidget, IMyWidget>
{
    // Implementations of the IMyWidget static metadata-defined functions "reset" and "resize"
    void fn_reset() override {
    }
    void fn_resize(float w, float h) override {
        width().set_value(w);   // width property as defined in VELK_INTERFACE
        height().set_value(h);
    }

    // Implementation of the pure virtual non-metadata function defined in IMyWidget
    void paint() override {}
};

Multiple interfaces are supported:

class ISerializable : public Interface<ISerializable>
{
public:
    VELK_INTERFACE(
        (PROP, velk::string, name, ""),
        (FN, void, serialize)
    )
};

class MyWidget : public ext::Object<MyWidget, IMyWidget, ISerializable>
{
    // Implementations for the functions defined in IMyWidget and ISerializable static metadata
    void fn_reset() override { /* ... */ }                  // zero-arg
    void fn_resize(float w, float h) override { /* ... */ } // typed args
    void fn_serialize() override { /* ... */ }              // from ISerializable

    // Non-metadata paint() from IMyWidget
    void paint() override {}
};

Register and create

auto& s = instance();                                           // global IVelk
s.type_registry().register_type<MyWidget>();                    // register factory

auto widget = s.create<IObject>(MyWidget::static_class_id());     // create by UID

Use typed accessors

auto widget = s.create<IMyWidget>(MyWidget::static_class_id());
if (widget) {
    auto wp = iw->width();
    wp.set_value(42.f);                                         // set property
    float w = wp.get_value();                                   // read property

    Event clicked = iw->on_clicked();                           // get event handle
    clicked.add_handler([]() { std::cout << "clicked"; });      // add event handler
    Function reset = iw->reset().invoke();                      // get function handle
}

Query metadata without instance

Static metadata is available from Velk without creating an instance:

if (auto* info = instance().type_registry().get_class_info(MyWidget::static_class_id())) {  // lookup by UID
    for (auto& i : info->interfaces) {                             // enumerate interfaces
        // i.uid, i.name
    }
    for (auto& m : info->members) {                                // enumerate members
        // m.name, m.kind, m.interfaceInfo
    }
}

Query metadata from object

Runtime metadata is available through IMetadata on any instance:

auto widget = s.create(MyWidget::static_class_id());
if (auto* meta = interface_cast<IMetadata>(widget)) {           // query interface for runtime introspection
    auto prop  = meta->get_property("width");                   // lookup by name
    auto event = meta->get_event("on_clicked");                 // lookup by name
    auto func  = meta->get_function("reset");                   // lookup by name
}

Direct state access

Properties are backed by inline State structs generated by VELK_INTERFACE. Use read_state and write_state for direct struct access with automatic change notifications. Both return a handle that converts to false if the interface is not implemented.

auto widget = s.create<IObject>(MyWidget::static_class_id());
auto* iw = interface_cast<IMyWidget>(widget);

// Read state directly
if (auto reader = read_state<IMyWidget>(iw)) {
    float w = reader->width;                                    // 100.f (default)
}

// Write state, fires on_changed for instantiated properties when writer goes out of scope
if (auto writer = write_state<IMyWidget>(iw)) {
    // struct IMyWidget::State {                                 // generated by VELK_INTERFACE
    //   float width { 100.f };
    //   float height { 100.f };
    //   int id { 0 };
    //   velk::vector<uint32_t> layers { 1u, 2u, 3u };
    // }
    writer->width = 200.f;
    writer->height = 50.f;
    writer->layers.push_back(4u);
}                                                               // ~StateWriter notifies listeners

iw->width().get_value();                                        // 200.f

About

Velk is a C++17 component object model library with interface-based polymorphism, typed properties with change notifications, events, compile-time metadata with runtime introspection, and a plugin system for modular extension via shared libraries.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Contributors