GigabiteStudios e651ae1575
Some checks failed
C++ bindings / gcc / Debug (push) Failing after 10s
C++ bindings / clang / Debug (push) Failing after 17s
C++ bindings / clang / Release (push) Failing after 10s
C++ bindings / gcc / Release (push) Failing after 16s
feat(bindings): add modern C++ interface for iKv
2026-06-17 18:57:54 -05:00
2026-06-17 23:40:35 +00:00

iKvxx

iKvxx is a modern C++17 binding for iKv. It provides automatic resource management, exceptions, type-safe accessors, and a compact Value API modeled after JsonCpp while preserving iKv text and binary format compatibility.

What it does

  • Parse iKv text from strings or files
  • Parse iKv binary data from memory or files
  • Build object and array trees with a JsonCpp-style API
  • Read and write both iKv1 and iKv2
  • Write the current iKv2 format by default
  • Manage the underlying C tree automatically with RAII
  • Report parse, type, bounds, allocation, and I/O failures with exceptions
  • Provide a reusable ikvxx::ikvxx CMake target and installable package

Syntax preview

ikv2 "player_save"
{
    "title" "iKvxx demo"
    "version" 2
    "player" {
        "name" "jondoe"
        "alive" true
        "speed" 12.5
    }
    "inventory" [
        "wrench"
        "battery"
        "map"
    ]
}

Library example

#include <ikvxx/ikvxx.hpp>

int main()
{
    ikv::Value root(ikv::objectValue, "player_save");
    root["title"] = "iKvxx demo";
    root["version"] = 2;

    auto player = root.makeObject("player");
    player["name"] = "jondoe";
    player["alive"] = true;
    player["speed"] = 12.5;

    auto inventory = root.makeArray("inventory", ikv::stringValue);
    inventory.append("wrench");
    inventory.append("battery");
    inventory.append("map");

    root.write("player_save.ikv");
}

The root and all child handles release their underlying iKv tree automatically. No manual ikv_free call is required.

Reading values

Use load for text files and the familiar operator[] syntax for object and array access:

#include <ikvxx/ikvxx.hpp>
#include <iostream>

int main()
{
    auto root = ikv::Value::load("player_save.ikv");

    std::cout << root["title"].asString() << '\n';
    std::cout << root["version"].asInt() << '\n';
    std::cout << root["player"]["speed"].asDouble() << '\n';

    auto inventory = root["inventory"];
    for (ikv::Value::ArrayIndex i = 0; i < inventory.size(); ++i)
        std::cout << inventory[i].asString() << '\n';
}

Missing object members produce a null Value. Invalid conversions and out-of-range array indexes throw a standard exception:

if (!root.isMember("difficulty"))
    root["difficulty"] = "normal";

if (root["optional"].isNull())
    root["optional"] = false;

try
{
    int value = root["title"].asInt();
}
catch (const ikv::Error& error)
{
    // "title" is a string, not an integer.
}

Parsing text

Value::parse auto-detects iKv1 and iKv2 input:

const std::string text = R"(
ikv2 "settings"
{
    "fullscreen" true
    "width" 1920
    "height" 1080
}
)";

auto settings = ikv::Value::parse(text);
bool fullscreen = settings["fullscreen"].asBool();

Arrays

Arrays may be untyped or restricted to one iKv value type. Typed arrays reject values of the wrong type:

ikv::Value root;

auto scores = root.makeArray("scores", ikv::intValue);
scores.append(10);
scores.append(25);
scores.append(100);

auto mixed = root.makeArray("mixed");
mixed.append("ready");
mixed.append(42);
mixed.append(true);

auto players = root.makeArray("players", ikv::objectValue);
auto first = players.appendObject();
first["name"] = "Ada";
first["score"] = 100;

Binary data

Binary iKv can be written to a file or encoded directly into a byte vector:

ikv::Value root;
root["answer"] = 42;

root.writeBinary("data.ikvb");
auto from_file = ikv::Value::loadBinary("data.ikvb");

std::vector<std::uint8_t> bytes = root.toBinary();
auto from_memory = ikv::Value::fromBinary(bytes.data(), bytes.size());

