# Quickstart

> SmashTable for C++

```cmake
include(FetchContent)
FetchContent_Declare(
    smashtable
    GIT_REPOSITORY https://github.com/ashvardanian/SmashTable
    GIT_TAG main
)
FetchContent_MakeAvailable(smashtable)
target_link_libraries(your_target PRIVATE smashtable::header)
```

For a system-wide install, standard CMake package discovery works:

```bash
sudo cmake --install build_release
```

```cmake
find_package(smashtable REQUIRED)
target_link_libraries(your_target PRIVATE smashtable::header)
```

The library throws nowhere, and will not let you make it.
Every API returns `status_t`, reads included, and a read delivers its answer through `noexcept` callbacks rather than through the return.
A read is fallible from `serializable_k` up, where it has to write down what it read before the commit can validate it.
A key whose copy or comparison can throw fails to compile rather than being quietly accepted.
All containers take custom allocators and can be pre-allocated.

The same shape as the Python example, with no threads in sight:

```cpp
#include <smashtable/basic_avl_tree.hpp>
#include <smashtable/monotonic_store.hpp>

namespace st = ashvardanian::smashtable;

using id_t = std::uint64_t;
using by_id_t = st::monotonic_avl_map<id_t, record_t>;      // id   → record
using by_name_t = st::monotonic_avl_map<name_t, id_t>;      // name → id

auto by_id = *by_id_t::make();
auto by_name = *by_name_t::make();

auto group = *st::make_transaction_group(by_id, by_name);
auto [ids, names] = group.participants();

_ = ids.upsert({id, record});
_ = names.upsert({record.name, id});
_ = ids.watch(id);                  // fail rather than clobber a concurrent write

_ = group.stage();                  // both reserve, or neither does
_ = group.commit();                 // both become visible
```

The body, the stage and the commit always run in that order, so `commit_with` takes the first and runs the other two, resetting every participant if anything refuses:

```cpp
st::status_t const landed = group.commit_with([&](auto &ids, auto &names) noexcept {
    st::status_t const filed = ids.upsert({id, record});
    return failed(filed) ? filed : names.upsert({record.name, id});
});
```

Staging the two by hand would leave the first one staged when the second refuses, which is the bug a group exists to remove.
Participants are visited in order of the store's address rather than of the argument list, so two groups naming the same stores in opposite orders cannot deadlock on each other.
A refused `stage()` rolls the staged prefix back rather than resetting it, so the writes survive and the group can be retried.

Basic containers — `basic_avl_tree`, `basic_wb_tree` — carry STL-style bidirectional iterators.
Transactional containers do not, because keeping an iterator valid across concurrent updates costs more than it returns:

- `find()`, `lower_bound()` and `upper_bound()` return `status_t` and take two `noexcept` callbacks, for the found and missing cases.
- `equal_range()` and `sample_one()` return `status_t` and take one callback, invoked per element in the answer.
- `range(from, until, callback)` walks `[from, until)` smallest first, and `range(from, until, callback, st::descending)` walks `(until, from]` greatest first, taking `st::unbounded` for either end.
- `sample_reservoir()` also returns `status_t`, but scatters into a random-access output iterator rather than a callback, taking the bounds, a generator, a running `seen` count and the reservoir capacity.
- `find_copy()`, `lower_bound_copy()` and `upper_bound_copy()` return an `expected<value_t>` for callers that cannot use a callback.

`#include <smashtable/shared.hpp>`

`#include <smashtable/row_search.hpp>`

## status_t

Whether an operation succeeded, and why it did not.

```cpp
enum status_t
```

The library's only result code.

