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::Emitter class in include/yaml-cpp/emitter.h serves as the primary interface for writing YAML from C++ objects, operating as a stateful stream builder.
  • Stream manipulators like BeginSeq, EndMap, Key, and Value defined in include/yaml-cpp/emitterdef.h control document structure and flow versus block styles.
  • Global settings such as SetIntBase, SetFloatPrecision, and SetShowTrailingZero modify 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 through GetLastError() when the internal state machine in src/emitter.cpp detects 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →