The request language¶
A Store is a request: its kind with its element types, then a list of property tags, in any order.
-
template<typename Kind, typename ...Tags>
class Store¶ A class template (
urd/store.hpp) deriving from the Store resolved from the request:Kindis one of the kinds below with its element types, andTagsare property tags from How the language is organized, at most one per category. Being a class template, it lets function templates deduce what it holds (template<typename K> void f(const Store<set<K>>&)), as the standard’s containers do. Equal requests, in whatever order, resolve to the same container, though each spelling is its own class.Store<...>::representationnames the container chosen, for tests that pin it; code that uses a Store never needs it.
-
template<typename K>
class set¶
-
template<typename K, typename V>
class map¶
-
template<typename V>
class multiset¶
-
template<typename V>
class sequence¶
-
template<typename V>
class queue¶
-
template<typename V>
class pool¶
-
template<typename V>
class slots¶
-
template<typename K>
class partition¶ The kinds (
urd/kinds.hpp), each with its element types: keysKidentify elements, valuesVare carried.Kmust behashable(the default),orderable(withsorted) orindexable(withdense); see Elements: identity, hashing, order, indices.Vhas no requirements of its own.
-
template<typename Compare>
class order_by¶ A tag that orders a
sortedStore or aby_priorityqueue byCompareinstead ofLess:Comparecompares two elements (in a map, two keys), asstd::set’s comparator does. The order isCompareand nothing else: two elements it does not separate are equivalent, so a unique Store keeps one of them.
-
template<auto P>
class by¶ A comparator by
P(element)underLess, fororder_by<by<&Job::deadline>>:Pis a member pointer or a stateless lambda. Name what tells elements apart inP(std::tuple(job.deadline, job.id)) to keep elements with equal deadlines distinct.
-
struct row_lengths¶
What a
fixed_shapeStore is built from: how long each row is, in order.
Nesting¶
A Store of Stores is written as one: an element, key or value can be another Store, to any depth.
using Tags = Store<set<std::string>, sorted>;
using Tagged = Store<map<Item, Tags>>; // a map of sorted sets
using Families = Store<set<Tags>>; // a set of sets (hashed by contents: Tags is sorted)
using Trips = Store<map<std::string, Store<sequence<Point>>>>; // a map of sequences
using Layers = Store<sequence<Store<map<Cell, Store<sequence<Item>>>>>>; // sequences of maps of sequences
Most rows are held as Stores, each owning its storage. Where the outer request says how rows live (frozen,
fixed_shape, retains, handles), urd stores them together and hands out a row as a view: see
Sequences and Pools.
Kinds¶
The kind of a Store is its first argument (Kind):
Request |
Kind |
Page |
|---|---|---|
|
a set |
|
|
a map (a multimap with |
|
|
a sequence |
|
|
a multiset: values kept by value |
|
|
a queue, by arrival or by priority |
|
|
a pool: rows interned as rows, records and values interned whole ( |
|
|
slots |
|
|
a partition of indices |
Rules¶
A request is checked when the Store type is formed (include/urd/spec.hpp); each failure is a static
assertion that names what is wrong.
The first argument is the kind with its element types (
set<K>,map<K, V>, …); every other is a tag.At most one tag per category:
denseandsparsecontradict, as dofrozenandmonotone, two workloads.What you state is what you get, with what it means: a set, and a map without
multi, keep each key once; a positional set numbers its keys as they come; afrozenStore hasstable_refs.frozenorfixed_shapeStores offer no insert,monotoneornonemptyones no erase or clear,monotone_until_clearones clear but no erase.A queue is reached by
fifo,lifoorby_priority;order_byorders asortedStore or aby_priorityqueue.Every tag answers a question of the request’s kind (its request format):
bidirectionalmeans nothing for a queue,flatnothing for a set.Any other request no container serves fails in the registry.
Resolution¶
The request’s kind is found first (Kind); then only that kind’s containers are tried
(declarations_of<Kind> in include/urd/registry/declarations.hpp), in an order backed by Benchmarks. The
first whose declaration provides every property of the request, whose reliances the request promises (what it
states, and the type traits of its elements, order, values and rows), and whose demands the request meets, is chosen;
where the request is frozen or small, containers with stable storage come after the others (the tags’ later, urd::tag_defaults
in include/urd/tags.hpp). A declared guarantee the request left open is free, with one exception: dense
is also a demand, since a container that stores by index needs keys that are indices, so a dense container serves
only a request that promises dense (a partition promises it). What a request resolves to is generated from the registry
and checked by a test, and a second test checks that no serving candidate demands what a request did not promise.
A request no container serves fails with a static assertion; its instantiation names the request:
In instantiation of 'no_representation_for<spec<urd::set<std::string>, urd::Less<std::string>, urd::uniqueness::unique, urd::keys::dense>>'
error: static assertion failed: urd::Store: no container serves this request: drop or change a tag
A call to an operation the properties rule out is a plain compile error: the Store has no such member.
Building and using¶
cmake -S python/native -B dependencies-build -DCMAKE_INSTALL_PREFIX="$PWD/dependencies-install" -DCMAKE_INSTALL_LIBDIR=lib
cmake --build dependencies-build && cmake --install dependencies-build # Boost, cista, gtl, GoogleTest, Google Benchmark
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j8 && ctest --test-dir build -j8
cmake --install build --prefix <where> # then find_package(urd) from there
Link urd::urd and include urd/store.hpp. The target carries Boost 1.89 or later (Container, Unordered,
Hash2), cista, and gtl, whose sharded tables serve concurrent sets and maps by hashed keys.
urd/store.hpp declares every representation and defines none: it costs the vocabulary and the resolution, and
stays as cheap as containers are added: a request names only the container it resolves to. A file includes the
header of each container its Stores resolve to, urd/repr/<kind>/<container>.hpp (Representations lists
them; the kinds’ directories are sequences, values, maps, queues, pools, slots, records and partitions, and wrappers
holds the wrappers),
and a Store whose container’s header is missing fails to compile naming the headers to include. No header includes
every container: the record pools alone bring cista’s serialization, which only a file holding such a pool should
pay for. The resolution table (docs/RESOLUTION.md) names each request’s headers. Beside these: urd/elements/flat.hpp where a record stored flat is written, and urd/elements/view.hpp where views are made.
This documentation is built with Sphinx:
python3 -m venv build/docs-venv && build/docs-venv/bin/pip install -r docs/requirements.txt
build/docs-venv/bin/sphinx-build -b html docs build/docs/html