Using Built-in Placeholder Sources

A placeholder needs a source before it can produce text. Sometimes the value belongs to the application, such as the name of a field site. At other times the process environment supplies it, such as a setting from the program’s launcher. The built-in var and env sources cover these cases without a custom Source.

Neither source is active on a new Replacer. Register the sources whose values your text may read, then call replaceOrThrow() for a completed result. The same expressions can also appear in quoted ELCL text; Using Placeholders in Configuration Text explains when its parser expands them.

Reading the Process Environment

Call addEnvironmentSource() to register env. The parameter after env: is a platform environment-variable name whose spelling and case are preserved. The source reads its value when replacement runs, so later calls can see changes to the process environment.

The demo sets a variable only to make its output repeatable. An application would normally receive that variable from its parent process.

text/Placeholders/BuiltInSources.cpp

void readEnvironmentSource() {
    auto environment = el::system::EnvironmentVariables{};
    environment.setOrThrow("ERBSLAND_DEMO_VOLCANO_REGION"_el, u8"  Vulkanområdet\tAskja  "_el);

    auto replacer = el::placeholder::Replacer{};
    replacer.addEnvironmentSource();
    replacer.addTextFilters();
    el::io::printLine(replacer.replaceOrThrow("Region: ${env:ERBSLAND_DEMO_VOLCANO_REGION|required|trim}"_el));

    environment.removeOrThrow("ERBSLAND_DEMO_VOLCANO_REGION"_el);
    el::io::printLine(replacer.replaceOrThrow("Unknown: ${env:ERBSLAND_DEMO_VOLCANO_REGION}"_el));
}

Output

Region: Vulkanområdet   Askja
Unknown: undefined

The ,required source flag reports a missing variable as a ReplacerError with the ValueNotFound category. Without that flag, a missing or unreadable variable produces the literal text undefined. An existing variable containing an empty string produces empty text; add the Transforming Placeholder Text with Built-in Filters required filter when an empty value is also unacceptable.

Environment values are ordinary replacement text. By default, env removes Unicode control and format characters except tab and newline. This keeps line breaks in multi-line data while excluding terminal controls and directional formatting. The ,unsafe_raw flag disables that cleanup, so use it only when the destination can safely handle the raw value. Flags can be combined, as in ${env:APPLICATION_PAYLOAD,required,unsafe_raw}.

For portable settings, choose simple uppercase ASCII names containing letters, digits, and underscores. The operating system determines whether environment names are case-sensitive. You may register the source under another name with addEnvironmentSource("process"_el); the expression then begins with ${process:...}. Registering a second source under an occupied name raises LogicFailure.

Supplying Application Values

setVariableSource() registers var with an application-owned StringMap. This is useful for values already known to your program, such as command-line choices or metadata for a document. The map makes the available names explicit and keeps each request independent of the process environment.

text/Placeholders/BuiltInSources.cpp

void readVariableSource() {
    auto replacer = el::placeholder::Replacer{};
    replacer.setVariableSource({{{"field site"_el, u8"Askja"_el}, {"rock type"_el, u8"Basalt"_el}}});
    el::io::printLine(replacer.replaceOrThrow("Site: ${var:FIELD_SITE}; rock: ${var:rock type}"_el));

    replacer.setVariableSource({{{"field site"_el, u8"Hekla"_el}}});
    el::io::printLine(replacer.replaceOrThrow("Next site: ${var:field site}"_el));
}

Output

Site: Askja; rock: Basalt
Next site: Hekla

Variable names follow regular ELCL name matching: names are case-insensitive, and spaces and underscores are equivalent. Thus field site, FIELD_SITE, and Field Site select the same entry. Invalid names and duplicates after normalization are rejected when the map is set. An unknown variable produces a ValueNotFound error during strict replacement; an entry with empty text is valid.

Calling setVariableSource() again with the same source name replaces the complete map. The second field note in the demo sees the new site; the old rock type entry is no longer available. You may also give the source a custom name with the optional second argument. When values need a lookup protocol rather than a fixed map, Providing Your Own Placeholder Values and Filters shows how to write a provider.

Complete demo sources

text/Placeholders/BuiltInSources.cpp

// Copyright (c) 2026 Tobias Erbsland - https://erbsland.dev
// SPDX-License-Identifier: Apache-2.0

#include "PlaceholderDemos.hpp"

#include <erbsland/EnvironmentVariables.hpp>
#include <erbsland/placeholder/Replacer.hpp>
#include <erbsland/StandardStreams.hpp>

namespace demo {

/// Read a process value when replacing a field note.
void readEnvironmentSource() {
    auto environment = el::system::EnvironmentVariables{};
    environment.setOrThrow("ERBSLAND_DEMO_VOLCANO_REGION"_el, u8"  Vulkanområdet\tAskja  "_el);

    auto replacer = el::placeholder::Replacer{};
    replacer.addEnvironmentSource();
    replacer.addTextFilters();
    el::io::printLine(replacer.replaceOrThrow("Region: ${env:ERBSLAND_DEMO_VOLCANO_REGION|required|trim}"_el));

    environment.removeOrThrow("ERBSLAND_DEMO_VOLCANO_REGION"_el);
    el::io::printLine(replacer.replaceOrThrow("Unknown: ${env:ERBSLAND_DEMO_VOLCANO_REGION}"_el));
}

/// Supply application values and replace the complete map for a later note.
void readVariableSource() {
    auto replacer = el::placeholder::Replacer{};
    replacer.setVariableSource({{{"field site"_el, u8"Askja"_el}, {"rock type"_el, u8"Basalt"_el}}});
    el::io::printLine(replacer.replaceOrThrow("Site: ${var:FIELD_SITE}; rock: ${var:rock type}"_el));

    replacer.setVariableSource({{{"field site"_el, u8"Hekla"_el}}});
    el::io::printLine(replacer.replaceOrThrow("Next site: ${var:field site}"_el));
}

}