Values follow `errno` where one fits, so a failure can be handed to `strerror` or mapped onto a platform error without a translation table. [`success_k`](#success_k) is zero, which is why the type is scoped and truth-testing goes through `succeeded:` an implicit conversion would read every success as false. Prefer checking an [`expected`](#expected), which already answers this question.

The three conflicts have no `errno` between them: `EBUSY` and `EAGAIN`, the closest, are about a resource being held, while these are about what another transaction published. A retry loop reads them to decide whether retrying can help, which is what [`conflicted`](https://www.unum.cloud/docs/smashtable/cpp/transactions.md#conflicted) answers.

Statuses fall into five categories. Resources: [`out_of_memory_heap_k`](#out_of_memory_heap_k) and [`input_output_k`](#input_output_k). Input: [`invalid_argument_k`](#invalid_argument_k). State: the three conflicts, [`operation_not_permitted_k`](#operation_not_permitted_k), [`operation_would_block_k`](#operation_would_block_k), [`capacity_exhausted_k`](#capacity_exhausted_k), [`key_already_exists_k`](#key_already_exists_k) and [`key_not_found_k`](#key_not_found_k). Interruptions: none, as no verb is cancelled or deferred. Defects: [`consistency_k`](#consistency_k) and [`unknown_k`](#unknown_k). Any verb that allocates, binds a device or touches a file may also return a Resources status. A verb's own block lists only what its contract adds.

- `success_k` = `0`: The operation did what it was asked, and any result it carries is live.
- `unknown_k` = `-1`: A failure the library cannot name more precisely, which no caller should branch on.
- `consistency_k` = `-2`: An invariant of the container itself did not hold.
- `write_conflict_k` = `-3`: A key this transaction wrote was published over since its snapshot.
- `read_conflict_k` = `-4`: A key this transaction read was published over since its snapshot.
- `phantom_conflict_k` = `-5`: A window this transaction read gained or lost a member since its snapshot.
- `out_of_memory_heap_k` = `ENOMEM`: The heap refused the allocation the operation needed.
- `invalid_argument_k` = `EINVAL`: An argument was outside what the call accepts, decided before anything was written.
- `operation_not_permitted_k` = `EPERM`: The object is not in a state that admits the call, such as committing what was never staged.
- `operation_would_block_k` = `EWOULDBLOCK`: A bounded retry gave up rather than block.
- `capacity_exhausted_k` = `ENOSPC`: A probe sequence with no slot left, which a rehash mends rather than more memory.
- `input_output_k` = `EIO`: A device or file refused a read or write, which no retry in memory mends.
- `key_already_exists_k` = `EEXIST`: A strict [`insert`](https://www.unum.cloud/docs/smashtable/cpp/isolation.md#insert) found the key already taken.
- `key_not_found_k` = `ENOENT`: An [`update`](https://www.unum.cloud/docs/smashtable/cpp/isolation.md#update) or erase found no such key.

## status_t

Whether an operation succeeded, and why it did not.

```cpp
enum status_t
```

The library's only result code.

Values follow `errno` where one fits, so a failure can be handed to `strerror` or mapped onto a platform error without a translation table. [`success_k`](#success_k) is zero, which is why the type is scoped and truth-testing goes through `succeeded:` an implicit conversion would read every success as false. Prefer checking an [`expected`](#expected), which already answers this question.

The three conflicts have no `errno` between them: `EBUSY` and `EAGAIN`, the closest, are about a resource being held, while these are about what another transaction published. A retry loop reads them to decide whether retrying can help, which is what [`conflicted`](https://www.unum.cloud/docs/smashtable/cpp/transactions.md#conflicted) answers.

Statuses fall into five categories. Resources: [`out_of_memory_heap_k`](#out_of_memory_heap_k) and [`input_output_k`](#input_output_k). Input: [`invalid_argument_k`](#invalid_argument_k). State: the three conflicts, [`operation_not_permitted_k`](#operation_not_permitted_k), [`operation_would_block_k`](#operation_would_block_k), [`capacity_exhausted_k`](#capacity_exhausted_k), [`key_already_exists_k`](#key_already_exists_k) and [`key_not_found_k`](#key_not_found_k). Interruptions: none, as no verb is cancelled or deferred. Defects: [`consistency_k`](#consistency_k) and [`unknown_k`](#unknown_k). Any verb that allocates, binds a device or touches a file may also return a Resources status. A verb's own block lists only what its contract adds.

## expected

A value that may not be there, and the reason when it is not - the library's only result.

```cpp
template <typename value_type_>
class expected
```

Storage is a `union`, so the value exists only on success and `value_type_` needs no default constructor. That is what lets one type cover both what `std::optional` carried and what a status carried, including the move-only transaction types that have no empty state to sit in. A default-constructed [`expected`](#expected) reports __failure__, since there is nothing in it.

Decomposes as `auto [value, [status](#status)]`, for value types that can be default-constructed to hand something back on the failure path; see [`get`](#get).

## expected

A reason with nothing behind it; a success has no value to report, so it reads as [`unknown_k`](#unknown_k).

```cpp
constexpr expected() noexcept;
```

```cpp
constexpr expected(status_t status) noexcept;
```

A reason with nothing behind it; a success has no value to report, so it reads as [`unknown_k`](#unknown_k).

```cpp
expected(value_t && value, status_t status = success_k) noexcept;
```

A value, kept only while [`status`](#status) says there is one.

```cpp
requires (std::is_nothrow_copy_constructible_v<value_t>)
expected(value_t const & value, status_t status = success_k) noexcept;
```

```cpp
expected(expected && other) noexcept;
```

```cpp
expected(expected const &)=delete;
```

A failure carries its reason and nothing else, so the argument is left to its owner.

## operator=

```cpp
expected & operator=(expected && other) noexcept;
```

```cpp
expected & operator=(expected const &)=delete;
```

## ~expected

```cpp
~expected() noexcept;
```

## operator bool

Whether the value is there, which is the only safe precondition for reading it.

```cpp
constexpr operator bool() const noexcept;
```

## failed

Whether there is no value, which is the protocol every result in the library answers.

```cpp
bool failed() const noexcept;
```

## status

Why there is no value, or success when there is one.

```cpp
status_t status() const noexcept;
```

## get

Tuple protocol behind `auto [value, [status](#status)]`, returning the value __by__ value.

```cpp
template <std::size_t index_>
requires (std::is_nothrow_default_constructible_v<value_t>)
auto get() &&noexcept;
```

```cpp
template <std::size_t index_>
requires (std::is_nothrow_default_constructible_v<value_t> && std::is_nothrow_copy_constructible_v<value_t>)
auto get() const &noexcept;
```

A reference would bind into storage that was never constructed when the status is a failure, which reads as garbage and trips no sanitizer, so the failure path default-constructs instead. Offered only where that is possible: a transaction cannot be decomposed, and has nothing worth binding when it fails anyway, so callers check it and dereference.

## succeeded

Whether [`status`](#status) reports success, which no implicit conversion can be trusted to say.

```cpp
bool succeeded(status_t status) noexcept;
```

```cpp
bool succeeded(bool is_satisfied) noexcept;
```

A plain condition is already its own verdict, so generic code can ask either shape.

```cpp
template <typename result_type_>
requires requires(result_type_ const &result) { result.failed(); }
bool succeeded(result_type_ const & result) noexcept;
```

The same question of a richer result - one that reports its own success, like the node handles the trees hand back - so generic code can ask it without knowing which it holds.

Without this, a caller holding a `bool` has to spell the question differently from one holding a `[status_t](#status_t)`, and anything asking it of both - a check, a gate, a fold - needs a branch on which it was handed.

## failed

Whether [`status`](#status) reports a failure, and so carries a reason.

```cpp
bool failed(status_t status) noexcept;
```

```cpp
bool failed(bool is_satisfied) noexcept;
```

The same of a plain condition, so [`succeeded`](#succeeded) and [`failed`](#failed) stay a pair for every shape.

## first_failure

Keeps the first refusal of a walk that reports more than once, so the reason travels.

```cpp
status_t first_failure(status_t recorded, status_t next) noexcept;
```

The discipline a __read__ walk wants: the first thing that went wrong is what the caller asked about, and nothing after it can be trusted anyway.

## last_failure

Keeps the last refusal of a walk that reports more than once.

```cpp
status_t last_failure(status_t recorded, status_t next) noexcept;
```

The discipline a __write__ walk wants, where stopping at the first refusal would leave the steps after it untouched: the walk runs to the end regardless, so the reason it carries out is the last one it met.

## name_of

The enumerator's own spelling, for a message a person reads rather than decodes.

```cpp
char const * name_of(status_t status) noexcept;
```

## name_of

The enumerator's own name without its suffix, for a benchmark or a log.

```cpp
char const * name_of(row_kit_t kit) noexcept;
```

