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));
}
}