How to Emit YAML from C++ Objects Using yaml-cpp Emitter
The YAML::Emitter class provides a stateful stream interface that serializes C++ primitives, containers, and custom objects into properly formatted YAML documents through operator<< overloads and stream manipulators.
yaml-cpp is a widely-used C++ library for handling YAML configuration files. When you need to programmatically generate YAML output from C++ data structures, the Emitter class serves as the primary serialization engine, managing indentation, quoting rules, and block versus flow styles automatically.
Core Architecture of the Emitter Class
The Emitter class defined in include/yaml-cpp/emitter.h acts as a token-by-token document builder. It maintains internal state to track container nesting and applies YAML syntax rules during emission.
Construction and Stream Interface
The constructor initializes an internal EmitterState and wraps either an internal std::ostringstream or a user-provided output stream:
Emitter(); // Uses internal string buffer
Emitter(std::ostream& out); // Writes directly to provided stream
According to the yaml-cpp source code in include/yaml-cpp/emitter.h, the class exposes its output through c_str() when using the default internal buffer, or writes directly to the bound stream.
Writing Primitives and Operator Overloads
The emitter provides overloaded Write methods for raw strings, numbers, booleans, null values, and binary data. These are wrapped by friend operator<< implementations that make the API feel like standard C++ streams:
// From include/yaml-cpp/emitter.h
operator<<(Emitter&, const std::string&);
operator<<(Emitter&, int);
operator<<(Emitter&, EMITTER_MANIP);
When emitting YAML::Node objects, the operator<< overload defined in include/yaml-cpp/node/emit.h delegates to the node's internal emit implementation, allowing entire trees to be dumped in a single call.
State Management and Syntax Validation
The implementation in src/emitter.cpp handles the heavy lifting through private methods like EmitBeginSeq, EmitEndMap, and SpaceOrIndentTo. These maintain a stack of container contexts to enforce proper indentation and track whether the current context is flow or block style.
Error handling relies on good() and GetLastError(). If you attempt to close a sequence that was never opened, the emitter sets an error state and refuses further writes until checked.
How to Write YAML from C++ Objects
Basic Scalars and Sequences
Use BeginSeq and EndSeq manipulators to create block sequences. Manipulators are defined in include/yaml-cpp/emitterdef.h:
#include <yaml-cpp/yaml.h>
using namespace YAML;
Emitter out;
out << BeginSeq;
out << "eggs";
out << "bread";
out << "milk";
out << EndSeq;
std::cout << out.c_str() << std::endl;
// Output:
// - eggs
// - bread
// - milk
Maps and Flow Style Collections
Create mappings using Key and Value manipulators. Force flow style (inline) output with the Flow manipulator:
Emitter out;
out << Flow << BeginMap;
out << Key << "shape" << Value << "square";
out << Key << "color" << Value << "blue";
out << EndMap;
// Result: {shape: square, color: blue}
Global Formatting Options
Control integer base representation and floating-point precision through global emitter settings. These settings affect all subsequent scalar emissions:
Emitter out;
out << BeginSeq;
out.SetIntBase(Dec);
out << 1024; // 1024
out.SetIntBase(Hex);
out << 1024; // 0x400
out.SetIntBase(Oct);
out << 1024; // 02000
out << EndSeq;
// Float precision
out.SetFloatPrecision(3);
out.SetDoublePrecision(2);
out.SetShowTrailingZero(true);
Custom Types with Operator Overloading
Define a custom operator<< for your structs to integrate them with the emitter pipeline:
struct Point {
int x, y;
};
Emitter& operator<<(Emitter& out, const Point& p) {
out << BeginMap;
out << Key << "x" << Value << p.x;
out << Key << "y" << Value << p.y;
out << EndMap;
return out;
}
// Usage
Emitter out;
out << BeginSeq;
out << Point{1, 2};
out << Point{3, 4};
out << EndSeq;
// Generates:
// - x: 1
// y: 2
// - x: 3
// y: 4
Emitting Existing Node Trees
If you have parsed or constructed a YAML::Node programmatically, emit it directly without manual traversal:
Node config = LoadFile("config.yaml");
Emitter out;
out << config;
std::cout << out.c_str();
The operator<< overload in include/yaml-cpp/node/emit.h handles the recursive emission of the entire node structure.
Advanced Features: Comments, Anchors, and Tags
Add YAML comments, anchors, and custom tags using specific manipulators:
Emitter out;
out << BeginMap;
out << Comment("Application configuration");
out << Key << "database" << Value << BeginMap;
out << Key << "host" << Value << "localhost";
out << Key << "port" << Value << 5432;
out << EndMap;
out << Key << "cache" << Value << Anchor("myCache") << BeginSeq;
out << "item1" << "item2";
out << EndSeq;
out << EndMap;
Fine-grained formatting controls like SingleQuoted, FloatPrecision, UpperCase, and LowerCase are available in include/yaml-cpp/emittermanip.h for specialized output requirements.
Summary
- The
YAML::Emitterclass ininclude/yaml-cpp/emitter.hserves as the primary interface for writing YAML from C++ objects, operating as a stateful stream builder. - Stream manipulators like
BeginSeq,EndMap,Key, andValuedefined ininclude/yaml-cpp/emitterdef.hcontrol document structure and flow versus block styles. - Global settings such as
SetIntBase,SetFloatPrecision, andSetShowTrailingZeromodify the formatting of subsequent scalar emissions. - Custom types integrate seamlessly by overloading
operator<<(Emitter&, const T&), following the pattern used for standard library types. - Error handling requires checking
good()after operations, with detailed messages available throughGetLastError()when the internal state machine insrc/emitter.cppdetects invalid sequences.
Frequently Asked Questions
How do I check if yaml-cpp emission succeeded?
Call the good() method on your Emitter instance after write operations. If it returns false, use GetLastError() to retrieve a descriptive string explaining the failure, such as mismatched EndSeq or EndMap calls.
Can I write YAML directly to a file instead of a string?
Yes. Construct the Emitter with an std::ostream reference: Emitter out(fileStream); where fileStream is an std::ofstream. When constructed this way, the emitter writes directly to the file rather than buffering internally, and you cannot use c_str() to retrieve the buffer.
How do I force specific quoting styles on strings?
Use the SingleQuoted manipulator defined in include/yaml-cpp/emittermanip.h before emitting a string: out << SingleQuoted << "value";. This forces the emitter to use single quotes regardless of automatic detection. Similar manipulators exist for double quotes and literal blocks.
What is the difference between Flow and Block styles in yaml-cpp?
Block style (the default) places each collection element on a new line with indentation, suitable for human readability. Flow style outputs collections inline using curly braces or brackets, useful for compact representations. Apply the Flow manipulator before BeginMap or BeginSeq to enable flow style for that specific container.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →