Controlling YAML Flow Style vs Block Style Output in yaml-cpp: A Complete Guide

yaml-cpp controls flow-style (inline) and block-style (multi-line) output through the YAML::Emitter class using stream manipulators (YAML::Flow, YAML::Block) and per-type format setters (SetSeqFormat, SetMapFormat).

The yaml-cpp library (jbeder/yaml-cpp) provides precise control over YAML output formatting through its emitter subsystem. When generating YAML documents, you can choose between compact flow-style notation ([1, 2, 3]) or readable block-style indentation. This guide explains how to manipulate these styles using the library's official API based on the actual source implementation in src/emitter.cpp and related files.

Understanding the Emitter Architecture

The YAML::Emitter class manages output generation through a state stack that tracks whether the current context uses block or flow formatting. According to the source code in src/emitter.cpp, the emitter maintains this state in EmitterState and determines collection delimiters based on the current flowType value.

How Context Switching Works

When you begin a collection using BeginSeq or BeginMap, the emitter calls PrepareNode, which forwards to specific preparation functions: FlowSeqPrepareNode, BlockSeqPrepareNode, FlowMapPrepareNode, or BlockMapPrepareNode. These functions consult the flowType stored in the emitter state and emit the appropriate delimiters ([ and ] for flow sequences, { and } for flow maps, or line breaks with indentation for block style).

Temporary Style Control with Manipulators

Manipulators are special objects defined in include/yaml-cpp/emitterdef.h that modify the emitter's state when inserted into the stream. The most relevant manipulators for style control are YAML::Flow and YAML::Block.

Applying Flow Style to Specific Collections

When you insert a manipulator immediately before BeginSeq or BeginMap, it forces that specific collection to render in the specified style. The manipulator affects only the next collection and automatically reverts to the previous context after the collection ends.

#include <yaml-cpp/yaml.h>
#include <iostream>

int main() {
    YAML::Emitter out;
    out << YAML::BeginMap
        << YAML::Key << "title" << YAML::Value << "Example"
        << YAML::Key << "items"
        << YAML::Flow          // Force flow style for this sequence only
        << YAML::BeginSeq
        << "one" << "two" << "three"
        << YAML::EndSeq
        << YAML::EndMap;
    
    std::cout << out.c_str() << std::endl;
    // Output: title: Example
    //         items: [one, two, three]
}

Scoped Block Style Application

Similarly, inserting YAML::Block before a collection forces multi-line formatting with proper indentation, even if the surrounding context uses flow style.

YAML::Emitter out;
out << YAML::BeginSeq
    << "first"
    << YAML::Block        // Force block style for this nested map
    << YAML::BeginMap
      << YAML::Key << "nested" << YAML::Value << "value"
    << YAML::EndMap
    << "third"
    << YAML::EndSeq;

Persistent Format Configuration with Setters

For global style control across multiple collections, the emitter exposes SetSeqFormat(EMITTER_MANIP) and SetMapFormat(EMITTER_MANIP). These methods, implemented in include/yaml-cpp/emitter.h, call Emitter::SetLocalValue to store the manipulator in the current EmitterState, affecting all subsequent collections of that type until changed again.

Setting Default Sequence Format

YAML::Emitter out;
out << YAML::SetSeqFormat(YAML::Flow);   // All following sequences use flow style
out << YAML::SetMapFormat(YAML::Block);  // All following maps use block style

out << YAML::BeginMap
    << YAML::Key << "list" << YAML::Value
    << YAML::BeginSeq << 1 << 2 << 3 << YAML::EndSeq  // Rendered as [1, 2, 3]
    << YAML::Key << "config"
    << YAML::BeginMap
      << YAML::Key << "enabled" << YAML::Value << true  // Rendered in block style
    << YAML::EndMap
    << YAML::EndMap;

Practical Implementation Examples

Mixed Style Documents

Combine flow and block styles within the same document to optimize readability:

YAML::Emitter out;
out << YAML::BeginMap
    << YAML::Key << "name"  << YAML::Value << "Alice"
    << YAML::Key << "scores"
    << YAML::Flow           // Inline array for compactness
    << YAML::BeginSeq
    << 85 << 92 << 78
    << YAML::EndSeq
    << YAML::Key << "details"
    << YAML::BeginMap      // Block map for readability (default)
      << YAML::Key << "age"   << YAML::Value << 30
      << YAML::Key << "city"  << YAML::Value << "Paris"
    << YAML::EndMap
    << YAML::EndMap;
std::cout << out.c_str();

Project-Wide Default Configuration

Set styles once at the emitter initialization for consistent formatting across your application:

YAML::Emitter out;
out << YAML::SetSeqFormat(YAML::Flow)   // All sequences inline
    << YAML::SetMapFormat(YAML::Block); // All maps block-style

// All subsequent collections follow these rules until modified
out << YAML::BeginMap
    << YAML::Key << "data" << YAML::Value
    << YAML::BeginSeq << "a" << "b" << "c" << YAML::EndSeq
    << YAML::EndMap;

Summary

  • Stream manipulators (YAML::Flow, YAML::Block) provide temporary, scoped control over individual collections by setting the flowType in the emitter state immediately before BeginSeq or BeginMap.
  • Format setters (SetSeqFormat, SetMapFormat) change the default style for all subsequent sequences or maps by modifying EmitterState through SetLocalValue.
  • The implementation in src/emitter.cpp routes collection starts through specific preparation functions (FlowSeqPrepareNode, BlockMapPrepareNode, etc.) that emit the correct delimiters based on the current state.
  • Styles automatically revert to the parent context after a collection ends, allowing fine-grained mixing of flow and block formats within nested structures.

Frequently Asked Questions

How long does a flow style manipulator remain active?

A manipulator like YAML::Flow applies only to the immediately following collection (BeginSeq or BeginMap). Once you call EndSeq or EndMap, the emitter reverts to the style defined by the parent context or the default settings. This scoping allows precise control without affecting sibling or parent nodes.

What is the difference between manipulators and SetSeqFormat?

Manipulators (YAML::Flow, YAML::Block) affect only the next collection and are transient. SetSeqFormat and SetMapFormat modify the emitter's persistent local state, affecting all future collections of that type until you explicitly change the setting again or destroy the emitter. Use manipulators for one-off changes and setters for global defaults.

Can I force block style for a specific map while keeping sequences in flow style?

Yes. You can mix styles arbitrarily by combining global defaults with local manipulators. For example, set SetSeqFormat(YAML::Flow) for all sequences, then insert YAML::Block immediately before any specific BeginMap that requires multi-line formatting. The manipulator overrides the global setting for that single collection.

Where are the manipulator objects defined in the source code?

The manipulator constants are defined in include/yaml-cpp/emitterdef.h, while the YAML::Emitter class that processes them is declared in include/yaml-cpp/emitter.h. The logic that interprets these manipulators and selects output delimiters resides in src/emitter.cpp, specifically within the PrepareNode family of functions and the EmitterState management code in src/emitterstate.cpp.

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 →