Writing¶
Glaze provides a unified return type for all write operations that gives you both error information and the byte count written.
The error_ctx Type¶
All write functions return glz::error_ctx:
struct error_ctx {
size_t count{}; // Bytes written to output
error_code ec{}; // Error code (none on success)
std::string_view custom_error_message{}; // Optional error details
operator bool() const noexcept; // Returns true when there IS an error
bool operator==(error_code e) const noexcept;
};
Key Properties¶
count: Number of bytes written, even on error (useful for debugging)ec: The error code (error_code::noneon success)operator bool(): Returnstruewhen there is an error (matchesstd::error_codesemantics)
Basic Usage¶
Writing to Resizable Buffers¶
my_struct obj{};
std::string buffer{};
auto ec = glz::write_json(obj, buffer);
if (ec) {
// Error occurred
std::cerr << glz::format_error(ec, buffer) << '\n';
return;
}
// Success: for resizable buffers, buffer.size() == ec.count
std::cout << "Wrote " << ec.count << " bytes\n";
Writing to Fixed-Size Buffers¶
Fixed-size buffers like std::array and std::span have automatic bounds checking:
std::array<char, 512> buffer; // Minimum 512 bytes recommended
auto ec = glz::write_json(obj, buffer);
if (ec) {
if (ec.ec == glz::error_code::buffer_overflow) {
// Buffer was too small
std::cerr << "Buffer overflow after " << ec.count << " bytes\n";
// Use a larger buffer or the string-returning overload:
auto result = glz::write_json(obj);
if (result) {
size_t required_size = result->size();
}
}
return;
}
// Success: ec.count contains bytes written
std::string_view json(buffer.data(), ec.count);
Writing to std::span¶
std::span is ideal for writing to external memory (shared memory, DMA buffers, memory-mapped files):
std::span<char> shared_memory(ptr, size);
auto ec = glz::write_json(obj, shared_memory);
if (ec) {
if (ec.ec == glz::error_code::buffer_overflow) {
return error::insufficient_shared_memory;
}
return error::serialization_failed;
}
// Notify consumers of bytes written
notify_consumers(ec.count);
Writing to Raw Pointers¶
Raw char* pointers are trusted to have sufficient space (no bounds checking):
char* dma_buffer = get_dma_buffer(); // Caller guarantees sufficient space
auto ec = glz::write_json(obj, dma_buffer);
if (!ec) {
trigger_dma_transfer(ec.count);
}
Writing to Streams¶
For writing directly to files or output streams with bounded memory, see Streaming I/O.
Buffer Overflow Handling¶
When writing to a fixed-size buffer that's too small, Glaze returns error_code::buffer_overflow:
std::array<char, 64> small_buffer;
auto ec = glz::write_json(large_object, small_buffer);
if (ec.ec == glz::error_code::buffer_overflow) {
// ec.count contains bytes written before overflow
// This is a LOWER BOUND on required size (not total required)
// The partial content is NOT valid JSON - do not parse or transmit
// Useful only for debugging:
std::string_view partial(small_buffer.data(), ec.count);
log_debug("Overflow after {} bytes: {}", ec.count, partial);
// To get actual required size, use the string-returning overload:
auto full = glz::write_json(large_object);
if (full) {
size_t required = full->size();
}
}
Important Notes on Buffer Overflow¶
- Content validity: The first
ec.countbytes contain valid serialized output - Partial data is NOT valid: The content is truncated mid-serialization and cannot be parsed
ec.countis a lower bound: It's the bytes written before failure, not total required size- Content beyond
ec.count: Unspecified (may be uninitialized or previous data)
Minimum Buffer Size Requirement¶
Bounded buffers (std::array, std::span, etc.) must be at least 512 bytes for reliable serialization. This requirement exists because Glaze uses internal padding for efficient writes. Buffers smaller than 512 bytes may return buffer_overflow even when the serialized output would fit.
// Good: 512+ bytes
std::array<char, 512> buffer;
auto ec = glz::write_json(obj, buffer);
// May fail unexpectedly for small outputs
std::array<char, 64> small_buffer; // Not recommended
For very small payloads where memory is extremely constrained, use a resizable buffer (std::string) and copy the result, or use raw char* if you can guarantee sufficient space.
Performance Optimization¶
Skipping Bounds Checking¶
For performance-critical paths where you've pre-validated buffer size:
struct fast_opts : glz::opts {
bool assume_sufficient_buffer = true;
};
std::array<char, 8192> large_buffer; // Known to be sufficient
auto ec = glz::write<fast_opts{}>(obj, large_buffer);
// No bounds checking overhead
Warning: Only use
assume_sufficient_bufferwhen you've verified the buffer is large enough. Buffer overflow with this option leads to undefined behavior.
Format Support¶
The error_ctx type is used consistently across all serialization formats:
// JSON
auto ec = glz::write_json(obj, buffer);
// BEVE (Binary)
auto ec = glz::write_beve(obj, buffer);
// CBOR
auto ec = glz::write_cbor(obj, buffer);
// MessagePack
auto ec = glz::write_msgpack(obj, buffer);
// Generic with options
auto ec = glz::write<glz::opts{.format = glz::JSON}>(obj, buffer);
Extending Buffer Support¶
Glaze uses a traits system that can be specialized for custom buffer types:
namespace glz {
template <size_t N>
struct buffer_traits<my_lib::ring_buffer<N>> {
static constexpr bool is_resizable = false;
static constexpr bool has_bounded_capacity = true;
static size_t capacity(const my_lib::ring_buffer<N>& b) noexcept {
return b.available_write_space();
}
static bool ensure_capacity(my_lib::ring_buffer<N>& b, size_t needed) noexcept {
return b.available_write_space() >= needed;
}
static void finalize(my_lib::ring_buffer<N>& b, size_t written) noexcept {
b.commit(written);
}
};
}
// Now works seamlessly
my_lib::ring_buffer<4096> ring;
auto ec = glz::write_json(obj, ring);
See Also¶
- Reading - Reading from buffers
- Streaming I/O - Reading/writing with streams
- Options - Compile time options including
assume_sufficient_buffer - Binary Format (BEVE) - Binary serialization