Vix.cpp v2.8.5 is here Read the blog
Skip to content

Values ​

This guide shows how to write, read, and delete values with Vix KV.

Use this page when you want to store strings, read them back, and understand the difference between the simple API and the Result API.

Public header ​

cpp
#include <vix/kv/kv.hpp>
#include <vix/print.hpp>

Basic value storage ​

The simplest way to store a value is put.

cpp
#include <vix/kv/kv.hpp>
#include <vix/print.hpp>

int main()
{
  auto kv = vix::kv::open("data/kv");

  kv.put("hello", "world");

  const auto value = kv.get("hello");

  if (value.has_value())
  {
    vix::print("hello =", *value);
  }

  (void)kv.close();

  return 0;
}

Run:

vix run main.cpp

Expected output:

hello = world

Simple API ​

The simple API uses string keys and optional values.

cpp
auto kv = vix::kv::open("data/kv");

kv.put("hello", "world");

const auto value = kv.get("hello");

if (value.has_value())
{
  vix::print(*value);
}

(void)kv.close();

Use this style when you want a compact local key-value API.

Missing values ​

When a key does not exist, get returns an empty optional.

cpp
const auto value = kv.get("missing");

if (!value.has_value())
{
  vix::print("missing");
}

This is not an error. It only means the key is not present.

Result API ​

The Result API uses structured keys and explicit error handling.

cpp
#include <vix/kv/kv.hpp>
#include <vix/print.hpp>

int main()
{
  auto opened = vix::kv::open_memory();

  if (opened.is_err())
  {
    vix::eprint(opened.error().message());
    return 1;
  }

  auto kv = opened.move_value();

  auto written = kv.set({"users", "1", "email"}, "ada@example.com");

  if (written.is_err())
  {
    vix::eprint(written.error().message());
    return 1;
  }

  auto value = kv.get({"users", "1", "email"});

  if (value.is_err())
  {
    vix::eprint(value.error().message());
    return 1;
  }

  vix::print("users/1/email =", value.value().to_string());

  (void)kv.close();

  return 0;
}

Expected output:

users/1/email = ada@example.com

KvValue ​

The Result API returns KvValue.

Use to_string() when you want a string representation.

cpp
auto value = kv.get({"users", "1", "name"});

if (value.is_ok())
{
  vix::print("name =", value.value().to_string());
}

Write values with set ​

Use set with structured keys.

cpp
auto written = kv.set({"settings", "theme"}, "dark");

if (written.is_err())
{
  vix::eprint(written.error().message());
  return 1;
}

Structured keys are useful for grouped data.

cpp
kv.set({"users", "1", "name"}, "Ada");
kv.set({"users", "1", "email"}, "ada@example.com");
kv.set({"users", "2", "name"}, "Grace");

Check if a value exists ​

Use contains.

cpp
if (kv.contains({"users", "1", "email"}))
{
  vix::print("email exists");
}

Example:

cpp
#include <vix/kv/kv.hpp>
#include <vix/print.hpp>

int main()
{
  auto opened = vix::kv::open_memory();

  if (opened.is_err())
  {
    vix::eprint(opened.error().message());
    return 1;
  }

  auto kv = opened.move_value();

  (void)kv.set({"session", "token"}, "abc123");

  vix::print(
      "contains =",
      kv.contains({"session", "token"}) ? "yes" : "no");

  (void)kv.close();

  return 0;
}

Expected output:

contains = yes

Delete values ​

Use erase to delete a value.

cpp
auto erased = kv.erase(vix::kv::KeyPath{"session", "token"});

if (erased.is_err())
{
  vix::eprint(erased.error().message());
  return 1;
}

Complete example:

cpp
#include <vix/kv/kv.hpp>
#include <vix/print.hpp>

int main()
{
  auto opened = vix::kv::open_memory();

  if (opened.is_err())
  {
    vix::eprint(opened.error().message());
    return 1;
  }

  auto kv = opened.move_value();

  (void)kv.set({"session", "token"}, "abc123");

  vix::print(
      "before erase =",
      kv.contains({"session", "token"}) ? "exists" : "missing");

  auto erased = kv.erase(vix::kv::KeyPath{"session", "token"});

  if (erased.is_err())
  {
    vix::eprint(erased.error().message());
    return 1;
  }

  vix::print(
      "after erase =",
      kv.contains({"session", "token"}) ? "exists" : "missing");

  (void)kv.close();

  return 0;
}

Expected output:

before erase = exists
after erase = missing

List values ​

Use list to read live values.

cpp
auto entries = kv.list({"users"});

if (entries.is_err())
{
  vix::eprint(entries.error().message());
  return 1;
}

Example:

cpp
#include <vix/kv/kv.hpp>
#include <vix/print.hpp>

static void print_key(const vix::kv::KeyPath &key)
{
  for (std::size_t i = 0; i < key.size(); ++i)
  {
    if (i > 0)
    {
      vix::print_inline("/");
    }

    vix::print_inline(key.at(i));
  }

  vix::print();
}

int main()
{
  auto opened = vix::kv::open_memory();

  if (opened.is_err())
  {
    vix::eprint(opened.error().message());
    return 1;
  }

  auto kv = opened.move_value();

  (void)kv.set({"users", "1", "name"}, "Ada");
  (void)kv.set({"users", "2", "name"}, "Grace");
  (void)kv.set({"settings", "theme"}, "dark");

  auto users = kv.list({"users"});

  if (users.is_err())
  {
    vix::eprint(users.error().message());
    return 1;
  }

  for (const auto &[key, value] : users.value())
  {
    vix::print_inline("key = ");
    print_key(key);

    vix::print("value =", value.to_string());
  }

  (void)kv.close();

  return 0;
}

List all live values ​

Call list() without a prefix to read all live entries.

cpp
auto all = kv.list();

if (all.is_err())
{
  vix::eprint(all.error().message());
  return 1;
}

for (const auto &[key, value] : all.value())
{
  vix::print(value.to_string());
}

Deleted keys are not returned as live values.

Value workflow ​

A normal value workflow looks like this:

cpp
auto opened = vix::kv::open_memory();

if (opened.is_err())
{
  vix::eprint(opened.error().message());
  return 1;
}

auto kv = opened.move_value();

auto written = kv.set({"users", "1", "name"}, "Ada");

if (written.is_err())
{
  vix::eprint(written.error().message());
  return 1;
}

auto value = kv.get({"users", "1", "name"});

if (value.is_ok())
{
  vix::print(value.value().to_string());
}

(void)kv.close();

Simple API vs Result API ​

OperationSimple APIResult API
Writekv.put("hello", "world")kv.set({"hello"}, "world")
Readkv.get("hello")kv.get({"hello"})
Missing valueempty std::optionalResult value/error model
Structured keysstring pathKeyPath
Error handlingcompactexplicit

Common workflows ​

Store and read a string ​

cpp
kv.put("hello", "world");

const auto value = kv.get("hello");

if (value.has_value())
{
  vix::print(*value);
}

Store and read a structured value ​

cpp
auto written = kv.set({"users", "1", "name"}, "Ada");

if (written.is_err())
{
  vix::eprint(written.error().message());
  return 1;
}

auto value = kv.get({"users", "1", "name"});

if (value.is_ok())
{
  vix::print(value.value().to_string());
}

Check existence ​

cpp
vix::print(
    "exists =",
    kv.contains({"users", "1", "name"}) ? "yes" : "no");

Delete a value ​

cpp
auto erased = kv.erase(vix::kv::KeyPath{"users", "1", "name"});

if (erased.is_err())
{
  vix::eprint(erased.error().message());
  return 1;
}

List a prefix ​

cpp
auto users = kv.list({"users"});

if (users.is_ok())
{
  vix::print("count =", users.value().size());
}

Common mistakes ​

Treating a missing simple value as an error ​

A missing value from the simple API is represented by an empty optional.

cpp
const auto value = kv.get("missing");

if (!value.has_value())
{
  vix::print("not found");
}

Forgetting to check Result errors ​

Wrong:

cpp
auto value = kv.get({"users", "1", "name"});
vix::print(value.value().to_string());

Correct:

cpp
auto value = kv.get({"users", "1", "name"});

if (value.is_err())
{
  vix::eprint(value.error().message());
  return 1;
}

vix::print(value.value().to_string());

Forgetting to_string() ​

Wrong:

cpp
vix::print(value.value());

Correct:

cpp
vix::print(value.value().to_string());

Deleting with the wrong key shape ​

If you wrote with:

cpp
kv.set({"session", "token"}, "abc123");

Delete with the same key path:

cpp
kv.erase(vix::kv::KeyPath{"session", "token"});

Expecting deleted values to appear in list ​

Deleted keys are tombstoned internally and are not returned as live entries by list.

Use stats() if you want to inspect tombstones.

cpp
const auto stats = kv.stats();

vix::print("tombstones =", stats.tombstone_count);
PagePurpose
Opening a databaseLearn how to open memory and durable stores.
KeysLearn how to structure keys.
PersistenceLearn how values survive restart.
RecoveryLearn how deletes and WAL recovery work.
StatsLearn how to inspect live keys and tombstones.

Next step ​

Continue with keys.

Released under the MIT License.