7.9 KiB
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
iKv1andiKv2 - Write the current
iKv2format 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::ikvxxCMake 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:
ikvxxikvxx::ikvxxfor 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++ APIsrc/value.cpp: C++ binding implementationtests/value_tests.cpp: unit and integration teststhird_party/iKv: pinned iKv Git submodulecmake/ikvxxConfig.cmake.in: installed CMake package configuration.gitea/workflows/bindings.yml: Ubuntu compiler and build matrix
Format behavior
- Generic parse APIs auto-detect
iKv1andiKv2 - Generic write APIs default to
iKv2 - Text and binary files remain compatible with the underlying iKv C library
- Explicit
ikv::Version::v1andikv::Version::v2output is supported iKv2binary 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::Valueconstruction 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, andasBool - 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
Valuehandles share ownership of the same tree - Structural assignment between two
Valueobjects 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.