Copy-on-Write TypesΒΆ
Copy-on-write (COW) is an optimization technique that makes copies cheap by implicitly sharing data between the original and the copy until one of them is modified. This makes it convenient and cheap to pass values around while also making sure that the data is only copied when necessary.
CAF uses copy-on-write for its message
type, which basically is a
type-erased tuple. CAF also includes cow_tuple
which wraps a std::tuple
and allows users to pass expensive data (like strings and lists) as a single
unit that can be passed around cheaply. This is particularly useful when
emitting tuples in a flow, where the tuples are frequently buffered and passed
around.
Here is a small example to illustrate the API:
auto addr = [](auto& tup) {
return reinterpret_cast<intptr_t>(std::addressof(tup.data()));
};
auto xs = caf::make_cow_tuple(1, 2, 3);
auto ys = xs;
sys.println("After initializing:");
sys.println("xs: {} ({:#x})", xs, addr(xs));
sys.println("ys: {} ({:#x})", ys, addr(ys));
ys.unshared() = std::tuple{4, 5, 6};
sys.println("After assigning to ys:");
sys.println("xs: {} ({:#x})", xs, addr(xs));
sys.println("ys: {} ({:#x})", ys, addr(ys));
An example output of this program is:
After initializing:
xs: [1, 2, 3] (0x10a001d30)
ys: [1, 2, 3] (0x10a001d30)
After assigning to ys:
xs: [1, 2, 3] (0x10a001d30)
ys: [4, 5, 6] (0x10a001d00)
By default, cow_tuple
only grants const
access to its elements. With
get<I>(xs)
, users can get access to a single value of xs
at the index
I
. With xs.data()
, users get a const
reference to the internally
stored std::tuple
.
In order to gain mutable access, users may call unshared()
to get a mutable
reference to the std::tuple
. This function makes a deep copy of the data if
there is more than one reference to the data at the moment. In our example
above, ys
initially points to the same data as xs
. After calling
unshared()
on ys
, however, the two tuples point to different data.
Especially when using the flow API, it is important to use values that are cheap to copy. For this reason, CAF includes a few convenience types that wrap their standard equivalent:
cow_string
: A copy-on-write string that wrapsstd::string
.cow_vector
: A copy-on-write vector that wrapsstd::vector
.cow_tuple
: A copy-on-write tuple that wrapsstd::tuple
.