
Description
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
Class stlab::copy_on_write<T>
A copy-on-write wrapper for any type that models Regular.
Copy-on-write semantics allow for an object to be lazily copied - only creating a copy when the value is modified and there is more than one reference to the value.
This class is thread safe and supports types that model Moveable.
Member Types
Member Types
Member Functions
Member Functions
Observers
Observers
Non-Member Functions
Non-Member Functions
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 <iterator>
#include <string>
#include <vector>
using namespace std;
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;
new_lines.reserve((lines.size() + 1) * 2);
new_lines.insert(new_lines.end(), lines.begin(), lines.begin() + index);
new_lines.insert(new_lines.end(), std::move(line));
new_lines.insert(new_lines.end(), lines.begin() + index, lines.end());
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;
new_lines.reserve((lines.size() - 1) * 2);
new_lines.insert(new_lines.end(), lines.begin(), lines.begin() + index);
new_lines.insert(new_lines.end(), lines.begin() + index + 1, lines.end());
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;
}
Definition copy_on_write.hpp:124
Copy-on-write wrapper implementation.
auto identity(const copy_on_write &x) const noexcept -> bool
Returns true if this object and the given object share the same underlying data.
Definition copy_on_write.hpp:342
auto read() const noexcept -> const element_type &
Returns a const reference to the underlying value for read-only access.
Definition copy_on_write.hpp:301
auto write() -> element_type &
Obtains a non-const reference to the underlying value.
Definition copy_on_write.hpp:263
Definition copy_on_write.hpp:111
License
Distributed under the Boost Software License, Version 1.0.