
Introduction
The stlab::copy_on_write<T> class provides a copy-on-write wrapper for any type that models Regular. This implementation allows multiple instances to share the same underlying data until one of them needs to modify it, at which point a copy is made. The class is not intended to be exposed in a class interface, but rather used as a utility to construct a type where some or all of the members are copy_on_write.
The stlab::copy_on_write<T>::write() operation optionally takes a transform and in-place transform function. For many mutable operations, there is a transform operation that can be applied to perform the copy with the mutation in a more efficient manner.
Copy-on-write is most useful for types between 4K and 1M in size and expected to be copied frequently, such as part of a transaction system, i.e., implementing undo/redo. Larger objects can be decomposed into smaller copy-on-write objects or use a data structure such as a rope that has an internal copy-on-write structure. Smaller objects can be more efficient by avoiding heap allocations (such as with small object optimization) and always copying; however, if they always heap allocate, they may be more efficient by using copy-on-write.
Key Features
- Thread-safe: Uses atomic reference counting for safe concurrent access
- Header-only: No compilation required, just include the header
- C++17: Leverages modern C++ features for clean, efficient implementation
Basic Usage
This example demonstrates the core functionality including efficient copying, copy-on-write semantics, identity checking, and swap operations.
#include <cassert>
#include <cstddef>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
using namespace stlab;
namespace {
class document {
public:
document() = default;
document(const document& other) noexcept = default;
document& operator=(const document& other) noexcept = default;
document(document&& other) noexcept = default;
document& operator=(document&& other) noexcept = default;
using iterator = std::vector<std::string>::const_iterator;
iterator begin()
const {
return _lines.
read().begin(); }
iterator end()
const {
return _lines.
read().end(); }
size_t size()
const {
return _lines.
read().size(); }
bool empty()
const {
return _lines.
read().empty(); }
bool identity(
const document& other)
const {
return _lines.
identity(other._lines); }
void insert(std::string&& line, size_t index) {
assert(index <= size() && "index out of bounds");
[&](const std::vector<std::string>& lines) {
std::vector<std::string> new_lines = lines;
new_lines.insert(new_lines.begin() + index, std::move(line));
return new_lines;
},
[&](std::vector<std::string>& lines) {
lines.insert(lines.begin() + index, std::move(line));
});
}
void erase(size_t index) {
assert(index <= size() && "index out of bounds");
[&](const std::vector<std::string>& lines) {
std::vector<std::string> new_lines = lines;
if (index < lines.size()) {
new_lines.erase(new_lines.begin() + index);
}
return new_lines;
},
[&](std::vector<std::string>& lines) {
lines.erase(lines.begin() + index);
});
}
};
}
int main() {
cerr << "--- Test starting ---" << endl;
document d0;
d0.insert("Hello, world!", 0);
d0.insert("After Hello", 1);
document d1(d0);
assert(d0.identity(d1));
cerr << "main: calling d1.insert..." << endl;
d1.insert("Start of d1", 0);
cerr << "main: d1.insert returned." << endl;
assert(!d0.identity(d1));
cout << "d0:" << endl;
for (const auto& line : d0) {
cout << line << endl;
}
cout << "d1:" << endl;
for (const auto& line : d1) {
cout << line << endl;
}
cerr << "--- Test finished ---" << endl;
return 0;
}
auto identity(const copy_on_write &x) const noexcept -> bool
auto read() const noexcept -> const element_type &
auto write() -> element_type &
Copy-on-write wrapper implementation.
License
Distributed under the Boost Software License, Version 1.0.