diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-05 17:37:54 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-05 17:37:54 +0300 |
| commit | 32f5f761bc8e960293b4f4feaf973dd0da26d0f8 (patch) | |
| tree | 8d0436fdd093d5255c3b75e45f9741882b22e2e4 /docs/handbook/tomlplusplus/basic-usage.md | |
| parent | 64f4ddfa97c19f371fe1847b20bd26803f0a25d5 (diff) | |
| download | Project-Tick-32f5f761bc8e960293b4f4feaf973dd0da26d0f8.tar.gz Project-Tick-32f5f761bc8e960293b4f4feaf973dd0da26d0f8.zip | |
NOISSUE Project Tick Handbook is Released!
Assisted-by: Claude:Opus-4.6-High
Signed-off-by: Mehmet Samet Duman <yongdohyun@projecttick.org>
Diffstat (limited to 'docs/handbook/tomlplusplus/basic-usage.md')
| -rw-r--r-- | docs/handbook/tomlplusplus/basic-usage.md | 705 |
1 files changed, 705 insertions, 0 deletions
diff --git a/docs/handbook/tomlplusplus/basic-usage.md b/docs/handbook/tomlplusplus/basic-usage.md new file mode 100644 index 0000000000..e11d47c42c --- /dev/null +++ b/docs/handbook/tomlplusplus/basic-usage.md @@ -0,0 +1,705 @@ +# toml++ — Basic Usage + +## Including the Library + +The simplest way to start using toml++ is with the default header-only mode: + +```cpp +#include <toml++/toml.hpp> +``` + +Or with the single-header drop-in: + +```cpp +#include "toml.hpp" +``` + +The library places everything in the `toml` namespace. Most examples use the string literal namespace: + +```cpp +using namespace std::string_view_literals; // for "..."sv +``` + +--- + +## Parsing TOML + +### Parsing a String + +```cpp +#include <toml++/toml.hpp> +#include <iostream> + +int main() +{ + auto tbl = toml::parse(R"( + title = "My Config" + + [database] + server = "192.168.1.1" + ports = [ 8001, 8001, 8002 ] + enabled = true + )"); + + std::cout << tbl << "\n"; + return 0; +} +``` + +`toml::parse()` accepts a `std::string_view` and returns a `toml::table` (when exceptions are enabled) or a `toml::parse_result` (when exceptions are disabled). + +### Parsing a File + +```cpp +auto tbl = toml::parse_file("config.toml"); +``` + +`toml::parse_file()` takes a file path as `std::string_view`, opens the file, and parses its contents. + +### Parsing from a Stream + +```cpp +#include <fstream> + +std::ifstream file("config.toml"); +auto tbl = toml::parse(file, "config.toml"); +``` + +The second argument is the source path used for error messages and `source()` metadata. + +### Parsing with Source Path + +You can provide a source path for diagnostic purposes: + +```cpp +auto tbl = toml::parse(toml_string, "my_config.toml"); +``` + +This path is stored in each node's `source().path` and appears in error messages. + +--- + +## Error Handling + +### With Exceptions (Default) + +When `TOML_EXCEPTIONS` is enabled (the default if you don't disable them), `toml::parse()` and `toml::parse_file()` throw `toml::parse_error` on failure: + +```cpp +try +{ + auto tbl = toml::parse_file("config.toml"); +} +catch (const toml::parse_error& err) +{ + std::cerr << "Parse error: " << err.description() << "\n"; + std::cerr << " at " << err.source() << "\n"; + // err.source().begin.line, err.source().begin.column +} +``` + +`toml::parse_error` inherits from `std::runtime_error` in this mode. + +### Without Exceptions + +When compiled with `TOML_EXCEPTIONS=0` (or with `-fno-exceptions`), `parse()` returns a `toml::parse_result`: + +```cpp +toml::parse_result result = toml::parse_file("config.toml"); + +if (result) +{ + // Success — result implicitly converts to table& + toml::table& tbl = result; + std::cout << tbl << "\n"; +} +else +{ + // Failure + std::cerr << "Parse error: " << result.error().description() << "\n"; + std::cerr << " at " << result.error().source() << "\n"; +} +``` + +`parse_result` is a discriminated union that holds either a `toml::table` or a `toml::parse_error`. It converts to `bool` for success checking. + +--- + +## Accessing Values + +### Using `operator[]` — The Easy Way + +`operator[]` on a `toml::table` returns a `toml::node_view`, which is a safe optional-like wrapper: + +```cpp +auto tbl = toml::parse(R"( + [server] + host = "localhost" + port = 8080 + debug = false + tags = ["web", "api"] +)"); + +// Chained access — never throws, returns empty view if path doesn't exist +auto host_view = tbl["server"]["host"]; +auto port_view = tbl["server"]["port"]; + +// Check if the view refers to a node +if (host_view) + std::cout << "Host exists\n"; +``` + +### Getting Values with `value<T>()` + +```cpp +// Returns std::optional<T> +std::optional<std::string_view> host = tbl["server"]["host"].value<std::string_view>(); +std::optional<int64_t> port = tbl["server"]["port"].value<int64_t>(); +std::optional<bool> debug = tbl["server"]["debug"].value<bool>(); + +if (host) + std::cout << "Host: " << *host << "\n"; +``` + +`value<T>()` is permissive — it allows some type conversions (e.g., reading an integer as a double). + +### Getting Values with `value_exact<T>()` + +```cpp +// Strict — only returns a value if the types match exactly +std::optional<int64_t> port = tbl["server"]["port"].value_exact<int64_t>(); +``` + +`value_exact<T>()` only succeeds if the underlying node is exactly that type. No conversions. + +### Getting Values with `value_or()` + +The most convenient accessor — returns the value or a default: + +```cpp +std::string_view host = tbl["server"]["host"].value_or("0.0.0.0"sv); +int64_t port = tbl["server"]["port"].value_or(80); +bool debug = tbl["server"]["debug"].value_or(true); + +// Safe even if the key doesn't exist: +std::string_view missing = tbl["nonexistent"]["key"].value_or("default"sv); +``` + +### Using `as<T>()` for Pointer Access + +`as<T>()` returns a pointer to the node if it matches the type, or `nullptr`: + +```cpp +if (auto* str_val = tbl["server"]["host"].as_string()) + std::cout << "Host: " << str_val->get() << "\n"; + +if (auto* port_val = tbl["server"]["port"].as_integer()) + std::cout << "Port: " << port_val->get() << "\n"; + +// Generic template version: +if (auto* arr = tbl["server"]["tags"].as<toml::array>()) + std::cout << "Tags count: " << arr->size() << "\n"; +``` + +Specific convenience methods exist: +- `as_table()` → `toml::table*` +- `as_array()` → `toml::array*` +- `as_string()` → `toml::value<std::string>*` +- `as_integer()` → `toml::value<int64_t>*` +- `as_floating_point()` → `toml::value<double>*` +- `as_boolean()` → `toml::value<bool>*` +- `as_date()` → `toml::value<toml::date>*` +- `as_time()` → `toml::value<toml::time>*` +- `as_date_time()` → `toml::value<toml::date_time>*` + +### Direct Node Access with `get()` + +On `toml::table`: +```cpp +toml::node* node = tbl.get("server"); +if (node && node->is_table()) +{ + toml::table& server = *node->as_table(); + // ... +} +``` + +On `toml::array`: +```cpp +auto* arr = tbl["server"]["tags"].as_array(); +if (arr && arr->size() > 0) +{ + toml::node& first = (*arr)[0]; + std::cout << first.value_or(""sv) << "\n"; +} +``` + +### Typed get with `get_as<T>()` + +```cpp +// On table — returns pointer if key exists AND matches type +if (auto* val = tbl.get_as<std::string>("title")) + std::cout << "Title: " << val->get() << "\n"; + +// On array — returns pointer if index is valid AND matches type +auto* arr = tbl["tags"].as_array(); +if (arr) +{ + if (auto* s = arr->get_as<std::string>(0)) + std::cout << "First tag: " << s->get() << "\n"; +} +``` + +--- + +## Iterating Tables + +### Range-based For Loop + +```cpp +auto tbl = toml::parse(R"( + a = 1 + b = "hello" + c = true +)"); + +for (auto&& [key, value] : tbl) +{ + std::cout << key << " = " << value << " (type: " << value.type() << ")\n"; +} +``` + +Output: +``` +a = 1 (type: integer) +b = "hello" (type: string) +c = true (type: boolean) +``` + +### Using `for_each()` + +`for_each()` calls a visitor with each key-value pair. The value is passed as its concrete type: + +```cpp +tbl.for_each([](auto& key, auto& value) +{ + std::cout << key << ": "; + if constexpr (toml::is_string<decltype(value)>) + std::cout << "string = " << value.get() << "\n"; + else if constexpr (toml::is_integer<decltype(value)>) + std::cout << "integer = " << value.get() << "\n"; + else if constexpr (toml::is_boolean<decltype(value)>) + std::cout << "boolean = " << value.get() << "\n"; + else + std::cout << "(other)\n"; +}); +``` + +--- + +## Iterating Arrays + +### Range-based For Loop + +```cpp +auto tbl = toml::parse(R"( + numbers = [1, 2, 3, 4, 5] +)"); + +auto& arr = *tbl["numbers"].as_array(); +for (auto& elem : arr) +{ + std::cout << elem.value_or(0) << " "; +} +// Output: 1 2 3 4 5 +``` + +### Index-based Access + +```cpp +for (size_t i = 0; i < arr.size(); i++) +{ + std::cout << arr[i].value_or(0) << " "; +} +``` + +### Using `for_each()` + +```cpp +arr.for_each([](size_t index, auto& elem) +{ + if constexpr (toml::is_integer<decltype(elem)>) + std::cout << "[" << index << "] = " << elem.get() << "\n"; +}); +``` + +--- + +## Creating TOML Programmatically + +### Constructing a Table + +```cpp +auto tbl = toml::table{ + { "title", "My Application" }, + { "version", 2 }, + { "debug", false }, + { "database", toml::table{ + { "host", "localhost" }, + { "port", 5432 } + }}, + { "tags", toml::array{ "web", "api", "rest" } } +}; + +std::cout << tbl << "\n"; +``` + +Output: +```toml +title = "My Application" +version = 2 +debug = false +tags = ["web", "api", "rest"] + +[database] +host = "localhost" +port = 5432 +``` + +### Inserting Values + +```cpp +toml::table config; + +// insert() — only inserts if key doesn't exist +config.insert("name", "MyApp"); +config.insert("name", "Overwritten"); // no-op, key already exists + +// insert_or_assign() — inserts or replaces +config.insert_or_assign("name", "ReplacedApp"); + +// emplace() — construct in place if key doesn't exist +config.emplace<std::string>("greeting", "Hello, World!"); +``` + +### Building Arrays + +```cpp +toml::array arr; +arr.push_back(1); +arr.push_back(2); +arr.push_back(3); +arr.push_back("mixed types are fine"); + +// Or construct directly: +auto arr2 = toml::array{ 10, 20, 30 }; + +// Emplace: +arr2.emplace_back<std::string>("hello"); +``` + +### Creating Date/Time Values + +```cpp +auto tbl = toml::table{ + { "birthday", toml::date{ 1990, 6, 15 } }, + { "alarm", toml::time{ 7, 30 } }, + { "event", toml::date_time{ + toml::date{ 2024, 12, 25 }, + toml::time{ 9, 0 }, + toml::time_offset{ -5, 0 } // EST + }} +}; + +std::cout << tbl << "\n"; +``` + +Output: +```toml +birthday = 1990-06-15 +alarm = 07:30:00 +event = 2024-12-25T09:00:00-05:00 +``` + +--- + +## Modifying Parsed Data + +```cpp +auto tbl = toml::parse(R"( + [server] + host = "localhost" + port = 8080 +)"); + +// Change a value +tbl.insert_or_assign("server", toml::table{ + { "host", "0.0.0.0" }, + { "port", 443 }, + { "ssl", true } +}); + +// Add a new section +tbl.insert("logging", toml::table{ + { "level", "info" }, + { "file", "/var/log/app.log" } +}); + +// Remove a key +if (auto* server = tbl["server"].as_table()) + server->erase("ssl"); + +// Modify array +tbl.insert("features", toml::array{ "auth", "cache" }); +if (auto* features = tbl["features"].as_array()) +{ + features->push_back("logging"); + features->insert(features->begin(), "core"); +} + +std::cout << tbl << "\n"; +``` + +--- + +## Serialization + +### To TOML (Default) + +Simply stream a table or use `toml_formatter`: + +```cpp +// These are equivalent: +std::cout << tbl << "\n"; +std::cout << toml::toml_formatter{ tbl } << "\n"; +``` + +### To JSON + +```cpp +std::cout << toml::json_formatter{ tbl } << "\n"; +``` + +### To YAML + +```cpp +std::cout << toml::yaml_formatter{ tbl } << "\n"; +``` + +### To a String + +```cpp +#include <sstream> + +std::ostringstream ss; +ss << tbl; +std::string toml_string = ss.str(); + +// Or as JSON: +ss.str(""); +ss << toml::json_formatter{ tbl }; +std::string json_string = ss.str(); +``` + +### To a File + +```cpp +#include <fstream> + +std::ofstream file("output.toml"); +file << tbl; +``` + +--- + +## Path-Based Access + +### Using `at_path()` + +```cpp +auto tbl = toml::parse(R"( + [database] + servers = [ + { host = "alpha", port = 5432 }, + { host = "beta", port = 5433 } + ] +)"); + +// Dot-separated path with array indices +auto host = toml::at_path(tbl, "database.servers[0].host"); +std::cout << host.value_or("unknown"sv) << "\n"; // "alpha" + +auto port = toml::at_path(tbl, "database.servers[1].port"); +std::cout << port.value_or(0) << "\n"; // 5433 +``` + +### Using `toml::path` + +```cpp +toml::path p("database.servers[0].host"); +auto view = tbl[p]; +std::cout << view.value_or("unknown"sv) << "\n"; + +// Path manipulation +toml::path parent = p.parent_path(); // "database.servers[0]" +std::cout << tbl[parent] << "\n"; // { host = "alpha", port = 5432 } +``` + +--- + +## The Visitor Pattern + +### Using `visit()` + +```cpp +toml::node& some_node = *tbl.get("title"); + +some_node.visit([](auto& val) +{ + // val is the concrete type: table&, array&, or value<T>& + using T = std::remove_cvref_t<decltype(val)>; + + if constexpr (std::is_same_v<T, toml::table>) + std::cout << "It's a table\n"; + else if constexpr (std::is_same_v<T, toml::array>) + std::cout << "It's an array\n"; + else + std::cout << "It's a value: " << val.get() << "\n"; +}); +``` + +### Using `for_each()` on Tables and Arrays + +`for_each()` iterates and visits each element with its concrete type: + +```cpp +tbl.for_each([](const toml::key& key, auto& value) +{ + std::cout << key << " -> " << value << "\n"; +}); +``` + +--- + +## Source Information + +Every parsed node tracks where it was defined: + +```cpp +auto tbl = toml::parse_file("config.toml"); + +if (auto* name = tbl.get("name")) +{ + auto& src = name->source(); + std::cout << "Defined at line " << src.begin.line + << ", column " << src.begin.column << "\n"; + + if (src.path) + std::cout << "In file: " << *src.path << "\n"; +} +``` + +--- + +## Type Checking + +```cpp +toml::node& node = /* some node */; + +// Virtual method checks +if (node.is_string()) { /* ... */ } +if (node.is_integer()) { /* ... */ } +if (node.is_table()) { /* ... */ } + +// Template check +if (node.is<double>()) { /* ... */ } +if (node.is<toml::array>()) { /* ... */ } + +// Get the type enum +switch (node.type()) +{ + case toml::node_type::string: break; + case toml::node_type::integer: break; + case toml::node_type::floating_point: break; + case toml::node_type::boolean: break; + case toml::node_type::date: break; + case toml::node_type::time: break; + case toml::node_type::date_time: break; + case toml::node_type::table: break; + case toml::node_type::array: break; + default: break; +} +``` + +--- + +## Complete Example: Config File Reader + +```cpp +#include <toml++/toml.hpp> +#include <iostream> +#include <string_view> + +using namespace std::string_view_literals; + +int main() +{ + toml::table config; + try + { + config = toml::parse_file("app.toml"); + } + catch (const toml::parse_error& err) + { + std::cerr << "Failed to parse config:\n" << err << "\n"; + return 1; + } + + // Read application settings + auto app_name = config["app"]["name"].value_or("Unknown"sv); + auto app_version = config["app"]["version"].value_or(1); + auto log_level = config["logging"]["level"].value_or("info"sv); + auto log_file = config["logging"]["file"].value_or("/tmp/app.log"sv); + + std::cout << "Application: " << app_name << " v" << app_version << "\n"; + std::cout << "Log level: " << log_level << "\n"; + std::cout << "Log file: " << log_file << "\n"; + + // Read database connections + if (auto* dbs = config["databases"].as_array()) + { + for (auto& db_node : *dbs) + { + if (auto* db = db_node.as_table()) + { + auto host = (*db)["host"].value_or("localhost"sv); + auto port = (*db)["port"].value_or(5432); + auto name = (*db)["name"].value_or("mydb"sv); + std::cout << "DB: " << name << " @ " << host << ":" << port << "\n"; + } + } + } + + // Modify and write back + config.insert_or_assign("last_run", toml::date_time{ + toml::date{ 2024, 1, 15 }, + toml::time{ 14, 30, 0 } + }); + + std::ofstream out("app.toml"); + out << config; + + return 0; +} +``` + +--- + +## Related Documentation + +- [node-system.md](node-system.md) — Deep dive into node types and value retrieval +- [tables.md](tables.md) — Table manipulation details +- [arrays.md](arrays.md) — Array manipulation details +- [parsing.md](parsing.md) — Parser internals and error handling +- [formatting.md](formatting.md) — Serialization customization +- [path-system.md](path-system.md) — Path-based navigation |
