Use Validation-Rules as Embedded ELCL Document
This tutorial shows how you can define validation rules as an embedded ELCL document and apply them to a parsed configuration.
This approach keeps the rules readable and close to the configuration syntax itself, while still allowing you to embed everything directly into your C++ application.
Important
Before using validation rules, link exactly one validation-rules library variant as described in Integrate Validation Rules as a Submodule.
When to Use This Approach
Embedded ELCL validation documents are ideal for quick starts and moderate rule sets.
Small embedded rule sets are easy to read and maintain. |
|
Rules stay close to the configuration format they validate. |
|
Less suitable for very large or highly dynamic rule sets. |
|
Errors in the rules document are detected at runtime, not at compile time. |
If your rules are mostly static and maintained by humans, this approach is often clearer than constructing rules in C++ code.
Step 1: Add the Required Headers
Include both the configuration and validation-rules headers:
#include <erbsland/all_conf.hpp>
#include <erbsland/all_conf_vr.hpp>
#include <format>
#include <iostream>
Step 2: Embed the Rules Document
The rules document is standard ELCL text.
Embed it as a C++ raw string literal and parse it using
Parser::parseTextOrThrow().
const auto rulesDocument = parser.parseTextOrThrow(R"(
[server]
type: "section"
[server.host]
type: "text"
default: "127.0.0.1"
[server.port]
type: "integer"
minimum: 1024
maximum: 65535
[server.mode]
type: "text"
in: "dev", "prod"
default: "dev"
)");
Important
Do not indent the ELCL content inside the raw string literal. Leading whitespace becomes part of the text and may change parsing behavior.
Step 3: Build and Apply the Rules
Convert the parsed rules document into a
vr::Rules instance and validate your configuration document:
using el::conf::vr::Rules;
const auto rules = Rules::createFromDocument(rulesDocument);
// Validate configuration for schema version 1
rules->validate(configDocument, 1);
During validation, the library:
Verifies structure (sections and keys)
Checks value types
Applies constraints (e.g.,
minimum,maximum,in)Inserts default values where defined
If validation fails, an Error with category
Validation is thrown.
Complete Example
#include <erbsland/all_conf.hpp>
#include <erbsland/all_conf_vr.hpp>
#include <format>
#include <iostream>
namespace el = erbsland;
int main() {
try {
el::conf::Parser parser;
const auto configDocument = parser.parseTextOrThrow(R"(
[server]
port: 8443
)");
const auto rulesDocument = parser.parseTextOrThrow(R"(
[server]
type: "section"
[server.host]
type: "text"
default: "127.0.0.1"
[server.port]
type: "integer"
minimum: 1024
maximum: 65535
[server.mode]
type: "text"
in: "dev", "prod"
default: "dev"
)");
const auto rules =
el::conf::vr::Rules::createFromDocument(rulesDocument);
rules->validate(configDocument, 1);
const auto host =
configDocument->getOrThrow<std::string>("server.host");
const auto port =
configDocument->getOrThrow<int>("server.port");
const auto mode =
configDocument->getOrThrow<std::string>("server.mode");
std::cout << std::format(
"host={} port={} mode={}\n",
host, port, mode);
return 0;
} catch (const el::conf::Error &error) {
std::cerr << error.toText().toCharString() << "\n";
return 1;
}
}
Try a Failing Input
Change the port in the configuration to 80 and run validation again.
Because the rule defines minimum: 1024, validation will fail and
throw an error describing the violated constraint.
This is a good way to verify that your rules behave exactly as intended.