diff --git a/examples/tests/README.md b/examples/tests/README.md index 5ff7d84..6dcbf5c 100644 --- a/examples/tests/README.md +++ b/examples/tests/README.md @@ -1,6 +1,7 @@ # tests -Two ways to write tests, in one project. +Tests are declared in `project.cpp` via `cfg.AddTest(name)`. One line per +test; deps and other options chain off the returned builder. ```sh cd examples/tests @@ -11,20 +12,31 @@ Layout: ``` mylib/MyMath.cppm # the library being tested -project.cpp # declares the library +project.cpp # declares the library and its tests -tests/Smoke/main.cpp # zero-config test (no project.cpp) -tests/UnitMyMath/main.cpp # test that links MyMath and exercises it -tests/UnitMyMath/project.cpp # required for tests with deps +tests/Smoke/main.cpp # zero-config test +tests/UnitMyMath/main.cpp # test that links MyMath ``` -## Auto-discovery +`project.cpp` does: -Each `tests//` directory becomes a test. Three layers, escalate only as needed: +```cpp +cfg.AddTest("Smoke"); // exit 0 = pass +cfg.AddTest("UnitMyMath").Dependencies({ &cfg }); // imports MyMath +``` -1. **`tests//main.cpp`** with no `project.cpp` — discovery synthesizes a Configuration. Top-level `*.cpp` files become implementations, `interfaces/*.cppm` become module interfaces. `Smoke` is this case. -2. **`tests//project.cpp`** — full control. Use this when you need defines, dependencies, or non-default targets. `UnitMyMath` is this case (it depends on `MyMath`). -3. Folders starting with `_` or `.` are skipped (e.g. `tests/_shared/` for cross-test helpers). +`AddTest(name)` defaults the source to `tests//main.cpp` (resolved at +the project root) and the target/march/mtune to the parent project's. The +builder methods overlay overrides: + +- `.Target(triple)` / `.March(...)` / `.Mtune(...)` / `.Sysroot(path)` +- `.Define(name, value)` / `.Debug()` +- `.Timeout(seconds)` +- `.Args(vec)` — runtime args +- `.Requires("tool:foo")` / `.Requires("file:/path")` / `.Requires("env:VAR")` +- `.Dependencies({ &otherCfg, ... })` +- `.Path(p)` — rebase the test's source-resolution path +- `.LinkFlag(s)` / `.CompileFlag(s)` ## Test conventions @@ -35,20 +47,46 @@ Each `tests//` directory becomes a test. Three layers, escalate only as ne ## Linking the parent project -`UnitMyMath/project.cpp` shows how a test links the project's own library: - -```cpp -cfg.dependencies = { ParentLib("MyMath") }; -``` - -`ParentLib("name")` looks up a `Configuration*` in the parent project (the root project's own config + its dependency graph) by `Configuration::name`. The fixture's project.cpp can omit `cfg.path`, `cfg.name`, etc. — the discovery loop fills folder-derived defaults. +`UnitMyMath` depends on the library via `.Dependencies({ &cfg })` — the +test imports `MyMath` and the build engine links `cfg`'s output into the +test exe. ## Cross-target test runs -Tests parse `--target=...` from the project args you pass on the command line: +`AddTest` inherits the parent project's target; for per-test cross-arch +runs, override via the builder: -```sh -crafter-build test --target=x86_64-w64-mingw32 --runner=cmd:wine +```cpp +cfg.AddTest("CrossArchAarch64") + .Target("aarch64-linux-gnu") + .Sysroot("/opt/aarch64-rootfs") + .Requires("tool:qemu-aarch64"); ``` -`--runner=` overrides the per-target runner for this invocation. Useful specs: `local`, `cmd:` (prefix-exec, e.g. `cmd:wine`, `cmd:qemu-aarch64`), `ssh:[:]`, `sshwin:[:]`. Or persist via env var: `CRAFTER_BUILD_RUNNER_=`. +`crafter-build test` sweeps every distinct target declared across the +project's tests plus the host triple, so cross-arch tests run by default. +`--target=` restricts the run to that target. + +`--runner=` overrides the per-target runner for one invocation. +Useful specs: `local`, `cmd:` (e.g. `cmd:wine`, `cmd:qemu-aarch64`). +Persistent override via env: `CRAFTER_BUILD_RUNNER_=`. + +## SIMD march fan-out + +For libraries with per-march SIMD codegen (e.g. Crafter.Math), use +`AddMarchVariants` to produce one Test per tier sharing the same source +and interface set: + +```cpp +constexpr MarchTier tiers[] = { + {"sapphirerapids", "native"}, + {"x86-64-v4", "generic"}, + {"x86-64-v3", "generic"}, +}; +cfg.AddMarchVariants("Vector", libInterfaces, tiers); +``` + +Each tier becomes a Test named `Vector-`, with the library's +interfaces rebuilt under that tier's `-march`/`-mtune` (so codegen actually +varies). Per-arch gating is just an `if (target.starts_with("x86_64"))` +guard around the call — no special toml syntax. diff --git a/examples/tests/project.cpp b/examples/tests/project.cpp index 58661ae..5a19fcd 100644 --- a/examples/tests/project.cpp +++ b/examples/tests/project.cpp @@ -4,7 +4,8 @@ namespace fs = std::filesystem; using namespace Crafter; // A pure library project: the root Configuration is the lib itself. Tests -// under tests/ are auto-discovered (see tests/Smoke and tests/UnitMyMath). +// are declared with cfg.AddTest — one line per test, with deps and other +// options applied via the returned builder. extern "C" Configuration CrafterBuildProject(std::span) { Configuration cfg; cfg.path = "./mylib/"; @@ -16,5 +17,9 @@ extern "C" Configuration CrafterBuildProject(std::span) std::array ifaces = { "MyMath" }; std::array impls = {}; cfg.GetInterfacesAndImplementations(ifaces, impls); + + cfg.AddTest("Smoke"); + cfg.AddTest("UnitMyMath").Dependencies({ &cfg }); + return cfg; } diff --git a/examples/tests/tests/UnitMyMath/project.cpp b/examples/tests/tests/UnitMyMath/project.cpp deleted file mode 100644 index 935382a..0000000 --- a/examples/tests/tests/UnitMyMath/project.cpp +++ /dev/null @@ -1,22 +0,0 @@ -import std; -import Crafter.Build; -namespace fs = std::filesystem; -using namespace Crafter; - -// Test that links the parent project's library so it can `import MyMath;` -// and call its functions. ParentLib(name) walks the parent project's -// dependency graph (and the parent itself) to find a Configuration by name. -extern "C" Configuration CrafterBuildProject(std::span) { - Configuration cfg; - cfg.path = "tests/UnitMyMath/"; - cfg.name = "UnitMyMath"; - cfg.outputName = "UnitMyMath"; - cfg.target = "x86_64-pc-linux-gnu"; - cfg.type = ConfigurationType::Executable; - cfg.dependencies = { ParentLib("MyMath") }; - - std::array ifaces = {}; - std::array impls = { "main" }; - cfg.GetInterfacesAndImplementations(ifaces, impls); - return cfg; -} diff --git a/implementations/Crafter.Build-Clang.cpp b/implementations/Crafter.Build-Clang.cpp index dd8c674..9da9db4 100644 --- a/implementations/Crafter.Build-Clang.cpp +++ b/implementations/Crafter.Build-Clang.cpp @@ -1318,12 +1318,9 @@ Test options (after the `test` subcommand): --runner= Override the test runner for this run. Specs: local cmd: (e.g. cmd:wine) - ssh:[:] - sshwin:[:] - wsl[:] - --target= Filter to tests whose cfg.target matches; this is - also forwarded to project-args so per-target tests - build for that triple. Default: host triple. + --target= Filter to tests whose cfg.target matches. Default: + sweep across every distinct target declared by the + project's tests plus the host triple. One or more name globs to filter tests (e.g. 'Unit*'). Project args: diff --git a/implementations/Crafter.Build-Test.cpp b/implementations/Crafter.Build-Test.cpp index 23ef72d..f7d7cbb 100644 --- a/implementations/Crafter.Build-Test.cpp +++ b/implementations/Crafter.Build-Test.cpp @@ -18,11 +18,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; -// toml++ is consumed as a translation-unit-private dependency in the GMF: the -// parser is only needed for test.toml discovery here, so keeping it out of -// `import std`-using module purviews avoids dragging it through the module -// graph (and through every PCM consumer of Crafter.Build). -#include "../lib/toml.hpp" export module Crafter.Build:Test_impl; import std; import :Test; @@ -387,74 +382,6 @@ TestResult Crafter::RunSingleTest(const Test& test, const fs::path& binary, std: } namespace { - // Declarative test metadata loaded from tests//test.toml. Lets a test - // ship just main.cpp + a few lines of config instead of a whole project.cpp - // when its needs are "pick a target, gate on prerequisites, run with these - // args". project.cpp stays the escape hatch for outer-driver tests that - // call Build() / inspect intermediate state. - struct TestManifest { - std::optional target; - std::optional march; - std::optional mtune; - std::optional sysroot; - std::vector requires_; - std::optional timeoutSeconds; - std::vector args; - std::vector> defines; - }; - - TestManifest ParseTestManifest(const fs::path& path) { - // toml++ builds with exceptions enabled by default; parse_file throws - // toml::parse_error on malformed input. Rethrow with the path attached - // so the discovery loop's catch can surface "where the error came from" - // alongside toml++'s "what was wrong". - toml::table t; - try { - t = toml::parse_file(path.string()); - } catch (const toml::parse_error& e) { - throw std::runtime_error(std::format( - "test.toml parse error in {}: {}", - path.string(), - std::string_view(e.description()))); - } - TestManifest m; - if (auto v = t["target"].value()) m.target = *v; - if (auto v = t["march"].value()) m.march = *v; - if (auto v = t["mtune"].value()) m.mtune = *v; - if (auto v = t["sysroot"].value()) m.sysroot = *v; - if (auto v = t["timeout"].value()) m.timeoutSeconds = static_cast(*v); - if (auto arr = t["requires"].as_array()) { - for (auto& el : *arr) { - if (auto s = el.value()) m.requires_.push_back(*s); - } - } - if (auto arr = t["args"].as_array()) { - for (auto& el : *arr) { - if (auto s = el.value()) m.args.push_back(*s); - } - } - if (auto tbl = t["defines"].as_table()) { - for (auto&& [k, v] : *tbl) { - if (auto s = v.value()) { - m.defines.emplace_back(std::string(k.str()), *s); - } - } - } - return m; - } - - // Apply manifest overlay onto a Configuration synthesized from the test - // folder. Target overrides come last so a manifest's `target = "..."` - // wins over the synth default (= run's targetFilter). Defines accumulate; - // they don't replace pre-existing ones. - void ApplyManifest(Configuration& cfg, const TestManifest& m) { - if (m.target) cfg.target = *m.target; - if (m.march) cfg.march = *m.march; - if (m.mtune) cfg.mtune = *m.mtune; - if (m.sysroot) cfg.sysroot = *m.sysroot; - for (auto& [k, v] : m.defines) cfg.defines.push_back({k, v}); - } - bool ToolOnPath(std::string_view name) { #ifdef _WIN32 std::string cmd = std::format("where {} > nul 2>&1", name); @@ -527,99 +454,103 @@ namespace { } } -namespace { - // Synthesize a Configuration for tests// folders that don't contain - // a project.cpp. Convention: cfg.path = the folder, cfg.name/outputName = - // folder basename, cfg.target = host (overridable via test.toml `target`), - // cfg.type = exe. Sources: top-level *.cpp (excluding project.cpp) become - // implementations, interfaces/*.cppm become module interfaces. Tests with - // deeper layouts or dependencies still need an explicit project.cpp. - // - // Why host-default instead of targetFilter-default: under the multi-target - // sweep, an arch-agnostic test (no test.toml target) should run at the - // host iteration only — not get rebuilt against every cross-target the - // suite happens to declare. Cross-targeting is an opt-in via test.toml. - Configuration SynthesizeTest(const fs::path& dir) { - Configuration cfg; - cfg.path = dir; - cfg.name = dir.filename().string(); - cfg.outputName = cfg.name; - cfg.target = HostTarget(); - cfg.type = ConfigurationType::Executable; +TestBuilder Configuration::AddTest(std::string_view name) { + // Empty-interfaces trampoline. The span we hand to the (name, interfaces) + // overload wraps a stack array; it's only consulted inside that call, so + // its lifetime is fine for the duration. + std::array noIfaces = {}; + return AddTest(name, noIfaces); +} - std::vector impls; - for (auto& e : fs::directory_iterator(dir)) { - if (!e.is_regular_file()) continue; - auto p = e.path(); - if (p.extension() != ".cpp") continue; - if (p.filename() == "project.cpp") continue; - impls.push_back(p.stem()); - } - std::ranges::sort(impls); +TestBuilder Configuration::AddTest(std::string_view name, std::span interfaces) { + Test t; + // Default to "./" so tests//main.cpp resolves at the project root + // (where crafter-build sets cwd from project.cpp's location). Projects + // whose lib lives in a subdir (e.g. cfg.path = "./mylib/") get this + // right by default; if the test layout is unusual, override via .Path(). + t.config.path = "./"; + t.config.name = std::string(name); + t.config.outputName = std::string(name); + t.config.target = this->target; + t.config.march = this->march; + t.config.mtune = this->mtune; + t.config.debug = this->debug; + t.config.type = ConfigurationType::Executable; - std::vector ifaces; - fs::path interfacesDir = dir / "interfaces"; - if (fs::exists(interfacesDir) && fs::is_directory(interfacesDir)) { - for (auto& e : fs::directory_iterator(interfacesDir)) { - if (!e.is_regular_file()) continue; - auto p = e.path(); - if (p.extension() != ".cppm") continue; - ifaces.push_back(fs::path("interfaces") / p.stem()); - } - std::ranges::sort(ifaces); - } + // Default source layout: tests//main.cpp resolved against the + // parent Configuration's path. cfg.path is typically "./" (project + // root), which puts the test sources at /tests//main.cpp. + fs::path mainSource = fs::path("tests") / std::string(name) / "main"; + std::array impls = { mainSource }; + t.config.GetInterfacesAndImplementations(interfaces, impls); - if (impls.empty() && ifaces.empty()) { - throw std::runtime_error(std::format( - "no .cpp or interfaces/*.cppm files found in {}", dir.string())); - } + tests.push_back(std::move(t)); + return TestBuilder{this, tests.size() - 1}; +} - cfg.GetInterfacesAndImplementations(ifaces, impls); - return cfg; +void Configuration::AddMarchVariants(std::string_view name, + std::span interfaces, + std::span tiers) { + for (const auto& tier : tiers) { + Test t; + t.config.path = "./"; + t.config.name = std::format("{}-{}", name, tier.march); + t.config.outputName = t.config.name; + t.config.target = this->target; + t.config.march = tier.march; + t.config.mtune = tier.mtune; + t.config.debug = this->debug; + t.config.type = ConfigurationType::Executable; + + fs::path mainSource = fs::path("tests") / std::string(name) / "main"; + std::array impls = { mainSource }; + // Interfaces stamped directly into each variant's Configuration — + // NOT via cfg.dependencies. The dependency machinery caches PCMs by + // dep->VariantId(), which would compile the parent's interfaces + // once with the parent's march; for SIMD-sensitive code each + // variant needs its own compile. Listing the interfaces as the + // test's own forces per-variant recompilation. + t.config.GetInterfacesAndImplementations(interfaces, impls); + tests.push_back(std::move(t)); } } +TestBuilder& TestBuilder::Path(fs::path p) { test().config.path = std::move(p); return *this; } +TestBuilder& TestBuilder::Target(std::string t) { test().config.target = std::move(t); return *this; } +TestBuilder& TestBuilder::March(std::string m) { test().config.march = std::move(m); return *this; } +TestBuilder& TestBuilder::Mtune(std::string m) { test().config.mtune = std::move(m); return *this; } +TestBuilder& TestBuilder::Sysroot(fs::path s) { test().config.sysroot = s.string(); return *this; } +TestBuilder& TestBuilder::Debug(bool d) { test().config.debug = d; return *this; } +TestBuilder& TestBuilder::Define(std::string n, std::string v) { + test().config.defines.push_back({std::move(n), std::move(v)}); + return *this; +} +TestBuilder& TestBuilder::Timeout(std::chrono::seconds s) { test().timeout = s; return *this; } +TestBuilder& TestBuilder::Args(std::vector a) { test().args = std::move(a); return *this; } +TestBuilder& TestBuilder::Requires(std::string r) { test().requires_.push_back(std::move(r)); return *this; } +TestBuilder& TestBuilder::Dependencies(std::vector d) { + test().config.dependencies = std::move(d); + return *this; +} +TestBuilder& TestBuilder::LinkFlag(std::string f) { test().config.linkFlags.push_back(std::move(f)); return *this; } +TestBuilder& TestBuilder::CompileFlag(std::string f) { test().config.compileFlags.push_back(std::move(f)); return *this; } + TestSummary Crafter::RunTests(Configuration& projectCfg, const RunTestsOptions& opts, std::span projectArgs) { // Multi-target sweep: when no --target= was given, the run covers every - // distinct target a test.toml declares plus the host target. Lets a bare - // `crafter-build test` exercise cross-arch tests without the user having - // to know which targets exist in this project. An explicit --target=X + // distinct target declared across projectCfg.tests plus the host target. + // Lets a bare `crafter-build test` exercise cross-arch tests without the + // user having to know which targets exist. An explicit --target=X // bypasses the sweep and runs that target only. if (opts.targetFilter.empty()) { std::set sweep; sweep.insert(HostTarget()); - fs::path testsDir = fs::current_path() / "tests"; - if (fs::exists(testsDir) && fs::is_directory(testsDir)) { - for (auto& e : fs::directory_iterator(testsDir)) { - if (!e.is_directory()) continue; - auto stem = e.path().filename().string(); - if (stem.empty() || stem[0] == '_' || stem[0] == '.') continue; - fs::path tomlPath = e.path() / "test.toml"; - if (!fs::exists(tomlPath)) continue; - try { - TestManifest m = ParseTestManifest(tomlPath); - if (m.target) sweep.insert(*m.target); - } catch (...) { - // Parse failures surface as discovery failures during the - // actual run; the sweep phase just collects targets. - } - } + for (const Test& t : projectCfg.tests) { + if (!t.config.target.empty()) sweep.insert(t.config.target); } TestSummary aggregate; - // Inline tests pushed by the caller (fixture-driven inner RunTests - // calls, e.g. RunnerClassification) must survive each sweep - // iteration. Configuration isn't copyable so we can't snapshot+restore; - // instead, remember the inline count and erase only the entries - // appended by the previous iteration's auto-discovery. - size_t inlineCount = projectCfg.tests.size(); for (const auto& target : sweep) { RunTestsOptions perTarget = opts; perTarget.targetFilter = target; - if (projectCfg.tests.size() > inlineCount) { - projectCfg.tests.erase( - projectCfg.tests.begin() + inlineCount, - projectCfg.tests.end()); - } if (sweep.size() > 1) { Progress::Clear(); std::println("\n=== target: {} ===", target); @@ -636,152 +567,37 @@ TestSummary Crafter::RunTests(Configuration& projectCfg, const RunTestsOptions& } TestSummary summary; - std::vector discoveryFailures; - - // Auto-discover tests one layer deep: each /tests// folder - // is a test. If it contains project.cpp, that's loaded for full control; - // otherwise the Configuration is synthesized from the folder contents. - // Folders whose name starts with '_' or '.' are skipped (so tests/_shared/ - // holds cross-test code without becoming a test). Each project.cpp receives - // the same args the root project did, so --target=... propagates through. - // - // Discovery is keyed off cwd (= the project root, since crafter-build loads - // ./project.cpp), not projectCfg.path: tests live at the project root even - // when projectCfg.path points at a subdirectory like "./src/" or "./lib/". - fs::path testsDir = fs::current_path() / "tests"; - if (fs::exists(testsDir) && fs::is_directory(testsDir)) { - // A discovered fixture is one of: - // - project.cpp present → outer-driver test (LoadProject) - // - test.toml present → declarative synth + manifest - // - neither, just *.cpp → bare synth (host-target) - // - both project.cpp and test.toml → XOR violation, discovery Fail - struct TestEntry { - fs::path dir; - fs::path pcpp; // outer-driver path - std::optional manifest; - }; - std::vector entries; - for (auto& entry : fs::directory_iterator(testsDir)) { - if (!entry.is_directory()) continue; - auto stem = entry.path().filename().string(); - if (stem.empty() || stem[0] == '_' || stem[0] == '.') continue; - TestEntry te; - te.dir = entry.path(); - auto pcpp = te.dir / "project.cpp"; - auto tomlPath = te.dir / "test.toml"; - bool hasPcpp = fs::exists(pcpp); - bool hasToml = fs::exists(tomlPath); - if (hasPcpp && hasToml) { - TestResult r; - r.name = stem; - r.outcome = TestOutcome::Fail; - r.exitCode = -1; - r.output = "both project.cpp and test.toml present — they're " - "mutually exclusive (delete one to disambiguate " - "outer-driver vs declarative test)"; - discoveryFailures.push_back(std::move(r)); - continue; - } - if (hasPcpp) te.pcpp = pcpp; - if (hasToml) { - try { - te.manifest = ParseTestManifest(tomlPath); - } catch (const std::exception& e) { - TestResult r; - r.name = stem; - r.outcome = TestOutcome::Fail; - r.exitCode = -1; - r.output = e.what(); - discoveryFailures.push_back(std::move(r)); - continue; - } - } - entries.push_back(std::move(te)); - } - std::ranges::sort(entries, [](auto& a, auto& b) { return a.dir < b.dir; }); - - // Inject --target= into the args we hand each fixture so its - // CrafterBuildProject can default to the run's target. The CLI's own - // --target=... propagates through projectArgs already; this only - // appends when missing so an explicit user choice wins. - std::string targetArg = std::format("--target={}", opts.targetFilter); - std::vector fixtureArgs(projectArgs.begin(), projectArgs.end()); - bool hasTarget = std::ranges::any_of(fixtureArgs, [](std::string_view a) { - return a.starts_with("--target="); - }); - if (!hasTarget) fixtureArgs.push_back(targetArg); - - for (auto& te : entries) { - Test t; - try { - if (!te.pcpp.empty()) { - t.config = LoadProject(te.pcpp, fixtureArgs); - } else { - t.config = SynthesizeTest(te.dir); - if (te.manifest) { - ApplyManifest(t.config, *te.manifest); - if (te.manifest->timeoutSeconds) { - t.timeout = std::chrono::seconds(*te.manifest->timeoutSeconds); - } - t.args = te.manifest->args; - t.requires_ = te.manifest->requires_; - } - } - } catch (const std::exception& e) { - // A broken fixture shouldn't kill the whole run. Surface as a - // Fail and let other tests proceed. - TestResult r; - r.name = te.dir.filename().string(); - r.outcome = TestOutcome::Fail; - r.exitCode = -1; - r.output = !te.pcpp.empty() - ? std::format("project.cpp failed to load: {}", e.what()) - : std::format("test discovery failed: {}", e.what()); - discoveryFailures.push_back(std::move(r)); - continue; - } - if (t.config.target != opts.targetFilter) continue; - t.runner = TestRunner::FromEnv(t.config.target, TestRunner::ForTarget(t.config)); - if (opts.runnerOverride) { - if (auto r = TestRunner::FromSpec(*opts.runnerOverride)) { - t.runner = std::move(*r); - } - } - projectCfg.tests.push_back(std::move(t)); - } - } + // Filter by target + glob, derive each Test's runner just-in-time. The + // runner is recomputed each sweep iteration (rather than once at AddTest + // time) because runnerOverride and FromEnv resolution depend on the + // run's options, which the project.cpp doesn't know about. std::vector filtered; filtered.reserve(projectCfg.tests.size()); for (auto& test : projectCfg.tests) { - if (MatchAny(opts.globs, test.config.name)) { - filtered.push_back(&test); + if (test.config.target != opts.targetFilter) continue; + if (!MatchAny(opts.globs, test.config.name)) continue; + test.runner = TestRunner::FromEnv(test.config.target, TestRunner::ForTarget(test.config)); + if (opts.runnerOverride) { + if (auto r = TestRunner::FromSpec(*opts.runnerOverride)) { + test.runner = std::move(*r); + } } + filtered.push_back(&test); } if (opts.listOnly) { - for (auto& r : discoveryFailures) { - std::println("{} (project.cpp broken)", r.name); - } for (auto* t : filtered) { std::println("{}", t->config.name); } return summary; } - if (filtered.empty() && discoveryFailures.empty()) { + if (filtered.empty()) { std::println("No tests matched."); return summary; } - // Render discovery failures upfront so they appear before parallel test - // results. They're already-determined Fails — no need to put them through - // the worker pool. - for (auto& r : discoveryFailures) { - PrintResult(r, ""); - WriteLog(projectCfg.path, r.name, r.output); - } - int jobs = opts.jobs > 0 ? opts.jobs : std::max(1u, std::thread::hardware_concurrency()); @@ -816,12 +632,11 @@ TestSummary Crafter::RunTests(Configuration& projectCfg, const RunTestsOptions& TestResult r; r.name = t.config.name; - // Declarative preconditions (test.toml requires = [...] or Test.requires_ - // set in project.cpp). Evaluated before the build so a missing tool/file/env - // turns into a Skip without paying the compile cost. Reports the first - // failure only — once one precondition is unmet the test couldn't run - // anyway, and a wall of "also missing X, also missing Y" buries the - // actionable root cause. + // Declarative preconditions set via TestBuilder::Requires. Evaluated + // before the build so a missing tool/file/env turns into a Skip without + // paying the compile cost. Reports the first failure only — once one + // precondition is unmet the test couldn't run anyway, and a wall of + // "also missing X, also missing Y" buries the actionable root cause. if (auto req = EvaluateRequires(t.requires_); !req.ok) { r.outcome = TestOutcome::Skipped; r.output = req.reason; @@ -846,7 +661,7 @@ TestSummary Crafter::RunTests(Configuration& projectCfg, const RunTestsOptions& r.exitCode = -1; r.output = std::format( "runner '{}' unavailable and not declared in requires " - "(add 'tool:{}' to test.toml requires to permit skipping)", + "(add .Requires(\"tool:{}\") to permit skipping)", t.runner.name, tool); } else { r.outcome = TestOutcome::Skipped; @@ -916,10 +731,6 @@ TestSummary Crafter::RunTests(Configuration& projectCfg, const RunTestsOptions& } threads.clear(); // joins all jthreads - for (auto& r : discoveryFailures) { - results.push_back(std::move(r)); - } - for (auto& r : results) { switch (r.outcome) { case TestOutcome::Pass: summary.passed++; break; diff --git a/interfaces/Crafter.Build-Clang.cppm b/interfaces/Crafter.Build-Clang.cppm index 45e8809..166734a 100644 --- a/interfaces/Crafter.Build-Clang.cppm +++ b/interfaces/Crafter.Build-Clang.cppm @@ -159,6 +159,24 @@ export namespace Crafter { std::vector linkFlags; std::vector tests; CRAFTER_API void GetInterfacesAndImplementations(std::span interfaces, std::span implementations); + // Declare a test. Sources default to `tests//main.cpp` resolved + // against this Configuration's path; target/march/mtune/debug are + // inherited from this Configuration so cross-arch projects don't have + // to re-specify. Returns a builder for chaining defines, deps, etc. + // Defined in Crafter.Build:Test. + CRAFTER_API struct TestBuilder AddTest(std::string_view name); + // Same as AddTest, but compiles the parent's `interfaces` directly + // into this test's Configuration (rather than going through a dep). + // Use when the test must rebuild those interfaces with its own + // compile flags — typically per-march SIMD codegen. + CRAFTER_API struct TestBuilder AddTest(std::string_view name, std::span interfaces); + // Math-style fan-out: one Test per MarchTier, all sharing the same + // `tests//main.cpp` source and the same interface set, each + // compiled with the tier's `-march`/`-mtune`. Test names are + // `-`. + CRAFTER_API void AddMarchVariants(std::string_view name, + std::span interfaces, + std::span tiers); // Suffix that uniquely identifies this Configuration's compile state. // target+march+mtune are spelled out for readability; the rest // (type, debug, sysroot, defines, compileFlags) collapse into a short diff --git a/interfaces/Crafter.Build-Test.cppm b/interfaces/Crafter.Build-Test.cppm index b2a340c..69c7b74 100644 --- a/interfaces/Crafter.Build-Test.cppm +++ b/interfaces/Crafter.Build-Test.cppm @@ -24,6 +24,55 @@ import std; import :Clang; export namespace Crafter { + // One row in a SIMD-march fan-out: a single Test variant compiles the + // shared source with `-march= -mtune=`. Passed by span to + // Configuration::AddMarchVariants — see Crafter.Math/project.cpp. + struct MarchTier { + std::string march; + std::string mtune; + }; + + // Fluent helper returned by Configuration::AddTest. Holds a back-pointer + // to the parent Configuration and the index of the just-pushed Test, so + // mutations survive any vector reallocation that subsequent AddTest + // calls might trigger. Each setter returns `*this` to support chaining. + // + // Note: source files (interfaces + implementations) are fixed at + // AddTest-time — pass them via the AddTest overload that takes + // interfaces. The builder only mutates compile state, deps, runtime + // args, and requires; it deliberately does NOT expose Sources() to + // avoid the double-`GetInterfacesAndImplementations` footgun (calling + // it twice would re-parse the same .cppm files into duplicate Module + // entries). + struct CRAFTER_API TestBuilder { + Configuration* parent; + std::size_t index; + + Test& test() const { return parent->tests[index]; } + + // Override the path the test's sources resolve against. Defaults to + // "./" (project root, where tests//main.cpp lives). Override + // only when the test source layout sits under a subdir. + TestBuilder& Path(std::filesystem::path p); + TestBuilder& Target(std::string t); + TestBuilder& March(std::string m); + TestBuilder& Mtune(std::string m); + TestBuilder& Sysroot(std::filesystem::path s); + TestBuilder& Debug(bool d = true); + TestBuilder& Define(std::string name, std::string value = ""); + TestBuilder& Timeout(std::chrono::seconds s); + // Replaces any previously-set args. + TestBuilder& Args(std::vector a); + // Appends one require entry ("tool:foo" / "file:..." / "env:VAR"). + TestBuilder& Requires(std::string r); + // Replaces the dependency list. Pointers must outlive the build. + TestBuilder& Dependencies(std::vector deps); + // Append a single linker flag (-l..., -L..., etc.). + TestBuilder& LinkFlag(std::string f); + // Append a single compile flag. + TestBuilder& CompileFlag(std::string f); + }; + struct RunTestsOptions { std::vector globs; int jobs = 0; diff --git a/lib/toml.hpp b/lib/toml.hpp deleted file mode 100644 index caf87c4..0000000 --- a/lib/toml.hpp +++ /dev/null @@ -1,17899 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// toml++ v3.4.0 -// https://github.com/marzer/tomlplusplus -// SPDX-License-Identifier: MIT -// -//---------------------------------------------------------------------------------------------------------------------- -// -// - THIS FILE WAS ASSEMBLED FROM MULTIPLE HEADER FILES BY A SCRIPT - PLEASE DON'T EDIT IT DIRECTLY - -// -// If you wish to submit a contribution to toml++, hooray and thanks! Before you crack on, please be aware that this -// file was assembled from a number of smaller files by a python script, and code contributions should not be made -// against it directly. You should instead make your changes in the relevant source file(s). The file names of the files -// that contributed to this header can be found at the beginnings and ends of the corresponding sections of this file. -// -//---------------------------------------------------------------------------------------------------------------------- -// -// TOML Language Specifications: -// latest: https://github.com/toml-lang/toml/blob/master/README.md -// v1.0.0: https://toml.io/en/v1.0.0 -// v0.5.0: https://toml.io/en/v0.5.0 -// changelog: https://github.com/toml-lang/toml/blob/master/CHANGELOG.md -// -//---------------------------------------------------------------------------------------------------------------------- -// -// MIT License -// -// Copyright (c) Mark Gillard -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the -// Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -//---------------------------------------------------------------------------------------------------------------------- -#ifndef TOMLPLUSPLUS_HPP -#define TOMLPLUSPLUS_HPP - -#define INCLUDE_TOMLPLUSPLUS_H // old guard name used pre-v3 -#define TOMLPLUSPLUS_H // guard name used in the legacy toml.h - -//******** impl/preprocessor.hpp ************************************************************************************* - -#ifndef __cplusplus -#error toml++ is a C++ library. -#endif - -#ifndef TOML_CPP -#ifdef _MSVC_LANG -#if _MSVC_LANG > __cplusplus -#define TOML_CPP _MSVC_LANG -#endif -#endif -#ifndef TOML_CPP -#define TOML_CPP __cplusplus -#endif -#if TOML_CPP >= 202900L -#undef TOML_CPP -#define TOML_CPP 29 -#elif TOML_CPP >= 202600L -#undef TOML_CPP -#define TOML_CPP 26 -#elif TOML_CPP >= 202302L -#undef TOML_CPP -#define TOML_CPP 23 -#elif TOML_CPP >= 202002L -#undef TOML_CPP -#define TOML_CPP 20 -#elif TOML_CPP >= 201703L -#undef TOML_CPP -#define TOML_CPP 17 -#elif TOML_CPP >= 201402L -#undef TOML_CPP -#define TOML_CPP 14 -#elif TOML_CPP >= 201103L -#undef TOML_CPP -#define TOML_CPP 11 -#else -#undef TOML_CPP -#define TOML_CPP 0 -#endif -#endif - -#if !TOML_CPP -#error toml++ requires C++17 or higher. For a pre-C++11 TOML library see https://github.com/ToruNiina/Boost.toml -#elif TOML_CPP < 17 -#error toml++ requires C++17 or higher. For a C++11 TOML library see https://github.com/ToruNiina/toml11 -#endif - -#ifndef TOML_MAKE_VERSION -#define TOML_MAKE_VERSION(major, minor, patch) (((major)*10000) + ((minor)*100) + ((patch))) -#endif - -#ifndef TOML_INTELLISENSE -#ifdef __INTELLISENSE__ -#define TOML_INTELLISENSE 1 -#else -#define TOML_INTELLISENSE 0 -#endif -#endif - -#ifndef TOML_DOXYGEN -#if defined(DOXYGEN) || defined(__DOXYGEN) || defined(__DOXYGEN__) || defined(__doxygen__) || defined(__POXY__) \ - || defined(__poxy__) -#define TOML_DOXYGEN 1 -#else -#define TOML_DOXYGEN 0 -#endif -#endif - -#ifndef TOML_CLANG -#ifdef __clang__ -#define TOML_CLANG __clang_major__ -#else -#define TOML_CLANG 0 -#endif - -// special handling for apple clang; see: -// - https://github.com/marzer/tomlplusplus/issues/189 -// - https://en.wikipedia.org/wiki/Xcode -// - -// https://stackoverflow.com/questions/19387043/how-can-i-reliably-detect-the-version-of-clang-at-preprocessing-time -#if TOML_CLANG && defined(__apple_build_version__) -#undef TOML_CLANG -#define TOML_CLANG_VERSION TOML_MAKE_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) -#if TOML_CLANG_VERSION >= TOML_MAKE_VERSION(15, 0, 0) -#define TOML_CLANG 16 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(14, 3, 0) -#define TOML_CLANG 15 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(14, 0, 0) -#define TOML_CLANG 14 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(13, 1, 6) -#define TOML_CLANG 13 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(13, 0, 0) -#define TOML_CLANG 12 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(12, 0, 5) -#define TOML_CLANG 11 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(12, 0, 0) -#define TOML_CLANG 10 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(11, 0, 3) -#define TOML_CLANG 9 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(11, 0, 0) -#define TOML_CLANG 8 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(10, 0, 1) -#define TOML_CLANG 7 -#else -#define TOML_CLANG 6 // not strictly correct but doesn't matter below this -#endif -#undef TOML_CLANG_VERSION -#endif -#endif - -#ifndef TOML_ICC -#ifdef __INTEL_COMPILER -#define TOML_ICC __INTEL_COMPILER -#ifdef __ICL -#define TOML_ICC_CL TOML_ICC -#else -#define TOML_ICC_CL 0 -#endif -#else -#define TOML_ICC 0 -#define TOML_ICC_CL 0 -#endif -#endif - -#ifndef TOML_MSVC_LIKE -#ifdef _MSC_VER -#define TOML_MSVC_LIKE _MSC_VER -#else -#define TOML_MSVC_LIKE 0 -#endif -#endif - -#ifndef TOML_MSVC -#if TOML_MSVC_LIKE && !TOML_CLANG && !TOML_ICC -#define TOML_MSVC TOML_MSVC_LIKE -#else -#define TOML_MSVC 0 -#endif -#endif - -#ifndef TOML_GCC_LIKE -#ifdef __GNUC__ -#define TOML_GCC_LIKE __GNUC__ -#else -#define TOML_GCC_LIKE 0 -#endif -#endif - -#ifndef TOML_GCC -#if TOML_GCC_LIKE && !TOML_CLANG && !TOML_ICC -#define TOML_GCC TOML_GCC_LIKE -#else -#define TOML_GCC 0 -#endif -#endif - -#ifndef TOML_CUDA -#if defined(__CUDACC__) || defined(__CUDA_ARCH__) || defined(__CUDA_LIBDEVICE__) -#define TOML_CUDA 1 -#else -#define TOML_CUDA 0 -#endif -#endif - -#ifndef TOML_NVCC -#ifdef __NVCOMPILER_MAJOR__ -#define TOML_NVCC __NVCOMPILER_MAJOR__ -#else -#define TOML_NVCC 0 -#endif -#endif - -#ifndef TOML_ARCH_ITANIUM -#if defined(__ia64__) || defined(__ia64) || defined(_IA64) || defined(__IA64__) || defined(_M_IA64) -#define TOML_ARCH_ITANIUM 1 -#define TOML_ARCH_BITNESS 64 -#else -#define TOML_ARCH_ITANIUM 0 -#endif -#endif - -#ifndef TOML_ARCH_AMD64 -#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) -#define TOML_ARCH_AMD64 1 -#define TOML_ARCH_BITNESS 64 -#else -#define TOML_ARCH_AMD64 0 -#endif -#endif - -#ifndef TOML_ARCH_X86 -#if defined(__i386__) || defined(_M_IX86) -#define TOML_ARCH_X86 1 -#define TOML_ARCH_BITNESS 32 -#else -#define TOML_ARCH_X86 0 -#endif -#endif - -#ifndef TOML_ARCH_ARM -#if defined(__aarch64__) || defined(__ARM_ARCH_ISA_A64) || defined(_M_ARM64) || defined(__ARM_64BIT_STATE) \ - || defined(_M_ARM64EC) -#define TOML_ARCH_ARM32 0 -#define TOML_ARCH_ARM64 1 -#define TOML_ARCH_ARM 1 -#define TOML_ARCH_BITNESS 64 -#elif defined(__arm__) || defined(_M_ARM) || defined(__ARM_32BIT_STATE) -#define TOML_ARCH_ARM32 1 -#define TOML_ARCH_ARM64 0 -#define TOML_ARCH_ARM 1 -#define TOML_ARCH_BITNESS 32 -#else -#define TOML_ARCH_ARM32 0 -#define TOML_ARCH_ARM64 0 -#define TOML_ARCH_ARM 0 -#endif -#endif - -#ifndef TOML_ARCH_BITNESS -#define TOML_ARCH_BITNESS 0 -#endif - -#ifndef TOML_ARCH_X64 -#if TOML_ARCH_BITNESS == 64 -#define TOML_ARCH_X64 1 -#else -#define TOML_ARCH_X64 0 -#endif -#endif - -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__CYGWIN__) -#define TOML_WINDOWS 1 -#else -#define TOML_WINDOWS 0 -#endif - -#ifdef __unix__ -#define TOML_UNIX 1 -#else -#define TOML_UNIX 0 -#endif - -#ifdef __linux__ -#define TOML_LINUX 1 -#else -#define TOML_LINUX 0 -#endif - -// TOML_HAS_INCLUDE -#ifndef TOML_HAS_INCLUDE -#ifdef __has_include -#define TOML_HAS_INCLUDE(header) __has_include(header) -#else -#define TOML_HAS_INCLUDE(header) 0 -#endif -#endif - -// TOML_HAS_BUILTIN -#ifndef TOML_HAS_BUILTIN -#ifdef __has_builtin -#define TOML_HAS_BUILTIN(name) __has_builtin(name) -#else -#define TOML_HAS_BUILTIN(name) 0 -#endif -#endif - -// TOML_HAS_FEATURE -#ifndef TOML_HAS_FEATURE -#ifdef __has_feature -#define TOML_HAS_FEATURE(name) __has_feature(name) -#else -#define TOML_HAS_FEATURE(name) 0 -#endif -#endif - -// TOML_HAS_ATTR -#ifndef TOML_HAS_ATTR -#ifdef __has_attribute -#define TOML_HAS_ATTR(attr) __has_attribute(attr) -#else -#define TOML_HAS_ATTR(attr) 0 -#endif -#endif - -// TOML_HAS_CPP_ATTR -#ifndef TOML_HAS_CPP_ATTR -#ifdef __has_cpp_attribute -#define TOML_HAS_CPP_ATTR(attr) __has_cpp_attribute(attr) -#else -#define TOML_HAS_CPP_ATTR(attr) 0 -#endif -#endif - -// TOML_ATTR (gnu attributes) -#ifndef TOML_ATTR -#if TOML_CLANG || TOML_GCC_LIKE -#define TOML_ATTR(...) __attribute__((__VA_ARGS__)) -#else -#define TOML_ATTR(...) -#endif -#endif - -// TOML_DECLSPEC (msvc attributes) -#ifndef TOML_DECLSPEC -#if TOML_MSVC_LIKE -#define TOML_DECLSPEC(...) __declspec(__VA_ARGS__) -#else -#define TOML_DECLSPEC(...) -#endif -#endif - -// TOML_COMPILER_HAS_EXCEPTIONS -#ifndef TOML_COMPILER_HAS_EXCEPTIONS -#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(__cpp_exceptions) -#define TOML_COMPILER_HAS_EXCEPTIONS 1 -#else -#define TOML_COMPILER_HAS_EXCEPTIONS 0 -#endif -#endif - -// TOML_COMPILER_HAS_RTTI -#ifndef TOML_COMPILER_HAS_RTTI -#if defined(_CPPRTTI) || defined(__GXX_RTTI) || TOML_HAS_FEATURE(cxx_rtti) -#define TOML_COMPILER_HAS_RTTI 1 -#else -#define TOML_COMPILER_HAS_RTTI 0 -#endif -#endif - -// TOML_CONCAT -#define TOML_CONCAT_1(x, y) x##y -#define TOML_CONCAT(x, y) TOML_CONCAT_1(x, y) - -// TOML_MAKE_STRING -#define TOML_MAKE_STRING_1(s) #s -#define TOML_MAKE_STRING(s) TOML_MAKE_STRING_1(s) - -// TOML_PRAGMA_XXXX (compiler-specific pragmas) -#if TOML_CLANG -#define TOML_PRAGMA_CLANG(decl) _Pragma(TOML_MAKE_STRING(clang decl)) -#else -#define TOML_PRAGMA_CLANG(decl) -#endif -#if TOML_CLANG >= 8 -#define TOML_PRAGMA_CLANG_GE_8(decl) TOML_PRAGMA_CLANG(decl) -#else -#define TOML_PRAGMA_CLANG_GE_8(decl) -#endif -#if TOML_CLANG >= 9 -#define TOML_PRAGMA_CLANG_GE_9(decl) TOML_PRAGMA_CLANG(decl) -#else -#define TOML_PRAGMA_CLANG_GE_9(decl) -#endif -#if TOML_CLANG >= 10 -#define TOML_PRAGMA_CLANG_GE_10(decl) TOML_PRAGMA_CLANG(decl) -#else -#define TOML_PRAGMA_CLANG_GE_10(decl) -#endif -#if TOML_CLANG >= 11 -#define TOML_PRAGMA_CLANG_GE_11(decl) TOML_PRAGMA_CLANG(decl) -#else -#define TOML_PRAGMA_CLANG_GE_11(decl) -#endif -#if TOML_GCC -#define TOML_PRAGMA_GCC(decl) _Pragma(TOML_MAKE_STRING(GCC decl)) -#else -#define TOML_PRAGMA_GCC(decl) -#endif -#if TOML_MSVC -#define TOML_PRAGMA_MSVC(...) __pragma(__VA_ARGS__) -#else -#define TOML_PRAGMA_MSVC(...) -#endif -#if TOML_ICC -#define TOML_PRAGMA_ICC(...) __pragma(__VA_ARGS__) -#else -#define TOML_PRAGMA_ICC(...) -#endif - -// TOML_ALWAYS_INLINE -#ifndef TOML_ALWAYS_INLINE -#ifdef _MSC_VER -#define TOML_ALWAYS_INLINE __forceinline -#elif TOML_GCC || TOML_CLANG || TOML_HAS_ATTR(__always_inline__) -#define TOML_ALWAYS_INLINE \ - TOML_ATTR(__always_inline__) \ - inline -#else -#define TOML_ALWAYS_INLINE inline -#endif -#endif - -// TOML_NEVER_INLINE -#ifndef TOML_NEVER_INLINE -#ifdef _MSC_VER -#define TOML_NEVER_INLINE TOML_DECLSPEC(noinline) -#elif TOML_CUDA // https://gitlab.gnome.org/GNOME/glib/-/issues/2555 -#define TOML_NEVER_INLINE TOML_ATTR(noinline) -#else -#if TOML_GCC || TOML_CLANG || TOML_HAS_ATTR(__noinline__) -#define TOML_NEVER_INLINE TOML_ATTR(__noinline__) -#endif -#endif -#ifndef TOML_NEVER_INLINE -#define TOML_NEVER_INLINE -#endif -#endif - -// MSVC attributes -#ifndef TOML_ABSTRACT_INTERFACE -#define TOML_ABSTRACT_INTERFACE TOML_DECLSPEC(novtable) -#endif -#ifndef TOML_EMPTY_BASES -#define TOML_EMPTY_BASES TOML_DECLSPEC(empty_bases) -#endif - -// TOML_TRIVIAL_ABI -#ifndef TOML_TRIVIAL_ABI -#if TOML_CLANG || TOML_HAS_ATTR(__trivial_abi__) -#define TOML_TRIVIAL_ABI TOML_ATTR(__trivial_abi__) -#else -#define TOML_TRIVIAL_ABI -#endif -#endif - -// TOML_NODISCARD -#ifndef TOML_NODISCARD -#if TOML_CPP >= 17 && TOML_HAS_CPP_ATTR(nodiscard) >= 201603 -#define TOML_NODISCARD [[nodiscard]] -#elif TOML_CLANG || TOML_GCC || TOML_HAS_ATTR(__warn_unused_result__) -#define TOML_NODISCARD TOML_ATTR(__warn_unused_result__) -#else -#define TOML_NODISCARD -#endif -#endif - -// TOML_NODISCARD_CTOR -#ifndef TOML_NODISCARD_CTOR -#if TOML_CPP >= 17 && TOML_HAS_CPP_ATTR(nodiscard) >= 201907 -#define TOML_NODISCARD_CTOR [[nodiscard]] -#else -#define TOML_NODISCARD_CTOR -#endif -#endif - -// pure + const -#ifndef TOML_PURE -#ifdef NDEBUG -#define TOML_PURE \ - TOML_DECLSPEC(noalias) \ - TOML_ATTR(pure) -#else -#define TOML_PURE -#endif -#endif -#ifndef TOML_CONST -#ifdef NDEBUG -#define TOML_CONST \ - TOML_DECLSPEC(noalias) \ - TOML_ATTR(const) -#else -#define TOML_CONST -#endif -#endif -#ifndef TOML_INLINE_GETTER -#define TOML_INLINE_GETTER \ - TOML_NODISCARD \ - TOML_ALWAYS_INLINE -#endif -#ifndef TOML_PURE_GETTER -#define TOML_PURE_GETTER \ - TOML_NODISCARD \ - TOML_PURE -#endif -#ifndef TOML_PURE_INLINE_GETTER -#define TOML_PURE_INLINE_GETTER \ - TOML_NODISCARD \ - TOML_ALWAYS_INLINE \ - TOML_PURE -#endif -#ifndef TOML_CONST_GETTER -#define TOML_CONST_GETTER \ - TOML_NODISCARD \ - TOML_CONST -#endif -#ifndef TOML_CONST_INLINE_GETTER -#define TOML_CONST_INLINE_GETTER \ - TOML_NODISCARD \ - TOML_ALWAYS_INLINE \ - TOML_CONST -#endif - -// TOML_ASSUME -#ifndef TOML_ASSUME -#ifdef _MSC_VER -#define TOML_ASSUME(expr) __assume(expr) -#elif TOML_ICC || TOML_CLANG || TOML_HAS_BUILTIN(__builtin_assume) -#define TOML_ASSUME(expr) __builtin_assume(expr) -#elif TOML_HAS_CPP_ATTR(assume) >= 202207 -#define TOML_ASSUME(expr) [[assume(expr)]] -#elif TOML_HAS_ATTR(__assume__) -#define TOML_ASSUME(expr) __attribute__((__assume__(expr))) -#else -#define TOML_ASSUME(expr) static_cast(0) -#endif -#endif - -// TOML_UNREACHABLE -#ifndef TOML_UNREACHABLE -#ifdef _MSC_VER -#define TOML_UNREACHABLE __assume(0) -#elif TOML_ICC || TOML_CLANG || TOML_GCC || TOML_HAS_BUILTIN(__builtin_unreachable) -#define TOML_UNREACHABLE __builtin_unreachable() -#else -#define TOML_UNREACHABLE static_cast(0) -#endif -#endif - -// TOML_LIKELY -#if TOML_CPP >= 20 && TOML_HAS_CPP_ATTR(likely) >= 201803 -#define TOML_LIKELY(...) (__VA_ARGS__) [[likely]] -#define TOML_LIKELY_CASE [[likely]] -#elif TOML_GCC || TOML_CLANG || TOML_HAS_BUILTIN(__builtin_expect) -#define TOML_LIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 1)) -#else -#define TOML_LIKELY(...) (__VA_ARGS__) -#endif -#ifndef TOML_LIKELY_CASE -#define TOML_LIKELY_CASE -#endif - -// TOML_UNLIKELY -#if TOML_CPP >= 20 && TOML_HAS_CPP_ATTR(unlikely) >= 201803 -#define TOML_UNLIKELY(...) (__VA_ARGS__) [[unlikely]] -#define TOML_UNLIKELY_CASE [[unlikely]] -#elif TOML_GCC || TOML_CLANG || TOML_HAS_BUILTIN(__builtin_expect) -#define TOML_UNLIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 0)) -#else -#define TOML_UNLIKELY(...) (__VA_ARGS__) -#endif -#ifndef TOML_UNLIKELY_CASE -#define TOML_UNLIKELY_CASE -#endif - -// TOML_FLAGS_ENUM -#if TOML_CLANG || TOML_HAS_ATTR(flag_enum) -#define TOML_FLAGS_ENUM __attribute__((flag_enum)) -#else -#define TOML_FLAGS_ENUM -#endif - -// TOML_OPEN_ENUM + TOML_CLOSED_ENUM -#if TOML_CLANG || TOML_HAS_ATTR(enum_extensibility) -#define TOML_OPEN_ENUM __attribute__((enum_extensibility(open))) -#define TOML_CLOSED_ENUM __attribute__((enum_extensibility(closed))) -#else -#define TOML_OPEN_ENUM -#define TOML_CLOSED_ENUM -#endif - -// TOML_OPEN_FLAGS_ENUM + TOML_CLOSED_FLAGS_ENUM -#define TOML_OPEN_FLAGS_ENUM TOML_OPEN_ENUM TOML_FLAGS_ENUM -#define TOML_CLOSED_FLAGS_ENUM TOML_CLOSED_ENUM TOML_FLAGS_ENUM - -// TOML_MAKE_FLAGS -#define TOML_MAKE_FLAGS_2(T, op, linkage) \ - TOML_CONST_INLINE_GETTER \ - linkage constexpr T operator op(T lhs, T rhs) noexcept \ - { \ - using under = std::underlying_type_t; \ - return static_cast(static_cast(lhs) op static_cast(rhs)); \ - } \ - \ - linkage constexpr T& operator TOML_CONCAT(op, =)(T & lhs, T rhs) noexcept \ - { \ - return lhs = (lhs op rhs); \ - } \ - \ - static_assert(true) -#define TOML_MAKE_FLAGS_1(T, linkage) \ - static_assert(std::is_enum_v); \ - \ - TOML_MAKE_FLAGS_2(T, &, linkage); \ - TOML_MAKE_FLAGS_2(T, |, linkage); \ - TOML_MAKE_FLAGS_2(T, ^, linkage); \ - \ - TOML_CONST_INLINE_GETTER \ - linkage constexpr T operator~(T val) noexcept \ - { \ - using under = std::underlying_type_t; \ - return static_cast(~static_cast(val)); \ - } \ - \ - TOML_CONST_INLINE_GETTER \ - linkage constexpr bool operator!(T val) noexcept \ - { \ - using under = std::underlying_type_t; \ - return !static_cast(val); \ - } \ - \ - static_assert(true) -#define TOML_MAKE_FLAGS(T) TOML_MAKE_FLAGS_1(T, ) - -#define TOML_UNUSED(...) static_cast(__VA_ARGS__) - -#define TOML_DELETE_DEFAULTS(T) \ - T(const T&) = delete; \ - T(T&&) = delete; \ - T& operator=(const T&) = delete; \ - T& operator=(T&&) = delete - -#define TOML_ASYMMETRICAL_EQUALITY_OPS(LHS, RHS, ...) \ - __VA_ARGS__ TOML_NODISCARD \ - friend bool operator==(RHS rhs, LHS lhs) noexcept \ - { \ - return lhs == rhs; \ - } \ - __VA_ARGS__ TOML_NODISCARD \ - friend bool operator!=(LHS lhs, RHS rhs) noexcept \ - { \ - return !(lhs == rhs); \ - } \ - __VA_ARGS__ TOML_NODISCARD \ - friend bool operator!=(RHS rhs, LHS lhs) noexcept \ - { \ - return !(lhs == rhs); \ - } \ - static_assert(true) - -#define TOML_EVAL_BOOL_1(T, F) T -#define TOML_EVAL_BOOL_0(T, F) F - -#if !defined(__POXY__) && !defined(POXY_IMPLEMENTATION_DETAIL) -#define POXY_IMPLEMENTATION_DETAIL(...) __VA_ARGS__ -#endif - -// COMPILER-SPECIFIC WARNING MANAGEMENT - -#if TOML_CLANG - -#define TOML_PUSH_WARNINGS \ - TOML_PRAGMA_CLANG(diagnostic push) \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wunknown-warning-option") \ - static_assert(true) - -#define TOML_DISABLE_SWITCH_WARNINGS \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wswitch") \ - static_assert(true) - -#define TOML_DISABLE_ARITHMETIC_WARNINGS \ - TOML_PRAGMA_CLANG_GE_10(diagnostic ignored "-Wimplicit-int-float-conversion") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wfloat-equal") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wdouble-promotion") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wchar-subscripts") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wshift-sign-overflow") \ - static_assert(true) - -#define TOML_DISABLE_SPAM_WARNINGS \ - TOML_PRAGMA_CLANG_GE_8(diagnostic ignored "-Wdefaulted-function-deleted") \ - TOML_PRAGMA_CLANG_GE_9(diagnostic ignored "-Wctad-maybe-unsupported") \ - TOML_PRAGMA_CLANG_GE_10(diagnostic ignored "-Wzero-as-null-pointer-constant") \ - TOML_PRAGMA_CLANG_GE_11(diagnostic ignored "-Wsuggest-destructor-override") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wweak-vtables") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wweak-template-vtables") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wdouble-promotion") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wchar-subscripts") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wmissing-field-initializers") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wpadded") \ - static_assert(true) - -#define TOML_POP_WARNINGS \ - TOML_PRAGMA_CLANG(diagnostic pop) \ - static_assert(true) - -#define TOML_DISABLE_WARNINGS \ - TOML_PRAGMA_CLANG(diagnostic push) \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Weverything") \ - static_assert(true, "") - -#define TOML_ENABLE_WARNINGS \ - TOML_PRAGMA_CLANG(diagnostic pop) \ - static_assert(true) - -#define TOML_SIMPLE_STATIC_ASSERT_MESSAGES 1 - -#elif TOML_MSVC - -#define TOML_PUSH_WARNINGS \ - __pragma(warning(push)) \ - static_assert(true) - -#if TOML_HAS_INCLUDE() -#pragma warning(push, 0) -#include -#pragma warning(pop) -#define TOML_DISABLE_CODE_ANALYSIS_WARNINGS \ - __pragma(warning(disable : ALL_CODE_ANALYSIS_WARNINGS)) \ - static_assert(true) -#else -#define TOML_DISABLE_CODE_ANALYSIS_WARNINGS static_assert(true) -#endif - -#define TOML_DISABLE_SWITCH_WARNINGS \ - __pragma(warning(disable : 4061)) \ - __pragma(warning(disable : 4062)) \ - __pragma(warning(disable : 4063)) \ - __pragma(warning(disable : 5262)) /* switch-case implicit fallthrough (false-positive) */ \ - __pragma(warning(disable : 26819)) /* cg: unannotated fallthrough */ \ - static_assert(true) - -#define TOML_DISABLE_SPAM_WARNINGS \ - __pragma(warning(disable : 4127)) /* conditional expr is constant */ \ - __pragma(warning(disable : 4324)) /* structure was padded due to alignment specifier */ \ - __pragma(warning(disable : 4348)) \ - __pragma(warning(disable : 4464)) /* relative include path contains '..' */ \ - __pragma(warning(disable : 4505)) /* unreferenced local function removed */ \ - __pragma(warning(disable : 4514)) /* unreferenced inline function has been removed */ \ - __pragma(warning(disable : 4582)) /* constructor is not implicitly called */ \ - __pragma(warning(disable : 4619)) /* there is no warning number 'XXXX' */ \ - __pragma(warning(disable : 4623)) /* default constructor was implicitly defined as deleted */ \ - __pragma(warning(disable : 4625)) /* copy constructor was implicitly defined as deleted */ \ - __pragma(warning(disable : 4626)) /* assignment operator was implicitly defined as deleted */ \ - __pragma(warning(disable : 4710)) /* function not inlined */ \ - __pragma(warning(disable : 4711)) /* function selected for automatic expansion */ \ - __pragma(warning(disable : 4820)) /* N bytes padding added */ \ - __pragma(warning(disable : 5026)) /* move constructor was implicitly defined as deleted */ \ - __pragma(warning(disable : 5027)) /* move assignment operator was implicitly defined as deleted */ \ - __pragma(warning(disable : 5039)) /* potentially throwing function passed to 'extern "C"' function */ \ - __pragma(warning(disable : 5045)) /* Compiler will insert Spectre mitigation */ \ - __pragma(warning(disable : 5264)) /* const variable is not used (false-positive) */ \ - __pragma(warning(disable : 26451)) \ - __pragma(warning(disable : 26490)) \ - __pragma(warning(disable : 26495)) \ - __pragma(warning(disable : 26812)) \ - __pragma(warning(disable : 26819)) \ - static_assert(true) - -#define TOML_DISABLE_ARITHMETIC_WARNINGS \ - __pragma(warning(disable : 4365)) /* argument signed/unsigned mismatch */ \ - __pragma(warning(disable : 4738)) /* storing 32-bit float result in memory */ \ - __pragma(warning(disable : 5219)) /* implicit conversion from integral to float */ \ - static_assert(true) - -#define TOML_POP_WARNINGS \ - __pragma(warning(pop)) \ - static_assert(true) - -#define TOML_DISABLE_WARNINGS \ - __pragma(warning(push, 0)) \ - __pragma(warning(disable : 4348)) \ - __pragma(warning(disable : 4668)) \ - __pragma(warning(disable : 5105)) \ - __pragma(warning(disable : 5264)) \ - TOML_DISABLE_CODE_ANALYSIS_WARNINGS; \ - TOML_DISABLE_SWITCH_WARNINGS; \ - TOML_DISABLE_SPAM_WARNINGS; \ - TOML_DISABLE_ARITHMETIC_WARNINGS; \ - static_assert(true) - -#define TOML_ENABLE_WARNINGS TOML_POP_WARNINGS - -#elif TOML_ICC - -#define TOML_PUSH_WARNINGS \ - __pragma(warning(push)) \ - static_assert(true) - -#define TOML_DISABLE_SPAM_WARNINGS \ - __pragma(warning(disable : 82)) /* storage class is not first */ \ - __pragma(warning(disable : 111)) /* statement unreachable (false-positive) */ \ - __pragma(warning(disable : 869)) /* unreferenced parameter */ \ - __pragma(warning(disable : 1011)) /* missing return (false-positive) */ \ - __pragma(warning(disable : 2261)) /* assume expr side-effects discarded */ \ - static_assert(true) - -#define TOML_POP_WARNINGS \ - __pragma(warning(pop)) \ - static_assert(true) - -#define TOML_DISABLE_WARNINGS \ - __pragma(warning(push, 0)) \ - TOML_DISABLE_SPAM_WARNINGS - -#define TOML_ENABLE_WARNINGS \ - __pragma(warning(pop)) \ - static_assert(true) - -#elif TOML_GCC - -#define TOML_PUSH_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic push) \ - static_assert(true) - -#define TOML_DISABLE_SWITCH_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wswitch") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wswitch-enum") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wswitch-default") \ - static_assert(true) - -#define TOML_DISABLE_ARITHMETIC_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wfloat-equal") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wsign-conversion") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wchar-subscripts") \ - static_assert(true) - -#define TOML_DISABLE_SUGGEST_ATTR_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wsuggest-attribute=const") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wsuggest-attribute=pure") \ - static_assert(true) - -#define TOML_DISABLE_SPAM_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wpadded") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wcast-align") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wcomment") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wtype-limits") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wuseless-cast") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wchar-subscripts") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wsubobject-linkage") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wmissing-field-initializers") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wmaybe-uninitialized") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wnoexcept") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wnull-dereference") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wduplicated-branches") \ - static_assert(true) - -#define TOML_POP_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic pop) \ - static_assert(true) - -#define TOML_DISABLE_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic push) \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wall") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wextra") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wpedantic") \ - TOML_DISABLE_SWITCH_WARNINGS; \ - TOML_DISABLE_ARITHMETIC_WARNINGS; \ - TOML_DISABLE_SUGGEST_ATTR_WARNINGS; \ - TOML_DISABLE_SPAM_WARNINGS; \ - static_assert(true) - -#define TOML_ENABLE_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic pop) \ - static_assert(true) - -#endif - -#ifndef TOML_PUSH_WARNINGS -#define TOML_PUSH_WARNINGS static_assert(true) -#endif -#ifndef TOML_DISABLE_CODE_ANALYSIS_WARNINGS -#define TOML_DISABLE_CODE_ANALYSIS_WARNINGS static_assert(true) -#endif -#ifndef TOML_DISABLE_SWITCH_WARNINGS -#define TOML_DISABLE_SWITCH_WARNINGS static_assert(true) -#endif -#ifndef TOML_DISABLE_SUGGEST_ATTR_WARNINGS -#define TOML_DISABLE_SUGGEST_ATTR_WARNINGS static_assert(true) -#endif -#ifndef TOML_DISABLE_SPAM_WARNINGS -#define TOML_DISABLE_SPAM_WARNINGS static_assert(true) -#endif -#ifndef TOML_DISABLE_ARITHMETIC_WARNINGS -#define TOML_DISABLE_ARITHMETIC_WARNINGS static_assert(true) -#endif -#ifndef TOML_POP_WARNINGS -#define TOML_POP_WARNINGS static_assert(true) -#endif -#ifndef TOML_DISABLE_WARNINGS -#define TOML_DISABLE_WARNINGS static_assert(true) -#endif -#ifndef TOML_ENABLE_WARNINGS -#define TOML_ENABLE_WARNINGS static_assert(true) -#endif -#ifndef TOML_SIMPLE_STATIC_ASSERT_MESSAGES -#define TOML_SIMPLE_STATIC_ASSERT_MESSAGES 0 -#endif - -#ifdef TOML_CONFIG_HEADER -#include TOML_CONFIG_HEADER -#endif - -// is the library being built as a shared lib/dll using meson and friends? -#ifndef TOML_SHARED_LIB -#define TOML_SHARED_LIB 0 -#endif - -// header-only mode -#if !defined(TOML_HEADER_ONLY) && defined(TOML_ALL_INLINE) // was TOML_ALL_INLINE pre-2.0 -#define TOML_HEADER_ONLY TOML_ALL_INLINE -#endif -#if !defined(TOML_HEADER_ONLY) || (defined(TOML_HEADER_ONLY) && TOML_HEADER_ONLY) || TOML_INTELLISENSE -#undef TOML_HEADER_ONLY -#define TOML_HEADER_ONLY 1 -#endif -#if TOML_DOXYGEN || TOML_SHARED_LIB -#undef TOML_HEADER_ONLY -#define TOML_HEADER_ONLY 0 -#endif - -// internal implementation switch -#if defined(TOML_IMPLEMENTATION) || TOML_HEADER_ONLY -#undef TOML_IMPLEMENTATION -#define TOML_IMPLEMENTATION 1 -#else -#define TOML_IMPLEMENTATION 0 -#endif - -// dll/shared lib function exports (legacy - TOML_API was the old name for this setting) -#if !defined(TOML_EXPORTED_MEMBER_FUNCTION) && !defined(TOML_EXPORTED_STATIC_FUNCTION) \ - && !defined(TOML_EXPORTED_FREE_FUNCTION) && !defined(TOML_EXPORTED_CLASS) && defined(TOML_API) -#define TOML_EXPORTED_MEMBER_FUNCTION TOML_API -#define TOML_EXPORTED_STATIC_FUNCTION TOML_API -#define TOML_EXPORTED_FREE_FUNCTION TOML_API -#endif - -// dll/shared lib exports -#if TOML_SHARED_LIB -#undef TOML_API -#undef TOML_EXPORTED_CLASS -#undef TOML_EXPORTED_MEMBER_FUNCTION -#undef TOML_EXPORTED_STATIC_FUNCTION -#undef TOML_EXPORTED_FREE_FUNCTION -#if TOML_WINDOWS -#if TOML_IMPLEMENTATION -#define TOML_EXPORTED_CLASS __declspec(dllexport) -#define TOML_EXPORTED_FREE_FUNCTION __declspec(dllexport) -#else -#define TOML_EXPORTED_CLASS __declspec(dllimport) -#define TOML_EXPORTED_FREE_FUNCTION __declspec(dllimport) -#endif -#ifndef TOML_CALLCONV -#define TOML_CALLCONV __cdecl -#endif -#elif defined(__GNUC__) && __GNUC__ >= 4 -#define TOML_EXPORTED_CLASS __attribute__((visibility("default"))) -#define TOML_EXPORTED_MEMBER_FUNCTION __attribute__((visibility("default"))) -#define TOML_EXPORTED_STATIC_FUNCTION __attribute__((visibility("default"))) -#define TOML_EXPORTED_FREE_FUNCTION __attribute__((visibility("default"))) -#endif -#endif -#ifndef TOML_EXPORTED_CLASS -#define TOML_EXPORTED_CLASS -#endif -#ifndef TOML_EXPORTED_MEMBER_FUNCTION -#define TOML_EXPORTED_MEMBER_FUNCTION -#endif -#ifndef TOML_EXPORTED_STATIC_FUNCTION -#define TOML_EXPORTED_STATIC_FUNCTION -#endif -#ifndef TOML_EXPORTED_FREE_FUNCTION -#define TOML_EXPORTED_FREE_FUNCTION -#endif - -// experimental language features -#if !defined(TOML_ENABLE_UNRELEASED_FEATURES) && defined(TOML_UNRELEASED_FEATURES) // was TOML_UNRELEASED_FEATURES - // pre-3.0 -#define TOML_ENABLE_UNRELEASED_FEATURES TOML_UNRELEASED_FEATURES -#endif -#if (defined(TOML_ENABLE_UNRELEASED_FEATURES) && TOML_ENABLE_UNRELEASED_FEATURES) || TOML_INTELLISENSE -#undef TOML_ENABLE_UNRELEASED_FEATURES -#define TOML_ENABLE_UNRELEASED_FEATURES 1 -#endif -#ifndef TOML_ENABLE_UNRELEASED_FEATURES -#define TOML_ENABLE_UNRELEASED_FEATURES 0 -#endif - -// parser -#if !defined(TOML_ENABLE_PARSER) && defined(TOML_PARSER) // was TOML_PARSER pre-3.0 -#define TOML_ENABLE_PARSER TOML_PARSER -#endif -#if !defined(TOML_ENABLE_PARSER) || (defined(TOML_ENABLE_PARSER) && TOML_ENABLE_PARSER) || TOML_INTELLISENSE -#undef TOML_ENABLE_PARSER -#define TOML_ENABLE_PARSER 1 -#endif - -// formatters -#if !defined(TOML_ENABLE_FORMATTERS) || (defined(TOML_ENABLE_FORMATTERS) && TOML_ENABLE_FORMATTERS) || TOML_INTELLISENSE -#undef TOML_ENABLE_FORMATTERS -#define TOML_ENABLE_FORMATTERS 1 -#endif - -// SIMD -#if !defined(TOML_ENABLE_SIMD) || (defined(TOML_ENABLE_SIMD) && TOML_ENABLE_SIMD) || TOML_INTELLISENSE -#undef TOML_ENABLE_SIMD -#define TOML_ENABLE_SIMD 1 -#endif - -// windows compat -#if !defined(TOML_ENABLE_WINDOWS_COMPAT) && defined(TOML_WINDOWS_COMPAT) // was TOML_WINDOWS_COMPAT pre-3.0 -#define TOML_ENABLE_WINDOWS_COMPAT TOML_WINDOWS_COMPAT -#endif -#if !defined(TOML_ENABLE_WINDOWS_COMPAT) || (defined(TOML_ENABLE_WINDOWS_COMPAT) && TOML_ENABLE_WINDOWS_COMPAT) \ - || TOML_INTELLISENSE -#undef TOML_ENABLE_WINDOWS_COMPAT -#define TOML_ENABLE_WINDOWS_COMPAT 1 -#endif - -#if !TOML_WINDOWS -#undef TOML_ENABLE_WINDOWS_COMPAT -#define TOML_ENABLE_WINDOWS_COMPAT 0 -#endif - -#ifndef TOML_INCLUDE_WINDOWS_H -#define TOML_INCLUDE_WINDOWS_H 0 -#endif - -// custom optional -#ifdef TOML_OPTIONAL_TYPE -#define TOML_HAS_CUSTOM_OPTIONAL_TYPE 1 -#else -#define TOML_HAS_CUSTOM_OPTIONAL_TYPE 0 -#endif - -// exceptions (library use) -#if TOML_COMPILER_HAS_EXCEPTIONS -#if !defined(TOML_EXCEPTIONS) || (defined(TOML_EXCEPTIONS) && TOML_EXCEPTIONS) -#undef TOML_EXCEPTIONS -#define TOML_EXCEPTIONS 1 -#endif -#else -#if defined(TOML_EXCEPTIONS) && TOML_EXCEPTIONS -#error TOML_EXCEPTIONS was explicitly enabled but exceptions are disabled/unsupported by the compiler. -#endif -#undef TOML_EXCEPTIONS -#define TOML_EXCEPTIONS 0 -#endif - -// calling convention for static/free/friend functions -#ifndef TOML_CALLCONV -#define TOML_CALLCONV -#endif - -#ifndef TOML_UNDEF_MACROS -#define TOML_UNDEF_MACROS 1 -#endif - -#ifndef TOML_MAX_NESTED_VALUES -#define TOML_MAX_NESTED_VALUES 128 -// this refers to the depth of nested values, e.g. inline tables and arrays. -// 128 is very generous; real TOML files rarely exceed single-digit nesting. -// keep this value low enough to avoid stack overflows in sanitizer-instrumented builds -// where each recursion cycle may consume ~3KB of stack. -#endif - -#ifndef TOML_MAX_DOTTED_KEYS_DEPTH -#define TOML_MAX_DOTTED_KEYS_DEPTH 1024 -#endif - -#ifdef TOML_CHAR_8_STRINGS -#if TOML_CHAR_8_STRINGS -#error TOML_CHAR_8_STRINGS was removed in toml++ 2.0.0; all value setters and getters now work with char8_t strings implicitly. -#endif -#endif - -#ifdef TOML_LARGE_FILES -#if !TOML_LARGE_FILES -#error Support for !TOML_LARGE_FILES (i.e. 'small files') was removed in toml++ 3.0.0. -#endif -#endif - -#ifndef TOML_LIFETIME_HOOKS -#define TOML_LIFETIME_HOOKS 0 -#endif - -#ifdef NDEBUG -#undef TOML_ASSERT -#define TOML_ASSERT(expr) static_assert(true) -#endif -#ifndef TOML_ASSERT -#ifndef assert -TOML_DISABLE_WARNINGS; -#include -TOML_ENABLE_WARNINGS; -#endif -#define TOML_ASSERT(expr) assert(expr) -#endif -#ifdef NDEBUG -#define TOML_ASSERT_ASSUME(expr) TOML_ASSUME(expr) -#else -#define TOML_ASSERT_ASSUME(expr) TOML_ASSERT(expr) -#endif - -#ifndef TOML_ENABLE_FLOAT16 -#define TOML_ENABLE_FLOAT16 0 -#endif - -#ifndef TOML_DISABLE_CONDITIONAL_NOEXCEPT_LAMBDA -#define TOML_DISABLE_CONDITIONAL_NOEXCEPT_LAMBDA 0 -#endif - -#ifndef TOML_DISABLE_NOEXCEPT_NOEXCEPT -#define TOML_DISABLE_NOEXCEPT_NOEXCEPT 0 - #ifdef _MSC_VER - #if _MSC_VER <= 1943 // Up to Visual Studio 2022 Version 17.13.6 - #undef TOML_DISABLE_NOEXCEPT_NOEXCEPT - #define TOML_DISABLE_NOEXCEPT_NOEXCEPT 1 - #endif - #endif -#endif - -#if !defined(TOML_FLOAT_CHARCONV) && (TOML_GCC || TOML_CLANG || (TOML_ICC && !TOML_ICC_CL)) -// not supported by any version of GCC or Clang as of 26/11/2020 -// not supported by any version of ICC on Linux as of 11/01/2021 -#define TOML_FLOAT_CHARCONV 0 -#endif -#if !defined(TOML_INT_CHARCONV) && (defined(__EMSCRIPTEN__) || defined(__APPLE__)) -// causes link errors on emscripten -// causes Mac OS SDK version errors on some versions of Apple Clang -#define TOML_INT_CHARCONV 0 -#endif -#ifndef TOML_INT_CHARCONV -#define TOML_INT_CHARCONV 1 -#endif -#ifndef TOML_FLOAT_CHARCONV -#define TOML_FLOAT_CHARCONV 1 -#endif -#if (TOML_INT_CHARCONV || TOML_FLOAT_CHARCONV) && !TOML_HAS_INCLUDE() -#undef TOML_INT_CHARCONV -#undef TOML_FLOAT_CHARCONV -#define TOML_INT_CHARCONV 0 -#define TOML_FLOAT_CHARCONV 0 -#endif - -#if defined(__cpp_concepts) && __cpp_concepts >= 201907 -#define TOML_REQUIRES(...) requires(__VA_ARGS__) -#else -#define TOML_REQUIRES(...) -#endif -#define TOML_ENABLE_IF(...) , typename std::enable_if<(__VA_ARGS__), int>::type = 0 -#define TOML_CONSTRAINED_TEMPLATE(condition, ...) \ - template <__VA_ARGS__ TOML_ENABLE_IF(condition)> \ - TOML_REQUIRES(condition) -#define TOML_HIDDEN_CONSTRAINT(condition, ...) TOML_CONSTRAINED_TEMPLATE(condition, __VA_ARGS__) - -#if defined(__SIZEOF_FLOAT128__) && defined(__FLT128_MANT_DIG__) && defined(__LDBL_MANT_DIG__) \ - && __FLT128_MANT_DIG__ > __LDBL_MANT_DIG__ -#define TOML_FLOAT128 __float128 -#endif - -#ifdef __SIZEOF_INT128__ -#define TOML_INT128 __int128_t -#define TOML_UINT128 __uint128_t -#endif - -// clang-format off - -//******** impl/version.hpp ****************************************************************************************** - -#define TOML_LIB_MAJOR 3 -#define TOML_LIB_MINOR 4 -#define TOML_LIB_PATCH 0 - -#define TOML_LANG_MAJOR 1 -#define TOML_LANG_MINOR 0 -#define TOML_LANG_PATCH 0 - -//******** impl/preprocessor.hpp ************************************************************************************* - -#define TOML_LIB_SINGLE_HEADER 1 - -#if TOML_ENABLE_UNRELEASED_FEATURES - #define TOML_LANG_EFFECTIVE_VERSION \ - TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH+1) -#else - #define TOML_LANG_EFFECTIVE_VERSION \ - TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH) -#endif - -#define TOML_LANG_HIGHER_THAN(major, minor, patch) \ - (TOML_LANG_EFFECTIVE_VERSION > TOML_MAKE_VERSION(major, minor, patch)) - -#define TOML_LANG_AT_LEAST(major, minor, patch) \ - (TOML_LANG_EFFECTIVE_VERSION >= TOML_MAKE_VERSION(major, minor, patch)) - -#define TOML_LANG_UNRELEASED \ - TOML_LANG_HIGHER_THAN(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH) - -#ifndef TOML_ABI_NAMESPACES - #if TOML_DOXYGEN - #define TOML_ABI_NAMESPACES 0 - #else - #define TOML_ABI_NAMESPACES 1 - #endif -#endif -#if TOML_ABI_NAMESPACES - #define TOML_NAMESPACE_START namespace toml { inline namespace TOML_CONCAT(v, TOML_LIB_MAJOR) - #define TOML_NAMESPACE_END } static_assert(true) - #define TOML_NAMESPACE ::toml::TOML_CONCAT(v, TOML_LIB_MAJOR) - #define TOML_ABI_NAMESPACE_START(name) inline namespace name { static_assert(true) - #define TOML_ABI_NAMESPACE_BOOL(cond, T, F) TOML_ABI_NAMESPACE_START(TOML_CONCAT(TOML_EVAL_BOOL_, cond)(T, F)) - #define TOML_ABI_NAMESPACE_END } static_assert(true) -#else - #define TOML_NAMESPACE_START namespace toml - #define TOML_NAMESPACE_END static_assert(true) - #define TOML_NAMESPACE toml - #define TOML_ABI_NAMESPACE_START(...) static_assert(true) - #define TOML_ABI_NAMESPACE_BOOL(...) static_assert(true) - #define TOML_ABI_NAMESPACE_END static_assert(true) -#endif -#define TOML_IMPL_NAMESPACE_START TOML_NAMESPACE_START { namespace impl -#define TOML_IMPL_NAMESPACE_END } TOML_NAMESPACE_END -#if TOML_HEADER_ONLY - #define TOML_ANON_NAMESPACE_START static_assert(TOML_IMPLEMENTATION); TOML_IMPL_NAMESPACE_START - #define TOML_ANON_NAMESPACE_END TOML_IMPL_NAMESPACE_END - #define TOML_ANON_NAMESPACE TOML_NAMESPACE::impl - #define TOML_EXTERNAL_LINKAGE inline - #define TOML_INTERNAL_LINKAGE inline -#else - #define TOML_ANON_NAMESPACE_START static_assert(TOML_IMPLEMENTATION); \ - using namespace toml; \ - namespace - #define TOML_ANON_NAMESPACE_END static_assert(true) - #define TOML_ANON_NAMESPACE - #define TOML_EXTERNAL_LINKAGE - #define TOML_INTERNAL_LINKAGE static -#endif - -// clang-format on - -// clang-format off - -#if TOML_SIMPLE_STATIC_ASSERT_MESSAGES - - #define TOML_SA_NEWLINE " " - #define TOML_SA_LIST_SEP ", " - #define TOML_SA_LIST_BEG " (" - #define TOML_SA_LIST_END ")" - #define TOML_SA_LIST_NEW " " - #define TOML_SA_LIST_NXT ", " - -#else - - #define TOML_SA_NEWLINE "\n| " - #define TOML_SA_LIST_SEP TOML_SA_NEWLINE " - " - #define TOML_SA_LIST_BEG TOML_SA_LIST_SEP - #define TOML_SA_LIST_END - #define TOML_SA_LIST_NEW TOML_SA_NEWLINE TOML_SA_NEWLINE - #define TOML_SA_LIST_NXT TOML_SA_LIST_NEW - -#endif - -#define TOML_SA_NATIVE_VALUE_TYPE_LIST \ - TOML_SA_LIST_BEG "std::string" \ - TOML_SA_LIST_SEP "int64_t" \ - TOML_SA_LIST_SEP "double" \ - TOML_SA_LIST_SEP "bool" \ - TOML_SA_LIST_SEP "toml::date" \ - TOML_SA_LIST_SEP "toml::time" \ - TOML_SA_LIST_SEP "toml::date_time" \ - TOML_SA_LIST_END - -#define TOML_SA_NODE_TYPE_LIST \ - TOML_SA_LIST_BEG "toml::table" \ - TOML_SA_LIST_SEP "toml::array" \ - TOML_SA_LIST_SEP "toml::value" \ - TOML_SA_LIST_SEP "toml::value" \ - TOML_SA_LIST_SEP "toml::value" \ - TOML_SA_LIST_SEP "toml::value" \ - TOML_SA_LIST_SEP "toml::value" \ - TOML_SA_LIST_SEP "toml::value" \ - TOML_SA_LIST_SEP "toml::value" \ - TOML_SA_LIST_END - -#define TOML_SA_UNWRAPPED_NODE_TYPE_LIST \ - TOML_SA_LIST_NEW "A native TOML value type" \ - TOML_SA_NATIVE_VALUE_TYPE_LIST \ - \ - TOML_SA_LIST_NXT "A TOML node type" \ - TOML_SA_NODE_TYPE_LIST - -// clang-format on - -TOML_PUSH_WARNINGS; -TOML_DISABLE_SPAM_WARNINGS; -TOML_DISABLE_SWITCH_WARNINGS; -TOML_DISABLE_SUGGEST_ATTR_WARNINGS; - -// misc warning false-positives -#if TOML_MSVC -#pragma warning(disable : 5031) // #pragma warning(pop): likely mismatch -#if TOML_SHARED_LIB -#pragma warning(disable : 4251) // dll exports for std lib types -#endif -#elif TOML_CLANG -TOML_PRAGMA_CLANG(diagnostic ignored "-Wheader-hygiene") -#if TOML_CLANG >= 12 -TOML_PRAGMA_CLANG(diagnostic ignored "-Wc++20-extensions") -#endif -#if TOML_CLANG == 13 -TOML_PRAGMA_CLANG(diagnostic ignored "-Wreserved-identifier") -#endif -#endif - -//******** impl/std_new.hpp ****************************************************************************************** - -TOML_DISABLE_WARNINGS; -#include -TOML_ENABLE_WARNINGS; - -#if (!defined(__apple_build_version__) && TOML_CLANG >= 8) || TOML_GCC >= 7 || TOML_ICC >= 1910 || TOML_MSVC >= 1914 -#define TOML_LAUNDER(x) __builtin_launder(x) -#elif defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 -#define TOML_LAUNDER(x) std::launder(x) -#else -#define TOML_LAUNDER(x) x -#endif - -//******** impl/std_string.hpp *************************************************************************************** - -TOML_DISABLE_WARNINGS; -#include -#include -TOML_ENABLE_WARNINGS; - -#if TOML_DOXYGEN \ - || (defined(__cpp_char8_t) && __cpp_char8_t >= 201811 && defined(__cpp_lib_char8_t) \ - && __cpp_lib_char8_t >= 201907) -#define TOML_HAS_CHAR8 1 -#else -#define TOML_HAS_CHAR8 0 -#endif - -namespace toml // non-abi namespace; this is not an error -{ - using namespace std::string_literals; - using namespace std::string_view_literals; -} - -#if TOML_ENABLE_WINDOWS_COMPAT - -TOML_IMPL_NAMESPACE_START -{ - TOML_NODISCARD - TOML_EXPORTED_FREE_FUNCTION - std::string narrow(std::wstring_view); - - TOML_NODISCARD - TOML_EXPORTED_FREE_FUNCTION - std::wstring widen(std::string_view); - -#if TOML_HAS_CHAR8 - - TOML_NODISCARD - TOML_EXPORTED_FREE_FUNCTION - std::wstring widen(std::u8string_view); - -#endif -} -TOML_IMPL_NAMESPACE_END; - -#endif // TOML_ENABLE_WINDOWS_COMPAT - -//******** impl/std_optional.hpp ************************************************************************************* - -TOML_DISABLE_WARNINGS; -#if !TOML_HAS_CUSTOM_OPTIONAL_TYPE -#include -#endif -TOML_ENABLE_WARNINGS; - -TOML_NAMESPACE_START -{ -#if TOML_HAS_CUSTOM_OPTIONAL_TYPE - - template - using optional = TOML_OPTIONAL_TYPE; - -#else - - template - using optional = std::optional; - -#endif -} -TOML_NAMESPACE_END; - -//******** impl/forward_declarations.hpp ***************************************************************************** - -TOML_DISABLE_WARNINGS; -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -TOML_ENABLE_WARNINGS; -TOML_PUSH_WARNINGS; -#ifdef _MSC_VER -#ifndef __clang__ -#pragma inline_recursion(on) -#endif -#pragma push_macro("min") -#pragma push_macro("max") -#undef min -#undef max -#endif - -#ifndef TOML_DISABLE_ENVIRONMENT_CHECKS -#define TOML_ENV_MESSAGE \ - "If you're seeing this error it's because you're building toml++ for an environment that doesn't conform to " \ - "one of the 'ground truths' assumed by the library. Essentially this just means that I don't have the " \ - "resources to test on more platforms, but I wish I did! You can try disabling the checks by defining " \ - "TOML_DISABLE_ENVIRONMENT_CHECKS, but your mileage may vary. Please consider filing an issue at " \ - "https://github.com/marzer/tomlplusplus/issues to help me improve support for your target environment. " \ - "Thanks!" - -static_assert(CHAR_BIT == 8, TOML_ENV_MESSAGE); -#ifdef FLT_RADIX -static_assert(FLT_RADIX == 2, TOML_ENV_MESSAGE); -#endif -static_assert('A' == 65, TOML_ENV_MESSAGE); -static_assert(sizeof(double) == 8, TOML_ENV_MESSAGE); -static_assert(std::numeric_limits::is_iec559, TOML_ENV_MESSAGE); -static_assert(std::numeric_limits::digits == 53, TOML_ENV_MESSAGE); -static_assert(std::numeric_limits::digits10 == 15, TOML_ENV_MESSAGE); -static_assert(std::numeric_limits::radix == 2, TOML_ENV_MESSAGE); - -#undef TOML_ENV_MESSAGE -#endif // !TOML_DISABLE_ENVIRONMENT_CHECKS - -// undocumented forward declarations are hidden from doxygen because they fuck it up =/ - -namespace toml // non-abi namespace; this is not an error -{ - using ::std::size_t; - using ::std::intptr_t; - using ::std::uintptr_t; - using ::std::ptrdiff_t; - using ::std::nullptr_t; - using ::std::int8_t; - using ::std::int16_t; - using ::std::int32_t; - using ::std::int64_t; - using ::std::uint8_t; - using ::std::uint16_t; - using ::std::uint32_t; - using ::std::uint64_t; - using ::std::uint_least32_t; - using ::std::uint_least64_t; -} - -TOML_NAMESPACE_START -{ - struct date; - struct time; - struct time_offset; - - TOML_ABI_NAMESPACE_BOOL(TOML_HAS_CUSTOM_OPTIONAL_TYPE, custopt, stdopt); - struct date_time; - TOML_ABI_NAMESPACE_END; - - struct source_position; - struct source_region; - - class node; - template - class node_view; - - class key; - class array; - class table; - template - class value; - - class path; - - class toml_formatter; - class json_formatter; - class yaml_formatter; - - TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex); -#if TOML_EXCEPTIONS - using parse_result = table; -#else - class parse_result; -#endif - TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS -} -TOML_NAMESPACE_END; - -TOML_IMPL_NAMESPACE_START -{ - using node_ptr = std::unique_ptr; - - TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, impl_ex, impl_noex); - class parser; - TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS - - // clang-format off - - inline constexpr std::string_view control_char_escapes[] = - { - "\\u0000"sv, - "\\u0001"sv, - "\\u0002"sv, - "\\u0003"sv, - "\\u0004"sv, - "\\u0005"sv, - "\\u0006"sv, - "\\u0007"sv, - "\\b"sv, - "\\t"sv, - "\\n"sv, - "\\u000B"sv, - "\\f"sv, - "\\r"sv, - "\\u000E"sv, - "\\u000F"sv, - "\\u0010"sv, - "\\u0011"sv, - "\\u0012"sv, - "\\u0013"sv, - "\\u0014"sv, - "\\u0015"sv, - "\\u0016"sv, - "\\u0017"sv, - "\\u0018"sv, - "\\u0019"sv, - "\\u001A"sv, - "\\u001B"sv, - "\\u001C"sv, - "\\u001D"sv, - "\\u001E"sv, - "\\u001F"sv, - }; - - inline constexpr std::string_view node_type_friendly_names[] = - { - "none"sv, - "table"sv, - "array"sv, - "string"sv, - "integer"sv, - "floating-point"sv, - "boolean"sv, - "date"sv, - "time"sv, - "date-time"sv - }; - - // clang-format on -} -TOML_IMPL_NAMESPACE_END; - -#if TOML_ABI_NAMESPACES -#if TOML_EXCEPTIONS -#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::impl_ex::parser -#else -#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::impl_noex::parser -#endif -#else -#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::parser -#endif - -namespace toml -{ -} - -TOML_NAMESPACE_START // abi namespace -{ - inline namespace literals - { - } - - enum class TOML_CLOSED_ENUM node_type : uint8_t - { - none, - table, - array, - string, - integer, - floating_point, - boolean, - date, - time, - date_time - }; - - template - inline std::basic_ostream& operator<<(std::basic_ostream& lhs, node_type rhs) - { - const auto str = impl::node_type_friendly_names[static_cast>(rhs)]; - using str_char_t = decltype(str)::value_type; - if constexpr (std::is_same_v) - return lhs << str; - else - { - if constexpr (sizeof(Char) == sizeof(str_char_t)) - return lhs << std::basic_string_view{ reinterpret_cast(str.data()), str.length() }; - else - return lhs << str.data(); - } - } - - enum class TOML_OPEN_FLAGS_ENUM value_flags : uint16_t // being an "OPEN" flags enum is not an error - { - none, - format_as_binary = 1, - format_as_octal = 2, - format_as_hexadecimal = 3, - }; - TOML_MAKE_FLAGS(value_flags); - - inline constexpr value_flags preserve_source_value_flags = - POXY_IMPLEMENTATION_DETAIL(value_flags{ static_cast>(-1) }); - - enum class TOML_CLOSED_FLAGS_ENUM format_flags : uint64_t - { - none, - quote_dates_and_times = (1ull << 0), - quote_infinities_and_nans = (1ull << 1), - allow_literal_strings = (1ull << 2), - allow_multi_line_strings = (1ull << 3), - allow_real_tabs_in_strings = (1ull << 4), - allow_unicode_strings = (1ull << 5), - allow_binary_integers = (1ull << 6), - allow_octal_integers = (1ull << 7), - allow_hexadecimal_integers = (1ull << 8), - indent_sub_tables = (1ull << 9), - indent_array_elements = (1ull << 10), - indentation = indent_sub_tables | indent_array_elements, - relaxed_float_precision = (1ull << 11), - terse_key_value_pairs = (1ull << 12), - force_multiline_arrays = (1ull << 13), - }; - TOML_MAKE_FLAGS(format_flags); - - template - struct TOML_TRIVIAL_ABI inserter - { - static_assert(std::is_reference_v); - - T value; - }; - template - inserter(T&&) -> inserter; - template - inserter(T&) -> inserter; - - using default_formatter = toml_formatter; -} -TOML_NAMESPACE_END; - -TOML_IMPL_NAMESPACE_START -{ - template - using remove_cvref = std::remove_cv_t>; - - template - using common_signed_type = std::common_type_t...>; - - template - inline constexpr bool is_one_of = (false || ... || std::is_same_v); - - template - inline constexpr bool all_integral = (std::is_integral_v && ...); - - template - inline constexpr bool is_cvref = std::is_reference_v || std::is_const_v || std::is_volatile_v; - - template - inline constexpr bool is_wide_string = - is_one_of, const wchar_t*, wchar_t*, std::wstring_view, std::wstring>; - - template - inline constexpr bool value_retrieval_is_nothrow = !std::is_same_v, std::string> -#if TOML_HAS_CHAR8 - && !std::is_same_v, std::u8string> -#endif - - && !is_wide_string; - - template - struct copy_ref_; - template - using copy_ref = typename copy_ref_::type; - - template - struct copy_ref_ - { - using type = Dest; - }; - - template - struct copy_ref_ - { - using type = std::add_lvalue_reference_t; - }; - - template - struct copy_ref_ - { - using type = std::add_rvalue_reference_t; - }; - - template - struct copy_cv_; - template - using copy_cv = typename copy_cv_::type; - - template - struct copy_cv_ - { - using type = Dest; - }; - - template - struct copy_cv_ - { - using type = std::add_const_t; - }; - - template - struct copy_cv_ - { - using type = std::add_volatile_t; - }; - - template - struct copy_cv_ - { - using type = std::add_cv_t; - }; - - template - using copy_cvref = - copy_ref, std::remove_reference_t>, Dest>, Src>; - - template - inline constexpr bool always_false = false; - - template - inline constexpr bool first_is_same = false; - template - inline constexpr bool first_is_same = true; - - template > - struct underlying_type_ - { - using type = std::underlying_type_t; - }; - template - struct underlying_type_ - { - using type = T; - }; - template - using underlying_type = typename underlying_type_::type; - - // general value traits - // (as they relate to their equivalent native TOML type) - struct default_value_traits - { - using native_type = void; - static constexpr bool is_native = false; - static constexpr bool is_losslessly_convertible_to_native = false; - static constexpr bool can_represent_native = false; - static constexpr bool can_partially_represent_native = false; - static constexpr auto type = node_type::none; - }; - - template - struct value_traits; - - template > - struct value_traits_base_selector - { - static_assert(!is_cvref); - - using type = default_value_traits; - }; - template - struct value_traits_base_selector - { - static_assert(!is_cvref); - - using type = value_traits>; - }; - - template - struct value_traits : value_traits_base_selector::type - {}; - template - struct value_traits : value_traits - {}; - template - struct value_traits : value_traits - {}; - template - struct value_traits : value_traits - {}; - template - struct value_traits : value_traits - {}; - template - struct value_traits : value_traits - {}; - - // integer value_traits specializations - standard types - template - struct integer_limits - { - static constexpr T min = T{ (std::numeric_limits>::min)() }; - static constexpr T max = T{ (std::numeric_limits>::max)() }; - }; - template - struct integer_traits_base : integer_limits - { - using native_type = int64_t; - static constexpr bool is_native = std::is_same_v, native_type>; - static constexpr bool is_signed = static_cast>(-1) < underlying_type{}; - static constexpr auto type = node_type::integer; - static constexpr bool can_partially_represent_native = true; - }; - template - struct unsigned_integer_traits : integer_traits_base - { - static constexpr bool is_losslessly_convertible_to_native = - integer_limits>::max <= 9223372036854775807ULL; - static constexpr bool can_represent_native = false; - }; - template - struct signed_integer_traits : integer_traits_base - { - using native_type = int64_t; - static constexpr bool is_losslessly_convertible_to_native = - integer_limits>::min >= (-9223372036854775807LL - 1LL) - && integer_limits>::max <= 9223372036854775807LL; - static constexpr bool can_represent_native = - integer_limits>::min <= (-9223372036854775807LL - 1LL) - && integer_limits>::max >= 9223372036854775807LL; - }; - template ::is_signed> - struct integer_traits : signed_integer_traits - {}; - template - struct integer_traits : unsigned_integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - static_assert(value_traits::is_native); - static_assert(value_traits::is_signed); - static_assert(value_traits::is_losslessly_convertible_to_native); - static_assert(value_traits::can_represent_native); - static_assert(value_traits::can_partially_represent_native); - - // integer value_traits specializations - non-standard types -#ifdef TOML_INT128 - template <> - struct integer_limits - { - static constexpr TOML_INT128 max = - static_cast((TOML_UINT128{ 1u } << ((__SIZEOF_INT128__ * CHAR_BIT) - 1)) - 1); - static constexpr TOML_INT128 min = -max - TOML_INT128{ 1 }; - }; - template <> - struct integer_limits - { - static constexpr TOML_UINT128 min = TOML_UINT128{}; - static constexpr TOML_UINT128 max = (2u * static_cast(integer_limits::max)) + 1u; - }; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; -#endif -#ifdef TOML_SMALL_INT_TYPE - template <> - struct value_traits : signed_integer_traits - {}; -#endif - - // floating-point traits base - template - struct float_traits_base - { - static constexpr auto type = node_type::floating_point; - using native_type = double; - static constexpr bool is_native = std::is_same_v; - static constexpr bool is_signed = true; - - static constexpr int bits = static_cast(sizeof(T) * CHAR_BIT); - static constexpr int digits = MantissaDigits; - static constexpr int digits10 = DecimalDigits; - - static constexpr bool is_losslessly_convertible_to_native = bits <= 64 // - && digits <= 53 // DBL_MANT_DIG - && digits10 <= 15; // DBL_DIG - - static constexpr bool can_represent_native = digits >= 53 // DBL_MANT_DIG - && digits10 >= 15; // DBL_DIG - - static constexpr bool can_partially_represent_native = digits > 0 && digits10 > 0; - }; - template - struct float_traits : float_traits_base::digits, std::numeric_limits::digits10> - {}; -#if TOML_ENABLE_FLOAT16 - template <> - struct float_traits<_Float16> : float_traits_base<_Float16, __FLT16_MANT_DIG__, __FLT16_DIG__> - {}; -#endif -#ifdef TOML_FLOAT128 - template <> - struct float_traits : float_traits_base - {}; -#endif - - // floating-point traits - template <> - struct value_traits : float_traits - {}; - template <> - struct value_traits : float_traits - {}; - template <> - struct value_traits : float_traits - {}; -#if TOML_ENABLE_FLOAT16 - template <> - struct value_traits<_Float16> : float_traits<_Float16> - {}; -#endif -#ifdef TOML_FLOAT128 - template <> - struct value_traits : float_traits - {}; -#endif -#ifdef TOML_SMALL_FLOAT_TYPE - template <> - struct value_traits : float_traits - {}; -#endif - static_assert(value_traits::is_native); - static_assert(value_traits::is_losslessly_convertible_to_native); - static_assert(value_traits::can_represent_native); - static_assert(value_traits::can_partially_represent_native); - - // string value_traits specializations - char-based strings - template - struct string_traits - { - using native_type = std::string; - static constexpr bool is_native = std::is_same_v; - static constexpr bool is_losslessly_convertible_to_native = true; - static constexpr bool can_represent_native = - !std::is_array_v && (!std::is_pointer_v || std::is_const_v>); - static constexpr bool can_partially_represent_native = can_represent_native; - static constexpr auto type = node_type::string; - }; - template <> - struct value_traits : string_traits - {}; - template <> - struct value_traits : string_traits - {}; - template <> - struct value_traits : string_traits - {}; - template - struct value_traits : string_traits - {}; - template <> - struct value_traits : string_traits - {}; - template - struct value_traits : string_traits - {}; - - // string value_traits specializations - char8_t-based strings -#if TOML_HAS_CHAR8 - template <> - struct value_traits : string_traits - {}; - template <> - struct value_traits : string_traits - {}; - template <> - struct value_traits : string_traits - {}; - template - struct value_traits : string_traits - {}; - template <> - struct value_traits : string_traits - {}; - template - struct value_traits : string_traits - {}; -#endif - - // string value_traits specializations - wchar_t-based strings on Windows -#if TOML_ENABLE_WINDOWS_COMPAT - template - struct wstring_traits - { - using native_type = std::string; - static constexpr bool is_native = false; - static constexpr bool is_losslessly_convertible_to_native = true; // narrow - static constexpr bool can_represent_native = std::is_same_v; // widen - static constexpr bool can_partially_represent_native = can_represent_native; - static constexpr auto type = node_type::string; - }; - template <> - struct value_traits : wstring_traits - {}; - template <> - struct value_traits : wstring_traits - {}; - template <> - struct value_traits : wstring_traits - {}; - template - struct value_traits : wstring_traits - {}; - template <> - struct value_traits : wstring_traits - {}; - template - struct value_traits : wstring_traits - {}; -#endif - - // other 'native' value_traits specializations - template - struct native_value_traits - { - using native_type = T; - static constexpr bool is_native = true; - static constexpr bool is_losslessly_convertible_to_native = true; - static constexpr bool can_represent_native = true; - static constexpr bool can_partially_represent_native = true; - static constexpr auto type = NodeType; - }; - template <> - struct value_traits : native_value_traits - {}; - template <> - struct value_traits : native_value_traits - {}; - template <> - struct value_traits