Pass an explicit version when compatibility with older data is required:

root.write("legacy.ikv", ikv::Version::v1);
root.writeBinary("legacy.ikvb", ikv::Version::v1);
auto legacy_bytes = root.toBinary(ikv::Version::v1);

Refreshing a tree

An existing root can be updated in place from a text or binary file. Other Value handles that share that root observe the refreshed data:

auto settings = ikv::Value::load("settings.ikv");
auto shared = settings;

settings.refresh("updated-settings.ikv");
// shared now refers to the refreshed tree as well.

refresh is only valid on a mutable root value. If refresh fails, the existing tree is preserved.

Build

Clone with the iKv submodule and build with CMake:

git clone --recurse-submodules https://dock-it.dev/iDENTITY-Technology/iKvxx.git
cd iKvxx
cmake -S . -B build
cmake --build build

If the repository was cloned without submodules:

git submodule update --init --recursive

This produces the reusable static library targets:

  • ikvxx
  • ikvxx::ikvxx for CMake consumers

CMake consumer usage

When iKvxx is included directly in another source tree:

add_subdirectory(path/to/iKvxx)
target_link_libraries(your_target PRIVATE ikvxx::ikvxx)

The library can also be installed and consumed with find_package:

cmake --install build --prefix /path/to/ikvxx-install
find_package(ikvxx 0.1 REQUIRED CONFIG)
target_link_libraries(your_target PRIVATE ikvxx::ikvxx)

Point CMAKE_PREFIX_PATH at the selected installation prefix when it is not in a standard system location.

Tests

Build and run the binding tests with CTest:

cmake -S . -B build -DIKVXX_BUILD_TESTS=ON
cmake --build build
ctest --test-dir build --output-on-failure

The test suite covers construction, every value type, object and array access, assignment and append overloads, conversion failures, bounds checks, text and binary I/O, format versions, refresh behavior, and shared ownership. Gitea Actions runs the suite on Ubuntu with GCC and Clang in both Debug and Release configurations.

On Windows, the complete configure, build, and test sequence can be run with:

tests\run.bat
tests\run.bat Debug

Project layout

  • include/ikvxx/ikvxx.hpp: public C++ API
  • src/value.cpp: C++ binding implementation
  • tests/value_tests.cpp: unit and integration tests
  • third_party/iKv: pinned iKv Git submodule
  • cmake/ikvxxConfig.cmake.in: installed CMake package configuration
  • .gitea/workflows/bindings.yml: Ubuntu compiler and build matrix

Format behavior

  • Generic parse APIs auto-detect iKv1 and iKv2
  • Generic write APIs default to iKv2
  • Text and binary files remain compatible with the underlying iKv C library
  • Explicit ikv::Version::v1 and ikv::Version::v2 output is supported
  • iKv2 binary input retains iKv's indexed, lazy root lookup behavior

Public API

The public interface is declared in include/ikvxx/ikvxx.hpp. Key areas include:

  • ikv::Value construction and automatic lifetime management
  • JsonCpp-style object lookup with operator[]
  • Array indexing and append operations
  • Runtime type inspection with isString, isInt, isObject, and related methods
  • Typed scalar access with asString, asInt, asInt64, asDouble, and asBool
  • Text parsing, loading, writing, and in-place refresh
  • Binary memory and file I/O
  • Explicit format-version selection

API constraints

iKvxx intentionally exposes errors where the underlying iKv model cannot safely provide JsonCpp behavior:

  • Roots must be objects or arrays; scalar roots are not supported by iKv
  • Arrays of arrays are not supported by the current iKv C API
  • Existing array elements cannot be replaced in place
  • Copied Value handles share ownership of the same tree
  • Structural assignment between two Value objects is disabled because the public C API cannot clone arbitrary subtrees
  • A child handle must not be retained across replacement of that child or a root refresh; acquire the child again after modifying the structure

License

This repository ships with the license text in LICENSE:

  • Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)

The iKv submodule includes its own license file at third_party/iKv/LICENSE.

Description
C++ bindings for iKv files
Readme CC-BY-SA-4.0 71 KiB
Languages
C++ 89.9%
CMake 7%
Batchfile 3.1%